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.y_position()) / 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.y_position()) / 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.y_position() + 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 const fraction = 1.0 - ((cy - cp->line.y_position()) / 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 const fraction = 1.0 - ((cy - line->y_position()) / 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);
2611 double const fraction = 1.0 - ((cy - line->y_position()) / line->height());
2615 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2621 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2623 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2627 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2629 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2630 line_drag_motion_callback (item, event);
2635 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2637 if (selection->regions.empty() || clicked_regionview == 0) {
2641 drag_info.copy = false;
2642 drag_info.item = item;
2643 drag_info.data = clicked_regionview;
2644 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2645 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2650 TimeAxisView* tvp = clicked_axisview;
2651 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2653 if (tv && tv->is_track()) {
2654 speed = tv->get_diskstream()->speed();
2657 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2658 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2659 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2660 // we want a move threshold
2661 drag_info.want_move_threshold = true;
2663 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2665 begin_reversible_command (_("move region(s)"));
2669 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2671 if (selection->regions.empty() || clicked_regionview == 0) {
2675 drag_info.copy = true;
2676 drag_info.item = item;
2677 drag_info.data = clicked_regionview;
2681 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2682 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2685 if (rtv && rtv->is_track()) {
2686 speed = rtv->get_diskstream()->speed();
2689 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2690 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2691 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2692 // we want a move threshold
2693 drag_info.want_move_threshold = true;
2694 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2695 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2696 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2700 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2702 if (selection->regions.empty() || clicked_regionview == 0) {
2706 drag_info.copy = false;
2707 drag_info.item = item;
2708 drag_info.data = clicked_regionview;
2709 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2710 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2715 TimeAxisView* tvp = clicked_axisview;
2716 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2718 if (tv && tv->is_track()) {
2719 speed = tv->get_diskstream()->speed();
2722 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2723 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2724 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2725 // we want a move threshold
2726 drag_info.want_move_threshold = true;
2727 drag_info.brushing = true;
2729 begin_reversible_command (_("Drag region brush"));
2733 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2737 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2738 nframes_t pending_region_position = 0;
2739 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2740 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2741 bool clamp_y_axis = false;
2742 vector<int32_t> height_list(512) ;
2743 vector<int32_t>::iterator j;
2745 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2747 drag_info.want_move_threshold = false; // don't copy again
2749 /* duplicate the region(s) */
2751 vector<RegionView*> new_regionviews;
2753 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2759 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2760 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2763 nrv = new AudioRegionView (*arv);
2765 nrv = new MidiRegionView (*mrv);
2770 nrv->get_canvas_group()->show ();
2772 new_regionviews.push_back (nrv);
2775 if (new_regionviews.empty()) {
2779 /* reset selection to new regionviews */
2781 selection->set (new_regionviews);
2783 /* reset drag_info data to reflect the fact that we are dragging the copies */
2785 drag_info.data = new_regionviews.front();
2787 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2790 /* Which trackview is this ? */
2792 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2793 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2795 /* The region motion is only processed if the pointer is over
2799 if (!tv || !tv->is_track()) {
2800 /* To make sure we hide the verbose canvas cursor when the mouse is
2801 not held over a track.
2803 hide_verbose_canvas_cursor ();
2807 original_pointer_order = drag_info.last_trackview->order;
2809 /************************************************************
2811 ************************************************************/
2813 if (drag_info.brushing) {
2814 clamp_y_axis = true;
2819 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2821 int32_t children = 0, numtracks = 0;
2822 // XXX hard coding track limit, oh my, so very very bad
2823 bitset <1024> tracks (0x00);
2824 /* get a bitmask representing the visible tracks */
2826 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2827 TimeAxisView *tracklist_timeview;
2828 tracklist_timeview = (*i);
2829 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2830 list<TimeAxisView*> children_list;
2832 /* zeroes are audio tracks. ones are other types. */
2834 if (!rtv2->hidden()) {
2836 if (visible_y_high < rtv2->order) {
2837 visible_y_high = rtv2->order;
2839 if (visible_y_low > rtv2->order) {
2840 visible_y_low = rtv2->order;
2843 if (!rtv2->is_track()) {
2844 tracks = tracks |= (0x01 << rtv2->order);
2847 height_list[rtv2->order] = (*i)->height;
2849 if ((children_list = rtv2->get_child_list()).size() > 0) {
2850 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2851 tracks = tracks |= (0x01 << (rtv2->order + children));
2852 height_list[rtv2->order + children] = (*j)->height;
2860 /* find the actual span according to the canvas */
2862 canvas_pointer_y_span = pointer_y_span;
2863 if (drag_info.last_trackview->order >= tv->order) {
2865 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2866 if (height_list[y] == 0 ) {
2867 canvas_pointer_y_span--;
2872 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2873 if ( height_list[y] == 0 ) {
2874 canvas_pointer_y_span++;
2879 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2880 RegionView* rv2 = (*i);
2881 double ix1, ix2, iy1, iy2;
2884 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2885 rv2->get_canvas_group()->i2w (ix1, iy1);
2886 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2887 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2889 if (rtv2->order != original_pointer_order) {
2890 /* this isn't the pointer track */
2892 if (canvas_pointer_y_span > 0) {
2894 /* moving up the canvas */
2895 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2897 int32_t visible_tracks = 0;
2898 while (visible_tracks < canvas_pointer_y_span ) {
2901 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2902 /* we're passing through a hidden track */
2907 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2908 clamp_y_axis = true;
2912 clamp_y_axis = true;
2915 } else if (canvas_pointer_y_span < 0) {
2917 /*moving down the canvas*/
2919 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2922 int32_t visible_tracks = 0;
2924 while (visible_tracks > canvas_pointer_y_span ) {
2927 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2931 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2932 clamp_y_axis = true;
2937 clamp_y_axis = true;
2943 /* this is the pointer's track */
2944 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2945 clamp_y_axis = true;
2946 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2947 clamp_y_axis = true;
2955 } else if (drag_info.last_trackview == tv) {
2956 clamp_y_axis = true;
2960 if (!clamp_y_axis) {
2961 drag_info.last_trackview = tv;
2964 /************************************************************
2966 ************************************************************/
2968 /* compute the amount of pointer motion in frames, and where
2969 the region would be if we moved it by that much.
2972 if (drag_info.move_threshold_passed) {
2974 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2976 nframes_t sync_frame;
2977 nframes_t sync_offset;
2980 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2982 sync_offset = rv->region()->sync_offset (sync_dir);
2983 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
2985 /* we snap if the snap modifier is not enabled.
2988 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2989 snap_to (sync_frame);
2992 if (sync_frame - sync_offset <= sync_frame) {
2993 pending_region_position = sync_frame - (sync_dir*sync_offset);
2995 pending_region_position = 0;
2999 pending_region_position = 0;
3002 if (pending_region_position > max_frames - rv->region()->length()) {
3003 pending_region_position = drag_info.last_frame_position;
3006 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3008 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3010 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3011 to make it appear at the new location.
3014 if (pending_region_position > drag_info.last_frame_position) {
3015 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3017 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3020 drag_info.last_frame_position = pending_region_position;
3027 /* threshold not passed */
3032 /*************************************************************
3034 ************************************************************/
3036 if (x_delta == 0 && (pointer_y_span == 0)) {
3037 /* haven't reached next snap point, and we're not switching
3038 trackviews. nothing to do.
3044 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3046 RegionView* rv2 = (*i);
3048 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3050 double ix1, ix2, iy1, iy2;
3051 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3052 rv2->get_canvas_group()->i2w (ix1, iy1);
3061 /*************************************************************
3063 ************************************************************/
3067 if (drag_info.first_move) {
3068 if (drag_info.move_threshold_passed) {
3079 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3080 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3082 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3084 RegionView* rv = (*i);
3085 double ix1, ix2, iy1, iy2;
3086 int32_t temp_pointer_y_span = pointer_y_span;
3088 /* get item BBox, which will be relative to parent. so we have
3089 to query on a child, then convert to world coordinates using
3093 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3094 rv->get_canvas_group()->i2w (ix1, iy1);
3095 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3096 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3097 RouteTimeAxisView* temp_rtv;
3099 if ((pointer_y_span != 0) && !clamp_y_axis) {
3102 for (j = height_list.begin(); j!= height_list.end(); j++) {
3103 if (x == canvas_rtv->order) {
3104 /* we found the track the region is on */
3105 if (x != original_pointer_order) {
3106 /*this isn't from the same track we're dragging from */
3107 temp_pointer_y_span = canvas_pointer_y_span;
3109 while (temp_pointer_y_span > 0) {
3110 /* we're moving up canvas-wise,
3111 so we need to find the next track height
3113 if (j != height_list.begin()) {
3116 if (x != original_pointer_order) {
3117 /* we're not from the dragged track, so ignore hidden tracks. */
3119 temp_pointer_y_span++;
3123 temp_pointer_y_span--;
3125 while (temp_pointer_y_span < 0) {
3127 if (x != original_pointer_order) {
3129 temp_pointer_y_span--;
3133 if (j != height_list.end()) {
3136 temp_pointer_y_span++;
3138 /* find out where we'll be when we move and set height accordingly */
3140 tvp2 = trackview_by_y_position (iy1 + y_delta);
3141 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3142 rv->set_y_position_and_height (0, temp_rtv->height);
3144 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3145 personally, i think this can confuse things, but never mind.
3148 //const GdkColor& col (temp_rtv->view->get_region_color());
3149 //rv->set_color (const_cast<GdkColor&>(col));
3156 /* prevent the regionview from being moved to before
3157 the zero position on the canvas.
3162 if (-x_delta > ix1) {
3165 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3166 x_delta = max_frames - rv->region()->last_frame();
3169 if (drag_info.first_move) {
3171 /* hide any dependent views */
3173 rv->get_time_axis_view().hide_dependent_views (*rv);
3175 /* this is subtle. raising the regionview itself won't help,
3176 because raise_to_top() just puts the item on the top of
3177 its parent's stack. so, we need to put the trackview canvas_display group
3178 on the top, since its parent is the whole canvas.
3181 rv->get_canvas_group()->raise_to_top();
3182 rv->get_time_axis_view().canvas_display->raise_to_top();
3183 cursor_group->raise_to_top();
3185 rv->fake_set_opaque (true);
3188 if (drag_info.brushing) {
3189 mouse_brush_insert_region (rv, pending_region_position);
3191 rv->move (x_delta, y_delta);
3194 } /* foreach region */
3198 if (drag_info.first_move && drag_info.move_threshold_passed) {
3199 cursor_group->raise_to_top();
3200 drag_info.first_move = false;
3203 if (x_delta != 0 && !drag_info.brushing) {
3204 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3209 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3212 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3213 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3214 bool nocommit = true;
3216 RouteTimeAxisView* rtv;
3217 bool regionview_y_movement;
3218 bool regionview_x_movement;
3219 vector<RegionView*> copies;
3221 /* first_move is set to false if the regionview has been moved in the
3225 if (drag_info.first_move && !(drag_info.copy && drag_info.x_constrained)) {
3232 /* The regionview has been moved at some stage during the grab so we need
3233 to account for any mouse movement between this event and the last one.
3236 region_drag_motion_callback (item, event);
3238 if (drag_info.brushing) {
3239 /* all changes were made during motion event handlers */
3241 if (drag_info.copy) {
3242 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3243 copies.push_back (*i);
3250 /* adjust for track speed */
3253 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3254 if (rtv && rtv->get_diskstream()) {
3255 speed = rtv->get_diskstream()->speed();
3258 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3259 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3261 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3262 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3266 if (drag_info.copy) {
3267 if (drag_info.x_constrained) {
3268 op_string = _("fixed time region copy");
3270 op_string = _("region copy");
3273 if (drag_info.x_constrained) {
3274 op_string = _("fixed time region drag");
3276 op_string = _("region drag");
3280 begin_reversible_command (op_string);
3282 if (regionview_y_movement) {
3284 /* moved to a different audio track. */
3286 vector<RegionView*> new_selection;
3288 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3290 RegionView* rv = (*i);
3292 double ix1, ix2, iy1, iy2;
3294 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3295 rv->get_canvas_group()->i2w (ix1, iy1);
3296 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3297 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3299 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3300 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3302 where = (nframes_t) (unit_to_frame (ix1) * speed);
3303 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3305 /* undo the previous hide_dependent_views so that xfades don't
3306 disappear on copying regions
3309 rv->get_time_axis_view().reveal_dependent_views (*rv);
3311 if (!drag_info.copy) {
3313 /* the region that used to be in the old playlist is not
3314 moved to the new one - we make a copy of it. as a result,
3315 any existing editor for the region should no longer be
3319 rv->hide_region_editor();
3320 rv->fake_set_opaque (false);
3322 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3323 from_playlist->remove_region ((rv->region()));
3324 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3328 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3330 copies.push_back (rv);
3333 latest_regionview = 0;
3335 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3336 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3337 to_playlist->add_region (new_region, where);
3338 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3341 if (latest_regionview) {
3342 new_selection.push_back (latest_regionview);
3345 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3346 was selected in all of them, then removing it from the playlist will have removed all
3347 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3348 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3349 corresponding regionview, and the selection is now empty).
3351 this could have invalidated any and all iterators into the region selection.
3353 the heuristic we use here is: if the region selection is empty, break out of the loop
3354 here. if the region selection is not empty, then restart the loop because we know that
3355 we must have removed at least the region(view) we've just been working on as well as any
3356 that we processed on previous iterations.
3358 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3359 we can just iterate.
3362 if (drag_info.copy) {
3365 if (selection->regions.empty()) {
3368 i = selection->regions.by_layer().begin();
3373 selection->set (new_selection);
3377 /* motion within a single track */
3379 list<RegionView*> regions = selection->regions.by_layer();
3381 if (drag_info.copy) {
3382 selection->clear_regions();
3385 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3389 if (!rv->region()->can_move()) {
3393 if (regionview_x_movement) {
3394 double ownspeed = 1.0;
3395 rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3397 if (rtv && rtv->get_diskstream()) {
3398 ownspeed = rtv->get_diskstream()->speed();
3401 /* base the new region position on the current position of the regionview.*/
3403 double ix1, ix2, iy1, iy2;
3405 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3406 rv->get_canvas_group()->i2w (ix1, iy1);
3407 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3411 where = rv->region()->position();
3414 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3416 assert (to_playlist);
3420 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3422 if (drag_info.copy) {
3424 boost::shared_ptr<Region> newregion;
3425 boost::shared_ptr<Region> ar;
3427 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3428 newregion = RegionFactory::create (ar);
3430 /* XXX MIDI HERE drobilla */
3436 latest_regionview = 0;
3437 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3438 to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
3441 if (latest_regionview) {
3442 rtv->reveal_dependent_views (*latest_regionview);
3443 selection->add (latest_regionview);
3448 /* just change the model */
3450 rv->region()->set_position (where, (void*) this);
3456 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3458 if (drag_info.copy) {
3459 copies.push_back (rv);
3467 commit_reversible_command ();
3470 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3476 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3478 /* Either add to or set the set the region selection, unless
3479 this is an alignment click (control used)
3482 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3483 TimeAxisView* tv = &rv.get_time_axis_view();
3484 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3486 if (rtv && rtv->is_track()) {
3487 speed = rtv->get_diskstream()->speed();
3490 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3492 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3494 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3496 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3500 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3506 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3512 nframes_t frame_rate;
3519 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3520 case AudioClock::BBT:
3521 session->bbt_time (frame, bbt);
3522 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3525 case AudioClock::SMPTE:
3526 session->smpte_time (frame, smpte);
3527 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3530 case AudioClock::MinSec:
3531 /* XXX this is copied from show_verbose_duration_cursor() */
3532 frame_rate = session->frame_rate();
3533 hours = frame / (frame_rate * 3600);
3534 frame = frame % (frame_rate * 3600);
3535 mins = frame / (frame_rate * 60);
3536 frame = frame % (frame_rate * 60);
3537 secs = (float) frame / (float) frame_rate;
3538 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3542 snprintf (buf, sizeof(buf), "%u", frame);
3546 if (xpos >= 0 && ypos >=0) {
3547 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3550 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3552 show_verbose_canvas_cursor ();
3556 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3563 nframes_t distance, frame_rate;
3565 Meter meter_at_start(session->tempo_map().meter_at(start));
3571 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3572 case AudioClock::BBT:
3573 session->bbt_time (start, sbbt);
3574 session->bbt_time (end, ebbt);
3577 /* XXX this computation won't work well if the
3578 user makes a selection that spans any meter changes.
3581 ebbt.bars -= sbbt.bars;
3582 if (ebbt.beats >= sbbt.beats) {
3583 ebbt.beats -= sbbt.beats;
3586 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3588 if (ebbt.ticks >= sbbt.ticks) {
3589 ebbt.ticks -= sbbt.ticks;
3592 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3595 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3598 case AudioClock::SMPTE:
3599 session->smpte_duration (end - start, smpte);
3600 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3603 case AudioClock::MinSec:
3604 /* XXX this stuff should be elsewhere.. */
3605 distance = end - start;
3606 frame_rate = session->frame_rate();
3607 hours = distance / (frame_rate * 3600);
3608 distance = distance % (frame_rate * 3600);
3609 mins = distance / (frame_rate * 60);
3610 distance = distance % (frame_rate * 60);
3611 secs = (float) distance / (float) frame_rate;
3612 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3616 snprintf (buf, sizeof(buf), "%u", end - start);
3620 if (xpos >= 0 && ypos >=0) {
3621 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3624 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3626 show_verbose_canvas_cursor ();
3630 Editor::collect_new_region_view (RegionView* rv)
3632 latest_regionview = rv;
3636 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3638 if (clicked_regionview == 0) {
3642 /* lets try to create new Region for the selection */
3644 vector<boost::shared_ptr<AudioRegion> > new_regions;
3645 create_region_from_selection (new_regions);
3647 if (new_regions.empty()) {
3651 /* XXX fix me one day to use all new regions */
3653 boost::shared_ptr<Region> region (new_regions.front());
3655 /* add it to the current stream/playlist.
3657 tricky: the streamview for the track will add a new regionview. we will
3658 catch the signal it sends when it creates the regionview to
3659 set the regionview we want to then drag.
3662 latest_regionview = 0;
3663 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3665 /* A selection grab currently creates two undo/redo operations, one for
3666 creating the new region and another for moving it.
3669 begin_reversible_command (_("selection grab"));
3671 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3673 XMLNode *before = &(playlist->get_state());
3674 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3675 XMLNode *after = &(playlist->get_state());
3676 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3678 commit_reversible_command ();
3682 if (latest_regionview == 0) {
3683 /* something went wrong */
3687 /* we need to deselect all other regionviews, and select this one
3688 i'm ignoring undo stuff, because the region creation will take care of it */
3689 selection->set (latest_regionview);
3691 drag_info.item = latest_regionview->get_canvas_group();
3692 drag_info.data = latest_regionview;
3693 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3694 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3698 drag_info.last_trackview = clicked_axisview;
3699 drag_info.last_frame_position = latest_regionview->region()->position();
3700 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3702 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3706 Editor::cancel_selection ()
3708 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3709 (*i)->hide_selection ();
3711 begin_reversible_command (_("cancel selection"));
3712 selection->clear ();
3713 clicked_selection = 0;
3714 commit_reversible_command ();
3718 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3720 nframes_t start = 0;
3727 drag_info.item = item;
3728 drag_info.motion_callback = &Editor::drag_selection;
3729 drag_info.finished_callback = &Editor::end_selection_op;
3734 case CreateSelection:
3735 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3736 drag_info.copy = true;
3738 drag_info.copy = false;
3740 start_grab (event, selector_cursor);
3743 case SelectionStartTrim:
3744 if (clicked_axisview) {
3745 clicked_axisview->order_selection_trims (item, true);
3747 start_grab (event, trimmer_cursor);
3748 start = selection->time[clicked_selection].start;
3749 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3752 case SelectionEndTrim:
3753 if (clicked_axisview) {
3754 clicked_axisview->order_selection_trims (item, false);
3756 start_grab (event, trimmer_cursor);
3757 end = selection->time[clicked_selection].end;
3758 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3762 start = selection->time[clicked_selection].start;
3764 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3768 if (selection_op == SelectionMove) {
3769 show_verbose_time_cursor(start, 10);
3771 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3776 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3778 nframes_t start = 0;
3781 nframes_t pending_position;
3783 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3784 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3787 pending_position = 0;
3790 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3791 snap_to (pending_position);
3794 /* only alter selection if the current frame is
3795 different from the last frame position (adjusted)
3798 if (pending_position == drag_info.last_pointer_frame) return;
3800 switch (selection_op) {
3801 case CreateSelection:
3803 if (drag_info.first_move) {
3804 snap_to (drag_info.grab_frame);
3807 if (pending_position < drag_info.grab_frame) {
3808 start = pending_position;
3809 end = drag_info.grab_frame;
3811 end = pending_position;
3812 start = drag_info.grab_frame;
3815 /* first drag: Either add to the selection
3816 or create a new selection->
3819 if (drag_info.first_move) {
3821 begin_reversible_command (_("range selection"));
3823 if (drag_info.copy) {
3824 /* adding to the selection */
3825 clicked_selection = selection->add (start, end);
3826 drag_info.copy = false;
3828 /* new selection-> */
3829 clicked_selection = selection->set (clicked_axisview, start, end);
3834 case SelectionStartTrim:
3836 if (drag_info.first_move) {
3837 begin_reversible_command (_("trim selection start"));
3840 start = selection->time[clicked_selection].start;
3841 end = selection->time[clicked_selection].end;
3843 if (pending_position > end) {
3846 start = pending_position;
3850 case SelectionEndTrim:
3852 if (drag_info.first_move) {
3853 begin_reversible_command (_("trim selection end"));
3856 start = selection->time[clicked_selection].start;
3857 end = selection->time[clicked_selection].end;
3859 if (pending_position < start) {
3862 end = pending_position;
3869 if (drag_info.first_move) {
3870 begin_reversible_command (_("move selection"));
3873 start = selection->time[clicked_selection].start;
3874 end = selection->time[clicked_selection].end;
3876 length = end - start;
3878 start = pending_position;
3881 end = start + length;
3886 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3887 start_canvas_autoscroll (1);
3891 selection->replace (clicked_selection, start, end);
3894 drag_info.last_pointer_frame = pending_position;
3895 drag_info.first_move = false;
3897 if (selection_op == SelectionMove) {
3898 show_verbose_time_cursor(start, 10);
3900 show_verbose_time_cursor(pending_position, 10);
3905 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3907 if (!drag_info.first_move) {
3908 drag_selection (item, event);
3909 /* XXX this is not object-oriented programming at all. ick */
3910 if (selection->time.consolidate()) {
3911 selection->TimeChanged ();
3913 commit_reversible_command ();
3915 /* just a click, no pointer movement.*/
3917 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3919 selection->clear_time();
3924 /* XXX what happens if its a music selection? */
3925 session->set_audio_range (selection->time);
3926 stop_canvas_autoscroll ();
3930 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3933 TimeAxisView* tvp = clicked_axisview;
3934 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3936 if (tv && tv->is_track()) {
3937 speed = tv->get_diskstream()->speed();
3940 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3941 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3942 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3944 //drag_info.item = clicked_regionview->get_name_highlight();
3945 drag_info.item = item;
3946 drag_info.motion_callback = &Editor::trim_motion_callback;
3947 drag_info.finished_callback = &Editor::trim_finished_callback;
3949 start_grab (event, trimmer_cursor);
3951 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3952 trim_op = ContentsTrim;
3954 /* These will get overridden for a point trim.*/
3955 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3956 /* closer to start */
3957 trim_op = StartTrim;
3958 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3966 show_verbose_time_cursor(region_start, 10);
3969 show_verbose_time_cursor(region_end, 10);
3972 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3978 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3980 RegionView* rv = clicked_regionview;
3981 nframes_t frame_delta = 0;
3982 bool left_direction;
3983 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3985 /* snap modifier works differently here..
3986 its' current state has to be passed to the
3987 various trim functions in order to work properly
3991 TimeAxisView* tvp = clicked_axisview;
3992 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3993 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3995 if (tv && tv->is_track()) {
3996 speed = tv->get_diskstream()->speed();
3999 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4000 left_direction = true;
4002 left_direction = false;
4006 snap_to (drag_info.current_pointer_frame);
4009 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4013 if (drag_info.first_move) {
4019 trim_type = "Region start trim";
4022 trim_type = "Region end trim";
4025 trim_type = "Region content trim";
4029 begin_reversible_command (trim_type);
4031 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4032 (*i)->fake_set_opaque(false);
4033 (*i)->region()->freeze ();
4035 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4037 arv->temporarily_hide_envelope ();
4039 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4040 insert_result = motion_frozen_playlists.insert (pl);
4041 if (insert_result.second) {
4042 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4047 if (left_direction) {
4048 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4050 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4055 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4058 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4059 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4065 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4068 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4069 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4076 bool swap_direction = false;
4078 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4079 swap_direction = true;
4082 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4083 i != selection->regions.by_layer().end(); ++i)
4085 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4093 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4096 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4099 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4103 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4104 drag_info.first_move = false;
4108 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4110 boost::shared_ptr<Region> region (rv.region());
4112 if (region->locked()) {
4116 nframes_t new_bound;
4119 TimeAxisView* tvp = clicked_axisview;
4120 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4122 if (tv && tv->is_track()) {
4123 speed = tv->get_diskstream()->speed();
4126 if (left_direction) {
4127 if (swap_direction) {
4128 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4130 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4133 if (swap_direction) {
4134 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4136 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4141 snap_to (new_bound);
4143 region->trim_start ((nframes_t) (new_bound * speed), this);
4144 rv.region_changed (StartChanged);
4148 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4150 boost::shared_ptr<Region> region (rv.region());
4152 if (region->locked()) {
4156 nframes_t new_bound;
4159 TimeAxisView* tvp = clicked_axisview;
4160 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4162 if (tv && tv->is_track()) {
4163 speed = tv->get_diskstream()->speed();
4166 if (left_direction) {
4167 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4169 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4173 snap_to (new_bound, (left_direction ? 0 : 1));
4176 region->trim_front ((nframes_t) (new_bound * speed), this);
4178 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4182 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4184 boost::shared_ptr<Region> region (rv.region());
4186 if (region->locked()) {
4190 nframes_t new_bound;
4193 TimeAxisView* tvp = clicked_axisview;
4194 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4196 if (tv && tv->is_track()) {
4197 speed = tv->get_diskstream()->speed();
4200 if (left_direction) {
4201 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4203 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4207 snap_to (new_bound);
4209 region->trim_end ((nframes_t) (new_bound * speed), this);
4210 rv.region_changed (LengthChanged);
4214 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4216 if (!drag_info.first_move) {
4217 trim_motion_callback (item, event);
4219 if (!clicked_regionview->get_selected()) {
4220 thaw_region_after_trim (*clicked_regionview);
4223 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4224 i != selection->regions.by_layer().end(); ++i)
4226 thaw_region_after_trim (**i);
4227 (*i)->fake_set_opaque (true);
4231 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4233 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4236 motion_frozen_playlists.clear ();
4238 commit_reversible_command();
4240 /* no mouse movement */
4246 Editor::point_trim (GdkEvent* event)
4248 RegionView* rv = clicked_regionview;
4249 nframes_t new_bound = drag_info.current_pointer_frame;
4251 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4252 snap_to (new_bound);
4255 /* Choose action dependant on which button was pressed */
4256 switch (event->button.button) {
4258 trim_op = StartTrim;
4259 begin_reversible_command (_("Start point trim"));
4261 if (rv->get_selected()) {
4263 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4264 i != selection->regions.by_layer().end(); ++i)
4266 if (!(*i)->region()->locked()) {
4267 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4268 XMLNode &before = pl->get_state();
4269 (*i)->region()->trim_front (new_bound, this);
4270 XMLNode &after = pl->get_state();
4271 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4277 if (!rv->region()->locked()) {
4278 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4279 XMLNode &before = pl->get_state();
4280 rv->region()->trim_front (new_bound, this);
4281 XMLNode &after = pl->get_state();
4282 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4286 commit_reversible_command();
4291 begin_reversible_command (_("End point trim"));
4293 if (rv->get_selected()) {
4295 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4297 if (!(*i)->region()->locked()) {
4298 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4299 XMLNode &before = pl->get_state();
4300 (*i)->region()->trim_end (new_bound, this);
4301 XMLNode &after = pl->get_state();
4302 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4308 if (!rv->region()->locked()) {
4309 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4310 XMLNode &before = pl->get_state();
4311 rv->region()->trim_end (new_bound, this);
4312 XMLNode &after = pl->get_state();
4313 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4317 commit_reversible_command();
4326 Editor::thaw_region_after_trim (RegionView& rv)
4328 boost::shared_ptr<Region> region (rv.region());
4330 if (region->locked()) {
4334 region->thaw (_("trimmed region"));
4335 XMLNode &after = region->playlist()->get_state();
4336 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4338 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4340 arv->unhide_envelope ();
4344 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4349 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4350 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4354 Location* location = find_location_from_marker (marker, is_start);
4355 location->set_hidden (true, this);
4360 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4366 drag_info.item = item;
4367 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4368 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4370 range_marker_op = op;
4372 if (!temp_location) {
4373 temp_location = new Location;
4377 case CreateRangeMarker:
4378 case CreateTransportMarker:
4380 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4381 drag_info.copy = true;
4383 drag_info.copy = false;
4385 start_grab (event, selector_cursor);
4389 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4394 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4396 nframes_t start = 0;
4398 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4400 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4401 snap_to (drag_info.current_pointer_frame);
4404 /* only alter selection if the current frame is
4405 different from the last frame position.
4408 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4410 switch (range_marker_op) {
4411 case CreateRangeMarker:
4412 case CreateTransportMarker:
4413 if (drag_info.first_move) {
4414 snap_to (drag_info.grab_frame);
4417 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4418 start = drag_info.current_pointer_frame;
4419 end = drag_info.grab_frame;
4421 end = drag_info.current_pointer_frame;
4422 start = drag_info.grab_frame;
4425 /* first drag: Either add to the selection
4426 or create a new selection.
4429 if (drag_info.first_move) {
4431 temp_location->set (start, end);
4435 update_marker_drag_item (temp_location);
4436 range_marker_drag_rect->show();
4437 range_marker_drag_rect->raise_to_top();
4443 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4444 start_canvas_autoscroll (1);
4448 temp_location->set (start, end);
4450 double x1 = frame_to_pixel (start);
4451 double x2 = frame_to_pixel (end);
4452 crect->property_x1() = x1;
4453 crect->property_x2() = x2;
4455 update_marker_drag_item (temp_location);
4458 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4459 drag_info.first_move = false;
4461 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4466 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4468 Location * newloc = 0;
4471 if (!drag_info.first_move) {
4472 drag_range_markerbar_op (item, event);
4474 switch (range_marker_op) {
4475 case CreateRangeMarker:
4477 begin_reversible_command (_("new range marker"));
4478 XMLNode &before = session->locations()->get_state();
4479 session->locations()->next_available_name(rangename,"unnamed");
4480 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4481 session->locations()->add (newloc, true);
4482 XMLNode &after = session->locations()->get_state();
4483 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4484 commit_reversible_command ();
4486 range_bar_drag_rect->hide();
4487 range_marker_drag_rect->hide();
4491 case CreateTransportMarker:
4492 // popup menu to pick loop or punch
4493 new_transport_marker_context_menu (&event->button, item);
4498 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4500 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4505 start = session->locations()->first_mark_before (drag_info.grab_frame);
4506 end = session->locations()->first_mark_after (drag_info.grab_frame);
4508 if (end == max_frames) {
4509 end = session->current_end_frame ();
4513 start = session->current_start_frame ();
4516 switch (mouse_mode) {
4518 /* find the two markers on either side and then make the selection from it */
4519 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4523 /* find the two markers on either side of the click and make the range out of it */
4524 selection->set (0, start, end);
4533 stop_canvas_autoscroll ();
4539 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4541 drag_info.item = item;
4542 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4543 drag_info.finished_callback = &Editor::end_mouse_zoom;
4545 start_grab (event, zoom_cursor);
4547 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4551 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4556 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4557 snap_to (drag_info.current_pointer_frame);
4559 if (drag_info.first_move) {
4560 snap_to (drag_info.grab_frame);
4564 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4566 /* base start and end on initial click position */
4567 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4568 start = drag_info.current_pointer_frame;
4569 end = drag_info.grab_frame;
4571 end = drag_info.current_pointer_frame;
4572 start = drag_info.grab_frame;
4577 if (drag_info.first_move) {
4579 zoom_rect->raise_to_top();
4582 reposition_zoom_rect(start, end);
4584 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4585 drag_info.first_move = false;
4587 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4592 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4594 if (!drag_info.first_move) {
4595 drag_mouse_zoom (item, event);
4597 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4598 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4600 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4603 temporal_zoom_to_frame (false, drag_info.grab_frame);
4605 temporal_zoom_step (false);
4606 center_screen (drag_info.grab_frame);
4614 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4616 double x1 = frame_to_pixel (start);
4617 double x2 = frame_to_pixel (end);
4618 double y2 = full_canvas_height - 1.0;
4620 zoom_rect->property_x1() = x1;
4621 zoom_rect->property_y1() = 1.0;
4622 zoom_rect->property_x2() = x2;
4623 zoom_rect->property_y2() = y2;
4627 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4629 drag_info.item = item;
4630 drag_info.motion_callback = &Editor::drag_rubberband_select;
4631 drag_info.finished_callback = &Editor::end_rubberband_select;
4633 start_grab (event, cross_hair_cursor);
4635 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4639 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4646 /* use a bigger drag threshold than the default */
4648 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4652 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4653 if (drag_info.first_move) {
4654 snap_to (drag_info.grab_frame);
4656 snap_to (drag_info.current_pointer_frame);
4659 /* base start and end on initial click position */
4661 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4662 start = drag_info.current_pointer_frame;
4663 end = drag_info.grab_frame;
4665 end = drag_info.current_pointer_frame;
4666 start = drag_info.grab_frame;
4669 if (drag_info.current_pointer_y < drag_info.grab_y) {
4670 y1 = drag_info.current_pointer_y;
4671 y2 = drag_info.grab_y;
4673 y2 = drag_info.current_pointer_y;
4674 y1 = drag_info.grab_y;
4678 if (start != end || y1 != y2) {
4680 double x1 = frame_to_pixel (start);
4681 double x2 = frame_to_pixel (end);
4683 rubberband_rect->property_x1() = x1;
4684 rubberband_rect->property_y1() = y1;
4685 rubberband_rect->property_x2() = x2;
4686 rubberband_rect->property_y2() = y2;
4688 rubberband_rect->show();
4689 rubberband_rect->raise_to_top();
4691 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4692 drag_info.first_move = false;
4694 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4699 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4701 if (!drag_info.first_move) {
4703 drag_rubberband_select (item, event);
4706 if (drag_info.current_pointer_y < drag_info.grab_y) {
4707 y1 = drag_info.current_pointer_y;
4708 y2 = drag_info.grab_y;
4711 y2 = drag_info.current_pointer_y;
4712 y1 = drag_info.grab_y;
4716 Selection::Operation op = Keyboard::selection_type (event->button.state);
4719 begin_reversible_command (_("rubberband selection"));
4721 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4722 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4724 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4728 commit_reversible_command ();
4732 selection->clear_tracks();
4733 selection->clear_regions();
4734 selection->clear_points ();
4735 selection->clear_lines ();
4738 rubberband_rect->hide();
4743 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4745 using namespace Gtkmm2ext;
4747 ArdourPrompter prompter (false);
4749 prompter.set_prompt (_("Name for region:"));
4750 prompter.set_initial_text (clicked_regionview->region()->name());
4751 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4752 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4753 prompter.show_all ();
4754 switch (prompter.run ()) {
4755 case Gtk::RESPONSE_ACCEPT:
4757 prompter.get_result(str);
4759 clicked_regionview->region()->set_name (str);
4767 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4769 drag_info.item = item;
4770 drag_info.motion_callback = &Editor::time_fx_motion;
4771 drag_info.finished_callback = &Editor::end_time_fx;
4775 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4779 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4781 RegionView* rv = clicked_regionview;
4783 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4784 snap_to (drag_info.current_pointer_frame);
4787 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4791 if (drag_info.current_pointer_frame > rv->region()->position()) {
4792 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4795 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4796 drag_info.first_move = false;
4798 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4802 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4804 clicked_regionview->get_time_axis_view().hide_timestretch ();
4806 if (drag_info.first_move) {
4810 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4811 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4813 begin_reversible_command (_("timestretch"));
4815 if (run_timestretch (selection->regions, percentage) == 0) {
4816 session->commit_reversible_command ();
4821 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4823 /* no brushing without a useful snap setting */
4826 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4829 switch (snap_mode) {
4831 return; /* can't work because it allows region to be placed anywhere */
4836 switch (snap_type) {
4839 case SnapToEditCursor:
4846 /* don't brush a copy over the original */
4848 if (pos == rv->region()->position()) {
4852 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4854 if (rtv == 0 || !rtv->is_track()) {
4858 boost::shared_ptr<Playlist> playlist = rtv->playlist();
4859 double speed = rtv->get_diskstream()->speed();
4861 XMLNode &before = playlist->get_state();
4862 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4863 XMLNode &after = playlist->get_state();
4864 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4866 // playlist is frozen, so we have to update manually
4868 playlist->Modified(); /* EMIT SIGNAL */
4872 Editor::track_height_step_timeout ()
4875 struct timeval delta;
4877 gettimeofday (&now, 0);
4878 timersub (&now, &last_track_height_step_timestamp, &delta);
4880 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4881 current_stepping_trackview = 0;