2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <pbd/error.h>
29 #include <gtkmm2ext/utils.h>
30 #include <pbd/memento_command.h>
32 #include "ardour_ui.h"
34 #include "time_axis_view.h"
35 #include "audio_time_axis.h"
36 #include "audio_region_view.h"
37 #include "midi_region_view.h"
39 #include "streamview.h"
40 #include "region_gain_line.h"
41 #include "automation_time_axis.h"
44 #include "selection.h"
47 #include "rgb_macros.h"
49 #include <ardour/types.h>
50 #include <ardour/profile.h>
51 #include <ardour/route.h>
52 #include <ardour/audio_track.h>
53 #include <ardour/audio_diskstream.h>
54 #include <ardour/playlist.h>
55 #include <ardour/audioplaylist.h>
56 #include <ardour/audioregion.h>
57 #include <ardour/midi_region.h>
58 #include <ardour/dB.h>
59 #include <ardour/utils.h>
60 #include <ardour/region_factory.h>
67 using namespace ARDOUR;
71 using namespace Editing;
74 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
88 switch (event->type) {
89 case GDK_BUTTON_RELEASE:
90 case GDK_BUTTON_PRESS:
91 case GDK_2BUTTON_PRESS:
92 case GDK_3BUTTON_PRESS:
93 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
95 case GDK_MOTION_NOTIFY:
96 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
98 case GDK_ENTER_NOTIFY:
99 case GDK_LEAVE_NOTIFY:
100 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
103 case GDK_KEY_RELEASE:
104 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
107 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
111 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
112 position is negative (as can be the case with motion events in particular),
113 the frame location is always positive.
116 return pixel_to_frame (*pcx);
120 Editor::mouse_mode_toggled (MouseMode m)
122 if (ignore_mouse_mode_toggle) {
128 if (mouse_select_button.get_active()) {
134 if (mouse_move_button.get_active()) {
140 if (mouse_gain_button.get_active()) {
146 if (mouse_zoom_button.get_active()) {
152 if (mouse_timefx_button.get_active()) {
158 if (mouse_audition_button.get_active()) {
169 Editor::set_mouse_mode (MouseMode m, bool force)
171 if (drag_info.item) {
175 if (!force && m == mouse_mode) {
183 if (mouse_mode != MouseRange) {
185 /* in all modes except range, hide the range selection,
186 show the object (region) selection.
189 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
190 (*i)->set_should_show_selection (true);
192 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
193 (*i)->hide_selection ();
199 in range mode,show the range selection.
202 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
203 if ((*i)->get_selected()) {
204 (*i)->show_selection (selection->time);
209 /* XXX the hack of unsetting all other buttongs should go
210 away once GTK2 allows us to use regular radio buttons drawn like
211 normal buttons, rather than my silly GroupedButton hack.
214 ignore_mouse_mode_toggle = true;
216 switch (mouse_mode) {
218 mouse_select_button.set_active (true);
219 current_canvas_cursor = selector_cursor;
223 mouse_move_button.set_active (true);
224 current_canvas_cursor = grabber_cursor;
228 mouse_gain_button.set_active (true);
229 current_canvas_cursor = cross_hair_cursor;
233 mouse_zoom_button.set_active (true);
234 current_canvas_cursor = zoom_cursor;
238 mouse_timefx_button.set_active (true);
239 current_canvas_cursor = time_fx_cursor; // just use playhead
243 mouse_audition_button.set_active (true);
244 current_canvas_cursor = speaker_cursor;
248 ignore_mouse_mode_toggle = false;
251 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
256 Editor::step_mouse_mode (bool next)
258 switch (current_mouse_mode()) {
260 if (next) set_mouse_mode (MouseRange);
261 else set_mouse_mode (MouseTimeFX);
265 if (next) set_mouse_mode (MouseZoom);
266 else set_mouse_mode (MouseObject);
270 if (next) set_mouse_mode (MouseGain);
271 else set_mouse_mode (MouseRange);
275 if (next) set_mouse_mode (MouseTimeFX);
276 else set_mouse_mode (MouseZoom);
280 if (next) set_mouse_mode (MouseAudition);
281 else set_mouse_mode (MouseGain);
285 if (next) set_mouse_mode (MouseObject);
286 else set_mouse_mode (MouseTimeFX);
292 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
296 /* in object/audition/timefx mode, any button press sets
297 the selection if the object can be selected. this is a
298 bit of hack, because we want to avoid this if the
299 mouse operation is a region alignment.
301 note: not dbl-click or triple-click
304 if (((mouse_mode != MouseObject) &&
305 (mouse_mode != MouseAudition || item_type != RegionItem) &&
306 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
307 (mouse_mode != MouseRange)) ||
309 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
314 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
316 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
318 /* almost no selection action on modified button-2 or button-3 events */
320 if (item_type != RegionItem && event->button.button != 2) {
326 Selection::Operation op = Keyboard::selection_type (event->button.state);
327 bool press = (event->type == GDK_BUTTON_PRESS);
329 // begin_reversible_command (_("select on click"));
333 case RegionViewNameHighlight:
335 case FadeInHandleItem:
337 case FadeOutHandleItem:
339 if (mouse_mode != MouseRange) {
340 commit = set_selected_regionview_from_click (press, op, true);
341 } else if (event->type == GDK_BUTTON_PRESS) {
342 commit = set_selected_track_from_click (press, op, false);
346 case CrossfadeViewItem:
347 commit = set_selected_track_from_click (press, op, false);
350 case GainAutomationControlPointItem:
351 case PanAutomationControlPointItem:
352 case RedirectAutomationControlPointItem:
353 commit = set_selected_track_from_click (press, op, true);
354 if (mouse_mode != MouseRange) {
355 commit |= set_selected_control_point_from_click (op, false);
360 /* for context click or range selection, select track */
361 if (event->button.button == 3) {
362 commit = set_selected_track_from_click (press, op, true);
363 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
364 commit = set_selected_track_from_click (press, op, false);
368 case AutomationTrackItem:
369 commit = set_selected_track_from_click (press, op, true);
377 // commit_reversible_command ();
382 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
384 track_canvas.grab_focus();
386 if (session && session->actively_recording()) {
390 button_selection (item, event, item_type);
392 if (drag_info.item == 0 &&
393 (Keyboard::is_delete_event (&event->button) ||
394 Keyboard::is_context_menu_event (&event->button) ||
395 Keyboard::is_edit_event (&event->button))) {
397 /* handled by button release */
401 switch (event->button.button) {
404 if (event->type == GDK_BUTTON_PRESS) {
406 if (drag_info.item) {
407 drag_info.item->ungrab (event->button.time);
410 /* single mouse clicks on any of these item types operate
411 independent of mouse mode, mostly because they are
412 not on the main track canvas or because we want
418 case PlayheadCursorItem:
419 start_cursor_grab (item, event);
423 if (Keyboard::modifier_state_equals (event->button.state,
424 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
425 hide_marker (item, event);
427 start_marker_grab (item, event);
431 case TempoMarkerItem:
432 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
433 start_tempo_marker_copy_grab (item, event);
435 start_tempo_marker_grab (item, event);
439 case MeterMarkerItem:
440 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
441 start_meter_marker_copy_grab (item, event);
443 start_meter_marker_grab (item, event);
453 case RangeMarkerBarItem:
454 start_range_markerbar_op (item, event, CreateRangeMarker);
458 case TransportMarkerBarItem:
459 start_range_markerbar_op (item, event, CreateTransportMarker);
468 switch (mouse_mode) {
471 case StartSelectionTrimItem:
472 start_selection_op (item, event, SelectionStartTrim);
475 case EndSelectionTrimItem:
476 start_selection_op (item, event, SelectionEndTrim);
480 if (Keyboard::modifier_state_contains
481 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
482 // contains and not equals because I can't use alt as a modifier alone.
483 start_selection_grab (item, event);
484 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
485 /* grab selection for moving */
486 start_selection_op (item, event, SelectionMove);
489 /* this was debated, but decided the more common action was to
490 make a new selection */
491 start_selection_op (item, event, CreateSelection);
496 start_selection_op (item, event, CreateSelection);
502 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
503 event->type == GDK_BUTTON_PRESS) {
505 start_rubberband_select (item, event);
507 } else if (event->type == GDK_BUTTON_PRESS) {
510 case FadeInHandleItem:
511 start_fade_in_grab (item, event);
514 case FadeOutHandleItem:
515 start_fade_out_grab (item, event);
519 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
520 start_region_copy_grab (item, event);
521 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
522 start_region_brush_grab (item, event);
524 start_region_grab (item, event);
528 case RegionViewNameHighlight:
529 start_trim (item, event);
534 /* rename happens on edit clicks */
535 start_trim (clicked_regionview->get_name_highlight(), event);
539 case GainAutomationControlPointItem:
540 case PanAutomationControlPointItem:
541 case RedirectAutomationControlPointItem:
542 start_control_point_grab (item, event);
546 case GainAutomationLineItem:
547 case PanAutomationLineItem:
548 case RedirectAutomationLineItem:
549 start_line_grab_from_line (item, event);
554 case AutomationTrackItem:
555 start_rubberband_select (item, event);
559 case ImageFrameHandleStartItem:
560 imageframe_start_handle_op(item, event) ;
563 case ImageFrameHandleEndItem:
564 imageframe_end_handle_op(item, event) ;
567 case MarkerViewHandleStartItem:
568 markerview_item_start_handle_op(item, event) ;
571 case MarkerViewHandleEndItem:
572 markerview_item_end_handle_op(item, event) ;
576 start_markerview_grab(item, event) ;
579 start_imageframe_grab(item, event) ;
597 // start_line_grab_from_regionview (item, event);
600 case GainControlPointItem:
601 start_control_point_grab (item, event);
605 start_line_grab_from_line (item, event);
608 case GainAutomationControlPointItem:
609 case PanAutomationControlPointItem:
610 case RedirectAutomationControlPointItem:
611 start_control_point_grab (item, event);
622 case GainAutomationControlPointItem:
623 case PanAutomationControlPointItem:
624 case RedirectAutomationControlPointItem:
625 start_control_point_grab (item, event);
628 case GainAutomationLineItem:
629 case PanAutomationLineItem:
630 case RedirectAutomationLineItem:
631 start_line_grab_from_line (item, event);
635 // XXX need automation mode to identify which
637 // start_line_grab_from_regionview (item, event);
647 if (event->type == GDK_BUTTON_PRESS) {
648 start_mouse_zoom (item, event);
655 if (item_type == RegionItem) {
656 start_time_fx (item, event);
661 /* handled in release */
670 switch (mouse_mode) {
672 if (event->type == GDK_BUTTON_PRESS) {
675 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
676 start_region_copy_grab (item, event);
678 start_region_grab (item, event);
682 case GainAutomationControlPointItem:
683 case PanAutomationControlPointItem:
684 case RedirectAutomationControlPointItem:
685 start_control_point_grab (item, event);
696 case RegionViewNameHighlight:
697 start_trim (item, event);
702 start_trim (clicked_regionview->get_name_highlight(), event);
713 if (event->type == GDK_BUTTON_PRESS) {
714 /* relax till release */
721 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
722 temporal_zoom_session();
724 temporal_zoom_to_frame (true, event_frame(event));
747 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
749 nframes_t where = event_frame (event, 0, 0);
751 /* no action if we're recording */
753 if (session && session->actively_recording()) {
757 /* first, see if we're finishing a drag ... */
759 if (drag_info.item) {
760 if (end_grab (item, event)) {
761 /* grab dragged, so do nothing else */
766 button_selection (item, event, item_type);
768 /* edit events get handled here */
770 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
776 case TempoMarkerItem:
777 edit_tempo_marker (item);
780 case MeterMarkerItem:
781 edit_meter_marker (item);
785 if (clicked_regionview->name_active()) {
786 return mouse_rename_region (item, event);
796 /* context menu events get handled here */
798 if (Keyboard::is_context_menu_event (&event->button)) {
800 if (drag_info.item == 0) {
802 /* no matter which button pops up the context menu, tell the menu
803 widget to use button 1 to drive menu selection.
808 case FadeInHandleItem:
810 case FadeOutHandleItem:
811 popup_fade_context_menu (1, event->button.time, item, item_type);
816 case RegionViewNameHighlight:
819 case AutomationTrackItem:
820 case CrossfadeViewItem:
821 popup_track_context_menu (1, event->button.time, where);
825 case RangeMarkerBarItem:
826 case TransportMarkerBarItem:
829 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
833 marker_context_menu (&event->button, item);
836 case TempoMarkerItem:
837 tm_marker_context_menu (&event->button, item);
840 case MeterMarkerItem:
841 tm_marker_context_menu (&event->button, item);
846 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
848 case ImageFrameTimeAxisItem:
849 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
852 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
854 case MarkerTimeAxisItem:
855 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
867 /* delete events get handled here */
869 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
872 case TempoMarkerItem:
873 remove_tempo_marker (item);
876 case MeterMarkerItem:
877 remove_meter_marker (item);
881 remove_marker (*item, event);
885 if (mouse_mode == MouseObject) {
886 remove_clicked_region ();
890 case GainControlPointItem:
891 if (mouse_mode == MouseGain) {
892 remove_gain_control_point (item, event);
896 case GainAutomationControlPointItem:
897 case PanAutomationControlPointItem:
898 case RedirectAutomationControlPointItem:
899 remove_control_point (item, event);
908 switch (event->button.button) {
912 /* see comments in button_press_handler */
914 case PlayheadCursorItem:
917 case GainAutomationLineItem:
918 case PanAutomationLineItem:
919 case RedirectAutomationLineItem:
920 case StartSelectionTrimItem:
921 case EndSelectionTrimItem:
925 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
926 snap_to (where, 0, true);
928 mouse_add_new_marker (where);
932 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
935 mouse_add_new_tempo_event (where);
939 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
947 switch (mouse_mode) {
950 case AutomationTrackItem:
951 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
965 // Gain only makes sense for audio regions
967 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
973 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
977 case AutomationTrackItem:
978 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
979 add_automation_event (item, event, where, event->button.y);
990 audition_selected_region ();
1007 switch (mouse_mode) {
1010 switch (item_type) {
1012 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1014 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1017 // Button2 click is unused
1030 // x_style_paste (where, 1.0);
1050 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1056 switch (item_type) {
1057 case GainControlPointItem:
1058 if (mouse_mode == MouseGain) {
1059 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1060 cp->set_visible (true);
1064 at_y = cp->get_y ();
1065 cp->item->i2w (at_x, at_y);
1069 fraction = 1.0 - (cp->get_y() / cp->line.height());
1071 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1072 show_verbose_canvas_cursor ();
1074 if (is_drawable()) {
1075 track_canvas.get_window()->set_cursor (*fader_cursor);
1080 case GainAutomationControlPointItem:
1081 case PanAutomationControlPointItem:
1082 case RedirectAutomationControlPointItem:
1083 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1084 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1085 cp->set_visible (true);
1089 at_y = cp->get_y ();
1090 cp->item->i2w (at_x, at_y);
1094 fraction = 1.0 - (cp->get_y() / cp->line.height());
1096 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1097 show_verbose_canvas_cursor ();
1099 if (is_drawable()) {
1100 track_canvas.get_window()->set_cursor (*fader_cursor);
1106 if (mouse_mode == MouseGain) {
1107 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1109 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1110 if (is_drawable()) {
1111 track_canvas.get_window()->set_cursor (*fader_cursor);
1116 case GainAutomationLineItem:
1117 case RedirectAutomationLineItem:
1118 case PanAutomationLineItem:
1119 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1121 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1123 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1125 if (is_drawable()) {
1126 track_canvas.get_window()->set_cursor (*fader_cursor);
1131 case RegionViewNameHighlight:
1132 if (is_drawable() && mouse_mode == MouseObject) {
1133 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1137 case StartSelectionTrimItem:
1138 case EndSelectionTrimItem:
1141 case ImageFrameHandleStartItem:
1142 case ImageFrameHandleEndItem:
1143 case MarkerViewHandleStartItem:
1144 case MarkerViewHandleEndItem:
1147 if (is_drawable()) {
1148 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1152 case EditCursorItem:
1153 case PlayheadCursorItem:
1154 if (is_drawable()) {
1155 track_canvas.get_window()->set_cursor (*grabber_cursor);
1159 case RegionViewName:
1161 /* when the name is not an active item, the entire name highlight is for trimming */
1163 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1164 if (mouse_mode == MouseObject && is_drawable()) {
1165 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1171 case AutomationTrackItem:
1172 if (is_drawable()) {
1173 Gdk::Cursor *cursor;
1174 switch (mouse_mode) {
1176 cursor = selector_cursor;
1179 cursor = zoom_cursor;
1182 cursor = cross_hair_cursor;
1186 track_canvas.get_window()->set_cursor (*cursor);
1188 AutomationTimeAxisView* atv;
1189 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1190 clear_entered_track = false;
1191 set_entered_track (atv);
1197 case RangeMarkerBarItem:
1198 case TransportMarkerBarItem:
1201 if (is_drawable()) {
1202 time_canvas.get_window()->set_cursor (*timebar_cursor);
1207 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1210 marker->set_color_rgba (color_map[cEnteredMarker]);
1212 case MeterMarkerItem:
1213 case TempoMarkerItem:
1214 if (is_drawable()) {
1215 time_canvas.get_window()->set_cursor (*timebar_cursor);
1218 case FadeInHandleItem:
1219 case FadeOutHandleItem:
1220 if (mouse_mode == MouseObject) {
1221 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1223 rect->property_fill_color_rgba() = 0;
1224 rect->property_outline_pixels() = 1;
1233 /* second pass to handle entered track status in a comprehensible way.
1236 switch (item_type) {
1238 case GainAutomationLineItem:
1239 case RedirectAutomationLineItem:
1240 case PanAutomationLineItem:
1241 case GainControlPointItem:
1242 case GainAutomationControlPointItem:
1243 case PanAutomationControlPointItem:
1244 case RedirectAutomationControlPointItem:
1245 /* these do not affect the current entered track state */
1246 clear_entered_track = false;
1249 case AutomationTrackItem:
1250 /* handled above already */
1254 set_entered_track (0);
1262 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1271 switch (item_type) {
1272 case GainControlPointItem:
1273 case GainAutomationControlPointItem:
1274 case PanAutomationControlPointItem:
1275 case RedirectAutomationControlPointItem:
1276 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1277 if (cp->line.npoints() > 1) {
1278 if (!cp->selected) {
1279 cp->set_visible (false);
1283 if (is_drawable()) {
1284 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1287 hide_verbose_canvas_cursor ();
1290 case RegionViewNameHighlight:
1291 case StartSelectionTrimItem:
1292 case EndSelectionTrimItem:
1293 case EditCursorItem:
1294 case PlayheadCursorItem:
1297 case ImageFrameHandleStartItem:
1298 case ImageFrameHandleEndItem:
1299 case MarkerViewHandleStartItem:
1300 case MarkerViewHandleEndItem:
1303 if (is_drawable()) {
1304 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1309 case GainAutomationLineItem:
1310 case RedirectAutomationLineItem:
1311 case PanAutomationLineItem:
1312 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1314 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1316 line->property_fill_color_rgba() = al->get_line_color();
1318 if (is_drawable()) {
1319 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1323 case RegionViewName:
1324 /* see enter_handler() for notes */
1325 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1326 if (is_drawable() && mouse_mode == MouseObject) {
1327 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1332 case RangeMarkerBarItem:
1333 case TransportMarkerBarItem:
1337 if (is_drawable()) {
1338 time_canvas.get_window()->set_cursor (*timebar_cursor);
1343 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1346 loc = find_location_from_marker (marker, is_start);
1347 if (loc) location_flags_changed (loc, this);
1349 case MeterMarkerItem:
1350 case TempoMarkerItem:
1352 if (is_drawable()) {
1353 time_canvas.get_window()->set_cursor (*timebar_cursor);
1358 case FadeInHandleItem:
1359 case FadeOutHandleItem:
1360 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1362 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1364 rect->property_fill_color_rgba() = rv->get_fill_color();
1365 rect->property_outline_pixels() = 0;
1370 case AutomationTrackItem:
1371 if (is_drawable()) {
1372 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1373 clear_entered_track = true;
1374 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1386 Editor::left_automation_track ()
1388 if (clear_entered_track) {
1389 set_entered_track (0);
1390 clear_entered_track = false;
1396 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1400 /* We call this so that MOTION_NOTIFY events continue to be
1401 delivered to the canvas. We need to do this because we set
1402 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1403 the density of the events, at the expense of a round-trip
1404 to the server. Given that this will mostly occur on cases
1405 where DISPLAY = :0.0, and given the cost of what the motion
1406 event might do, its a good tradeoff.
1409 track_canvas.get_pointer (x, y);
1411 if (current_stepping_trackview) {
1412 /* don't keep the persistent stepped trackview if the mouse moves */
1413 current_stepping_trackview = 0;
1414 step_timeout.disconnect ();
1417 if (session && session->actively_recording()) {
1418 /* Sorry. no dragging stuff around while we record */
1422 drag_info.item_type = item_type;
1423 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1424 &drag_info.current_pointer_y);
1426 if (!from_autoscroll && drag_info.item) {
1427 /* item != 0 is the best test i can think of for dragging.
1429 if (!drag_info.move_threshold_passed) {
1431 bool x_threshold_passed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1432 bool y_threshold_passed = (abs ((int) (drag_info.current_pointer_y - drag_info.grab_y)) > 4);
1434 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1436 // and change the initial grab loc/frame if this drag info wants us to
1438 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1439 drag_info.grab_frame = drag_info.current_pointer_frame;
1440 drag_info.grab_x = drag_info.current_pointer_x;
1441 drag_info.grab_y = drag_info.current_pointer_y;
1442 drag_info.last_pointer_frame = drag_info.grab_frame;
1443 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1448 switch (item_type) {
1449 case PlayheadCursorItem:
1450 case EditCursorItem:
1452 case GainControlPointItem:
1453 case RedirectAutomationControlPointItem:
1454 case GainAutomationControlPointItem:
1455 case PanAutomationControlPointItem:
1456 case TempoMarkerItem:
1457 case MeterMarkerItem:
1458 case RegionViewNameHighlight:
1459 case StartSelectionTrimItem:
1460 case EndSelectionTrimItem:
1463 case RedirectAutomationLineItem:
1464 case GainAutomationLineItem:
1465 case PanAutomationLineItem:
1466 case FadeInHandleItem:
1467 case FadeOutHandleItem:
1470 case ImageFrameHandleStartItem:
1471 case ImageFrameHandleEndItem:
1472 case MarkerViewHandleStartItem:
1473 case MarkerViewHandleEndItem:
1476 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1477 (event->motion.state & Gdk::BUTTON2_MASK))) {
1478 if (!from_autoscroll) {
1479 maybe_autoscroll (event);
1481 (this->*(drag_info.motion_callback)) (item, event);
1490 switch (mouse_mode) {
1495 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1496 (event->motion.state & GDK_BUTTON2_MASK))) {
1497 if (!from_autoscroll) {
1498 maybe_autoscroll (event);
1500 (this->*(drag_info.motion_callback)) (item, event);
1511 track_canvas_motion (event);
1512 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1520 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1522 if (drag_info.item == 0) {
1523 fatal << _("programming error: start_grab called without drag item") << endmsg;
1529 cursor = grabber_cursor;
1532 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1534 if (event->button.button == 2) {
1535 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1536 drag_info.y_constrained = true;
1537 drag_info.x_constrained = false;
1539 drag_info.y_constrained = false;
1540 drag_info.x_constrained = true;
1543 drag_info.x_constrained = false;
1544 drag_info.y_constrained = false;
1547 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1548 drag_info.last_pointer_frame = drag_info.grab_frame;
1549 drag_info.current_pointer_frame = drag_info.grab_frame;
1550 drag_info.current_pointer_x = drag_info.grab_x;
1551 drag_info.current_pointer_y = drag_info.grab_y;
1552 drag_info.cumulative_x_drag = 0;
1553 drag_info.cumulative_y_drag = 0;
1554 drag_info.first_move = true;
1555 drag_info.move_threshold_passed = false;
1556 drag_info.want_move_threshold = false;
1557 drag_info.pointer_frame_offset = 0;
1558 drag_info.brushing = false;
1559 drag_info.copied_location = 0;
1561 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1563 event->button.time);
1565 if (session && session->transport_rolling()) {
1566 drag_info.was_rolling = true;
1568 drag_info.was_rolling = false;
1571 switch (snap_type) {
1572 case SnapToRegionStart:
1573 case SnapToRegionEnd:
1574 case SnapToRegionSync:
1575 case SnapToRegionBoundary:
1576 build_region_boundary_cache ();
1584 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1586 drag_info.item->ungrab (0);
1587 drag_info.item = new_item;
1590 cursor = grabber_cursor;
1593 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1597 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1599 bool did_drag = false;
1601 stop_canvas_autoscroll ();
1603 if (drag_info.item == 0) {
1607 drag_info.item->ungrab (event->button.time);
1609 if (drag_info.finished_callback) {
1610 (this->*(drag_info.finished_callback)) (item, event);
1613 did_drag = !drag_info.first_move;
1615 hide_verbose_canvas_cursor();
1618 drag_info.copy = false;
1619 drag_info.motion_callback = 0;
1620 drag_info.finished_callback = 0;
1621 drag_info.last_trackview = 0;
1622 drag_info.last_frame_position = 0;
1623 drag_info.grab_frame = 0;
1624 drag_info.last_pointer_frame = 0;
1625 drag_info.current_pointer_frame = 0;
1626 drag_info.brushing = false;
1628 if (drag_info.copied_location) {
1629 delete drag_info.copied_location;
1630 drag_info.copied_location = 0;
1637 Editor::set_edit_cursor (GdkEvent* event)
1639 nframes_t pointer_frame = event_frame (event);
1641 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1642 if (snap_type != SnapToEditCursor) {
1643 snap_to (pointer_frame);
1647 edit_cursor->set_position (pointer_frame);
1648 edit_cursor_clock.set (pointer_frame);
1652 Editor::set_playhead_cursor (GdkEvent* event)
1654 nframes_t pointer_frame = event_frame (event);
1656 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1657 snap_to (pointer_frame);
1661 session->request_locate (pointer_frame, session->transport_rolling());
1666 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1668 drag_info.item = item;
1669 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1670 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1674 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1675 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1679 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1681 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1685 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1687 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1689 nframes_t fade_length;
1691 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1692 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1698 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1702 if (pos < (arv->region()->position() + 64)) {
1703 fade_length = 64; // this should be a minimum defined somewhere
1704 } else if (pos > arv->region()->last_frame()) {
1705 fade_length = arv->region()->length();
1707 fade_length = pos - arv->region()->position();
1709 /* mapover the region selection */
1711 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1713 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1719 tmp->reset_fade_in_shape_width (fade_length);
1722 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1724 drag_info.first_move = false;
1728 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1730 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1732 nframes_t fade_length;
1734 if (drag_info.first_move) return;
1736 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1737 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1742 if (pos < (arv->region()->position() + 64)) {
1743 fade_length = 64; // this should be a minimum defined somewhere
1744 } else if (pos > arv->region()->last_frame()) {
1745 fade_length = arv->region()->length();
1747 fade_length = pos - arv->region()->position();
1750 begin_reversible_command (_("change fade in length"));
1752 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1754 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1760 AutomationList& alist = tmp->audio_region()->fade_in();
1761 XMLNode &before = alist.get_state();
1763 tmp->audio_region()->set_fade_in_length (fade_length);
1765 XMLNode &after = alist.get_state();
1766 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1769 commit_reversible_command ();
1773 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1775 drag_info.item = item;
1776 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1777 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1781 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1782 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1786 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1788 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1792 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1794 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1796 nframes_t fade_length;
1798 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1799 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1805 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1809 if (pos > (arv->region()->last_frame() - 64)) {
1810 fade_length = 64; // this should really be a minimum fade defined somewhere
1812 else if (pos < arv->region()->position()) {
1813 fade_length = arv->region()->length();
1816 fade_length = arv->region()->last_frame() - pos;
1819 /* mapover the region selection */
1821 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1823 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1829 tmp->reset_fade_out_shape_width (fade_length);
1832 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1834 drag_info.first_move = false;
1838 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1840 if (drag_info.first_move) return;
1842 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1844 nframes_t fade_length;
1846 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1847 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1853 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1857 if (pos > (arv->region()->last_frame() - 64)) {
1858 fade_length = 64; // this should really be a minimum fade defined somewhere
1860 else if (pos < arv->region()->position()) {
1861 fade_length = arv->region()->length();
1864 fade_length = arv->region()->last_frame() - pos;
1867 begin_reversible_command (_("change fade out length"));
1869 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1871 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1877 AutomationList& alist = tmp->audio_region()->fade_out();
1878 XMLNode &before = alist.get_state();
1880 tmp->audio_region()->set_fade_out_length (fade_length);
1882 XMLNode &after = alist.get_state();
1883 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1886 commit_reversible_command ();
1890 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1892 drag_info.item = item;
1893 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1894 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1898 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1899 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1903 Cursor* cursor = (Cursor *) drag_info.data;
1905 if (cursor == playhead_cursor) {
1906 _dragging_playhead = true;
1908 if (session && drag_info.was_rolling) {
1909 session->request_stop ();
1912 if (session && session->is_auditioning()) {
1913 session->cancel_audition ();
1917 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1919 show_verbose_time_cursor (cursor->current_frame, 10);
1923 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1925 Cursor* cursor = (Cursor *) drag_info.data;
1926 nframes_t adjusted_frame;
1928 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1929 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1935 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1936 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1937 snap_to (adjusted_frame);
1941 if (adjusted_frame == drag_info.last_pointer_frame) return;
1943 cursor->set_position (adjusted_frame);
1945 if (cursor == edit_cursor) {
1946 edit_cursor_clock.set (cursor->current_frame);
1948 UpdateAllTransportClocks (cursor->current_frame);
1951 show_verbose_time_cursor (cursor->current_frame, 10);
1953 drag_info.last_pointer_frame = adjusted_frame;
1954 drag_info.first_move = false;
1958 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1960 if (drag_info.first_move) return;
1962 cursor_drag_motion_callback (item, event);
1964 _dragging_playhead = false;
1966 if (item == &playhead_cursor->canvas_item) {
1968 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1970 } else if (item == &edit_cursor->canvas_item) {
1971 edit_cursor->set_position (edit_cursor->current_frame);
1972 edit_cursor_clock.set (edit_cursor->current_frame);
1977 Editor::update_marker_drag_item (Location *location)
1979 double x1 = frame_to_pixel (location->start());
1980 double x2 = frame_to_pixel (location->end());
1982 if (location->is_mark()) {
1983 marker_drag_line_points.front().set_x(x1);
1984 marker_drag_line_points.back().set_x(x1);
1985 marker_drag_line->property_points() = marker_drag_line_points;
1988 range_marker_drag_rect->property_x1() = x1;
1989 range_marker_drag_rect->property_x2() = x2;
1994 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
1998 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1999 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2005 Location *location = find_location_from_marker (marker, is_start);
2007 drag_info.item = item;
2008 drag_info.data = marker;
2009 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2010 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2014 drag_info.copied_location = new Location (*location);
2015 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2017 update_marker_drag_item (location);
2019 if (location->is_mark()) {
2020 marker_drag_line->show();
2021 marker_drag_line->raise_to_top();
2024 range_marker_drag_rect->show();
2025 range_marker_drag_rect->raise_to_top();
2028 if (is_start) show_verbose_time_cursor (location->start(), 10);
2029 else show_verbose_time_cursor (location->end(), 10);
2033 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2036 Marker* marker = (Marker *) drag_info.data;
2037 Location *real_location;
2038 Location *copy_location;
2040 bool move_both = false;
2044 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2045 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2051 nframes_t next = newframe;
2053 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2054 snap_to (newframe, 0, true);
2057 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2061 /* call this to find out if its the start or end */
2063 real_location = find_location_from_marker (marker, is_start);
2065 /* use the copy that we're "dragging" around */
2067 copy_location = drag_info.copied_location;
2069 f_delta = copy_location->end() - copy_location->start();
2071 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2075 if (copy_location->is_mark()) {
2078 copy_location->set_start (newframe);
2082 if (is_start) { // start-of-range marker
2085 copy_location->set_start (newframe);
2086 copy_location->set_end (newframe + f_delta);
2087 } else if (newframe < copy_location->end()) {
2088 copy_location->set_start (newframe);
2090 snap_to (next, 1, true);
2091 copy_location->set_end (next);
2092 copy_location->set_start (newframe);
2095 } else { // end marker
2098 copy_location->set_end (newframe);
2099 copy_location->set_start (newframe - f_delta);
2100 } else if (newframe > copy_location->start()) {
2101 copy_location->set_end (newframe);
2103 } else if (newframe > 0) {
2104 snap_to (next, -1, true);
2105 copy_location->set_start (next);
2106 copy_location->set_end (newframe);
2111 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2112 drag_info.first_move = false;
2114 update_marker_drag_item (copy_location);
2116 LocationMarkers* lm = find_location_markers (real_location);
2117 lm->set_position (copy_location->start(), copy_location->end());
2119 show_verbose_time_cursor (newframe, 10);
2123 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2125 if (drag_info.first_move) {
2126 marker_drag_motion_callback (item, event);
2130 Marker* marker = (Marker *) drag_info.data;
2134 begin_reversible_command ( _("move marker") );
2135 XMLNode &before = session->locations()->get_state();
2137 Location * location = find_location_from_marker (marker, is_start);
2140 if (location->is_mark()) {
2141 location->set_start (drag_info.copied_location->start());
2143 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2147 XMLNode &after = session->locations()->get_state();
2148 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2149 commit_reversible_command ();
2151 marker_drag_line->hide();
2152 range_marker_drag_rect->hide();
2156 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2159 MeterMarker* meter_marker;
2161 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2162 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2166 meter_marker = dynamic_cast<MeterMarker*> (marker);
2168 MetricSection& section (meter_marker->meter());
2170 if (!section.movable()) {
2174 drag_info.item = item;
2175 drag_info.data = marker;
2176 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2177 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2181 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2183 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2187 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2190 MeterMarker* meter_marker;
2192 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2193 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2197 meter_marker = dynamic_cast<MeterMarker*> (marker);
2199 // create a dummy marker for visual representation of moving the copy.
2200 // The actual copying is not done before we reach the finish callback.
2202 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2203 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name,
2204 *new MeterSection(meter_marker->meter()));
2206 drag_info.item = &new_marker->the_item();
2207 drag_info.copy = true;
2208 drag_info.data = new_marker;
2209 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2210 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2214 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2216 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2220 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2222 MeterMarker* marker = (MeterMarker *) drag_info.data;
2223 nframes_t adjusted_frame;
2225 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2226 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2232 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2233 snap_to (adjusted_frame);
2236 if (adjusted_frame == drag_info.last_pointer_frame) return;
2238 marker->set_position (adjusted_frame);
2241 drag_info.last_pointer_frame = adjusted_frame;
2242 drag_info.first_move = false;
2244 show_verbose_time_cursor (adjusted_frame, 10);
2248 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2250 if (drag_info.first_move) return;
2252 meter_marker_drag_motion_callback (drag_info.item, event);
2254 MeterMarker* marker = (MeterMarker *) drag_info.data;
2257 TempoMap& map (session->tempo_map());
2258 map.bbt_time (drag_info.last_pointer_frame, when);
2260 if (drag_info.copy == true) {
2261 begin_reversible_command (_("copy meter mark"));
2262 XMLNode &before = map.get_state();
2263 map.add_meter (marker->meter(), when);
2264 XMLNode &after = map.get_state();
2265 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2266 commit_reversible_command ();
2268 // delete the dummy marker we used for visual representation of copying.
2269 // a new visual marker will show up automatically.
2272 begin_reversible_command (_("move meter mark"));
2273 XMLNode &before = map.get_state();
2274 map.move_meter (marker->meter(), when);
2275 XMLNode &after = map.get_state();
2276 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2277 commit_reversible_command ();
2282 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2285 TempoMarker* tempo_marker;
2287 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2288 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2292 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2293 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2297 MetricSection& section (tempo_marker->tempo());
2299 if (!section.movable()) {
2303 drag_info.item = item;
2304 drag_info.data = marker;
2305 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2306 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2310 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2311 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2315 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2318 TempoMarker* tempo_marker;
2320 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2321 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2325 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2326 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2330 // create a dummy marker for visual representation of moving the copy.
2331 // The actual copying is not done before we reach the finish callback.
2333 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2334 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name,
2335 *new TempoSection(tempo_marker->tempo()));
2337 drag_info.item = &new_marker->the_item();
2338 drag_info.copy = true;
2339 drag_info.data = new_marker;
2340 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2341 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2345 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2347 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2351 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2353 TempoMarker* marker = (TempoMarker *) drag_info.data;
2354 nframes_t adjusted_frame;
2356 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2357 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2363 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2364 snap_to (adjusted_frame);
2367 if (adjusted_frame == drag_info.last_pointer_frame) return;
2369 /* OK, we've moved far enough to make it worth actually move the thing. */
2371 marker->set_position (adjusted_frame);
2373 show_verbose_time_cursor (adjusted_frame, 10);
2375 drag_info.last_pointer_frame = adjusted_frame;
2376 drag_info.first_move = false;
2380 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2382 if (drag_info.first_move) return;
2384 tempo_marker_drag_motion_callback (drag_info.item, event);
2386 TempoMarker* marker = (TempoMarker *) drag_info.data;
2389 TempoMap& map (session->tempo_map());
2390 map.bbt_time (drag_info.last_pointer_frame, when);
2392 if (drag_info.copy == true) {
2393 begin_reversible_command (_("copy tempo mark"));
2394 XMLNode &before = map.get_state();
2395 map.add_tempo (marker->tempo(), when);
2396 XMLNode &after = map.get_state();
2397 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2398 commit_reversible_command ();
2400 // delete the dummy marker we used for visual representation of copying.
2401 // a new visual marker will show up automatically.
2404 begin_reversible_command (_("move tempo mark"));
2405 XMLNode &before = map.get_state();
2406 map.move_tempo (marker->tempo(), when);
2407 XMLNode &after = map.get_state();
2408 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2409 commit_reversible_command ();
2414 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2416 ControlPoint* control_point;
2418 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2419 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2423 // We shouldn't remove the first or last gain point
2424 if (control_point->line.is_last_point(*control_point) ||
2425 control_point->line.is_first_point(*control_point)) {
2429 control_point->line.remove_point (*control_point);
2433 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2435 ControlPoint* control_point;
2437 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2438 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2442 control_point->line.remove_point (*control_point);
2446 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2448 ControlPoint* control_point;
2450 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2451 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2455 drag_info.item = item;
2456 drag_info.data = control_point;
2457 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2458 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2460 start_grab (event, fader_cursor);
2462 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2464 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2465 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2466 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2468 show_verbose_canvas_cursor ();
2472 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2474 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2476 double cx = drag_info.current_pointer_x;
2477 double cy = drag_info.current_pointer_y;
2479 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2480 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2482 if (drag_info.x_constrained) {
2483 cx = drag_info.grab_x;
2485 if (drag_info.y_constrained) {
2486 cy = drag_info.grab_y;
2489 cp->line.parent_group().w2i (cx, cy);
2493 cy = min ((double) cp->line.height(), cy);
2495 //translate cx to frames
2496 nframes_t cx_frames = unit_to_frame (cx);
2498 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2499 snap_to (cx_frames);
2502 float fraction = 1.0 - (cy / cp->line.height());
2506 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2512 cp->line.point_drag (*cp, cx_frames , fraction, push);
2514 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2516 drag_info.first_move = false;
2520 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2522 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2524 if (drag_info.first_move) {
2528 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2529 reset_point_selection ();
2533 control_point_drag_motion_callback (item, event);
2535 cp->line.end_drag (cp);
2539 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2541 switch (mouse_mode) {
2543 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2544 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2552 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2556 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2557 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2561 start_line_grab (al, event);
2565 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2569 nframes_t frame_within_region;
2571 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2575 cx = event->button.x;
2576 cy = event->button.y;
2577 line->parent_group().w2i (cx, cy);
2578 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2580 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2581 current_line_drag_info.after)) {
2582 /* no adjacent points */
2586 drag_info.item = &line->grab_item();
2587 drag_info.data = line;
2588 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2589 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2591 start_grab (event, fader_cursor);
2593 double fraction = 1.0 - (cy / line->height());
2595 line->start_drag (0, drag_info.grab_frame, fraction);
2597 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2598 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2599 show_verbose_canvas_cursor ();
2603 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2605 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2606 double cx = drag_info.current_pointer_x;
2607 double cy = drag_info.current_pointer_y;
2609 line->parent_group().w2i (cx, cy);
2612 fraction = 1.0 - (cy / line->height());
2616 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2622 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2624 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2628 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2630 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2631 line_drag_motion_callback (item, event);
2636 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2638 if (selection->regions.empty() || clicked_regionview == 0) {
2642 drag_info.copy = false;
2643 drag_info.item = item;
2644 drag_info.data = clicked_regionview;
2645 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2646 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2651 TimeAxisView* tvp = clicked_axisview;
2652 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2654 if (tv && tv->is_track()) {
2655 speed = tv->get_diskstream()->speed();
2658 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2659 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2660 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2661 // we want a move threshold
2662 drag_info.want_move_threshold = true;
2664 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2666 begin_reversible_command (_("move region(s)"));
2670 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2672 if (selection->regions.empty() || clicked_regionview == 0) {
2676 drag_info.copy = true;
2677 drag_info.item = item;
2678 drag_info.data = clicked_regionview;
2682 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2683 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2686 if (rtv && rtv->is_track()) {
2687 speed = rtv->get_diskstream()->speed();
2690 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2691 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2692 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2693 // we want a move threshold
2694 drag_info.want_move_threshold = true;
2695 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2696 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2697 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2701 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2703 if (selection->regions.empty() || clicked_regionview == 0) {
2707 drag_info.copy = false;
2708 drag_info.item = item;
2709 drag_info.data = clicked_regionview;
2710 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2711 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2716 TimeAxisView* tvp = clicked_axisview;
2717 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2719 if (tv && tv->is_track()) {
2720 speed = tv->get_diskstream()->speed();
2723 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2724 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2725 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2726 // we want a move threshold
2727 drag_info.want_move_threshold = true;
2728 drag_info.brushing = true;
2730 begin_reversible_command (_("Drag region brush"));
2734 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2738 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2739 nframes_t pending_region_position = 0;
2740 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2741 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2742 bool clamp_y_axis = false;
2743 vector<int32_t> height_list(512) ;
2744 vector<int32_t>::iterator j;
2746 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2748 drag_info.want_move_threshold = false; // don't copy again
2750 /* duplicate the region(s) */
2752 vector<RegionView*> new_regionviews;
2754 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2760 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2761 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2764 nrv = new AudioRegionView (*arv);
2766 nrv = new MidiRegionView (*mrv);
2771 nrv->get_canvas_group()->show ();
2773 new_regionviews.push_back (nrv);
2776 if (new_regionviews.empty()) {
2780 /* reset selection to new regionviews */
2782 selection->set (new_regionviews);
2784 /* reset drag_info data to reflect the fact that we are dragging the copies */
2786 drag_info.data = new_regionviews.front();
2788 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2791 /* Which trackview is this ? */
2793 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2794 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2796 /* The region motion is only processed if the pointer is over
2800 if (!tv || !tv->is_track()) {
2801 /* To make sure we hide the verbose canvas cursor when the mouse is
2802 not held over a track.
2804 hide_verbose_canvas_cursor ();
2808 original_pointer_order = drag_info.last_trackview->order;
2810 /************************************************************
2812 ************************************************************/
2814 if (drag_info.brushing) {
2815 clamp_y_axis = true;
2820 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2822 int32_t children = 0, numtracks = 0;
2823 // XXX hard coding track limit, oh my, so very very bad
2824 bitset <1024> tracks (0x00);
2825 /* get a bitmask representing the visible tracks */
2827 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2828 TimeAxisView *tracklist_timeview;
2829 tracklist_timeview = (*i);
2830 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2831 list<TimeAxisView*> children_list;
2833 /* zeroes are audio tracks. ones are other types. */
2835 if (!rtv2->hidden()) {
2837 if (visible_y_high < rtv2->order) {
2838 visible_y_high = rtv2->order;
2840 if (visible_y_low > rtv2->order) {
2841 visible_y_low = rtv2->order;
2844 if (!rtv2->is_track()) {
2845 tracks = tracks |= (0x01 << rtv2->order);
2848 height_list[rtv2->order] = (*i)->height;
2850 if ((children_list = rtv2->get_child_list()).size() > 0) {
2851 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2852 tracks = tracks |= (0x01 << (rtv2->order + children));
2853 height_list[rtv2->order + children] = (*j)->height;
2861 /* find the actual span according to the canvas */
2863 canvas_pointer_y_span = pointer_y_span;
2864 if (drag_info.last_trackview->order >= tv->order) {
2866 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2867 if (height_list[y] == 0 ) {
2868 canvas_pointer_y_span--;
2873 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2874 if ( height_list[y] == 0 ) {
2875 canvas_pointer_y_span++;
2880 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2881 RegionView* rv2 = (*i);
2882 double ix1, ix2, iy1, iy2;
2885 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2886 rv2->get_canvas_group()->i2w (ix1, iy1);
2887 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2888 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2890 if (rtv2->order != original_pointer_order) {
2891 /* this isn't the pointer track */
2893 if (canvas_pointer_y_span > 0) {
2895 /* moving up the canvas */
2896 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2898 int32_t visible_tracks = 0;
2899 while (visible_tracks < canvas_pointer_y_span ) {
2902 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2903 /* we're passing through a hidden track */
2908 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2909 clamp_y_axis = true;
2913 clamp_y_axis = true;
2916 } else if (canvas_pointer_y_span < 0) {
2918 /*moving down the canvas*/
2920 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2923 int32_t visible_tracks = 0;
2925 while (visible_tracks > canvas_pointer_y_span ) {
2928 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2932 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2933 clamp_y_axis = true;
2938 clamp_y_axis = true;
2944 /* this is the pointer's track */
2945 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2946 clamp_y_axis = true;
2947 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2948 clamp_y_axis = true;
2956 } else if (drag_info.last_trackview == tv) {
2957 clamp_y_axis = true;
2961 if (!clamp_y_axis) {
2962 drag_info.last_trackview = tv;
2965 /************************************************************
2967 ************************************************************/
2969 /* compute the amount of pointer motion in frames, and where
2970 the region would be if we moved it by that much.
2973 if (drag_info.move_threshold_passed) {
2975 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2977 nframes_t sync_frame;
2978 nframes_t sync_offset;
2981 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2983 sync_offset = rv->region()->sync_offset (sync_dir);
2984 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
2986 /* we snap if the snap modifier is not enabled.
2989 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2990 snap_to (sync_frame);
2993 if (sync_frame - sync_offset <= sync_frame) {
2994 pending_region_position = sync_frame - (sync_dir*sync_offset);
2996 pending_region_position = 0;
3000 pending_region_position = 0;
3003 if (pending_region_position > max_frames - rv->region()->length()) {
3004 pending_region_position = drag_info.last_frame_position;
3007 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3009 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3011 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3012 to make it appear at the new location.
3015 if (pending_region_position > drag_info.last_frame_position) {
3016 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3018 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3021 drag_info.last_frame_position = pending_region_position;
3028 /* threshold not passed */
3033 /*************************************************************
3035 ************************************************************/
3037 if (x_delta == 0 && (pointer_y_span == 0)) {
3038 /* haven't reached next snap point, and we're not switching
3039 trackviews. nothing to do.
3045 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3047 RegionView* rv2 = (*i);
3049 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3051 double ix1, ix2, iy1, iy2;
3052 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3053 rv2->get_canvas_group()->i2w (ix1, iy1);
3062 /*************************************************************
3064 ************************************************************/
3068 if (drag_info.first_move) {
3069 if (drag_info.move_threshold_passed) {
3080 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3081 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3083 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3085 RegionView* rv = (*i);
3086 double ix1, ix2, iy1, iy2;
3087 int32_t temp_pointer_y_span = pointer_y_span;
3089 /* get item BBox, which will be relative to parent. so we have
3090 to query on a child, then convert to world coordinates using
3094 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3095 rv->get_canvas_group()->i2w (ix1, iy1);
3096 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3097 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3098 RouteTimeAxisView* temp_rtv;
3100 if ((pointer_y_span != 0) && !clamp_y_axis) {
3103 for (j = height_list.begin(); j!= height_list.end(); j++) {
3104 if (x == canvas_rtv->order) {
3105 /* we found the track the region is on */
3106 if (x != original_pointer_order) {
3107 /*this isn't from the same track we're dragging from */
3108 temp_pointer_y_span = canvas_pointer_y_span;
3110 while (temp_pointer_y_span > 0) {
3111 /* we're moving up canvas-wise,
3112 so we need to find the next track height
3114 if (j != height_list.begin()) {
3117 if (x != original_pointer_order) {
3118 /* we're not from the dragged track, so ignore hidden tracks. */
3120 temp_pointer_y_span++;
3124 temp_pointer_y_span--;
3126 while (temp_pointer_y_span < 0) {
3128 if (x != original_pointer_order) {
3130 temp_pointer_y_span--;
3134 if (j != height_list.end()) {
3137 temp_pointer_y_span++;
3139 /* find out where we'll be when we move and set height accordingly */
3141 tvp2 = trackview_by_y_position (iy1 + y_delta);
3142 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3143 rv->set_height (temp_rtv->height);
3145 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3146 personally, i think this can confuse things, but never mind.
3149 //const GdkColor& col (temp_rtv->view->get_region_color());
3150 //rv->set_color (const_cast<GdkColor&>(col));
3157 /* prevent the regionview from being moved to before
3158 the zero position on the canvas.
3163 if (-x_delta > ix1) {
3166 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3167 x_delta = max_frames - rv->region()->last_frame();
3170 if (drag_info.first_move) {
3172 /* hide any dependent views */
3174 rv->get_time_axis_view().hide_dependent_views (*rv);
3176 /* this is subtle. raising the regionview itself won't help,
3177 because raise_to_top() just puts the item on the top of
3178 its parent's stack. so, we need to put the trackview canvas_display group
3179 on the top, since its parent is the whole canvas.
3182 rv->get_canvas_group()->raise_to_top();
3183 rv->get_time_axis_view().canvas_display->raise_to_top();
3184 cursor_group->raise_to_top();
3186 rv->fake_set_opaque (true);
3189 if (drag_info.brushing) {
3190 mouse_brush_insert_region (rv, pending_region_position);
3192 rv->move (x_delta, y_delta);
3195 } /* foreach region */
3199 if (drag_info.first_move && drag_info.move_threshold_passed) {
3200 cursor_group->raise_to_top();
3201 drag_info.first_move = false;
3204 if (x_delta != 0 && !drag_info.brushing) {
3205 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3210 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3213 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3214 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3215 bool nocommit = true;
3217 RouteTimeAxisView* rtv;
3218 bool regionview_y_movement;
3219 bool regionview_x_movement;
3220 vector<RegionView*> copies;
3222 /* first_move is set to false if the regionview has been moved in the
3226 if (drag_info.first_move && !(drag_info.copy && drag_info.x_constrained)) {
3233 /* The regionview has been moved at some stage during the grab so we need
3234 to account for any mouse movement between this event and the last one.
3237 region_drag_motion_callback (item, event);
3239 if (drag_info.brushing) {
3240 /* all changes were made during motion event handlers */
3242 if (drag_info.copy) {
3243 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3244 copies.push_back (*i);
3251 /* adjust for track speed */
3254 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3255 if (rtv && rtv->get_diskstream()) {
3256 speed = rtv->get_diskstream()->speed();
3259 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3260 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3262 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3263 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3267 if (drag_info.copy) {
3268 if (drag_info.x_constrained) {
3269 op_string = _("fixed time region copy");
3271 op_string = _("region copy");
3274 if (drag_info.x_constrained) {
3275 op_string = _("fixed time region drag");
3277 op_string = _("region drag");
3281 begin_reversible_command (op_string);
3283 if (regionview_y_movement) {
3285 /* moved to a different audio track. */
3287 vector<RegionView*> new_selection;
3289 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3291 RegionView* rv = (*i);
3293 double ix1, ix2, iy1, iy2;
3295 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3296 rv->get_canvas_group()->i2w (ix1, iy1);
3297 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3298 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3300 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3301 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3303 where = (nframes_t) (unit_to_frame (ix1) * speed);
3304 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3306 /* undo the previous hide_dependent_views so that xfades don't
3307 disappear on copying regions
3310 rv->get_time_axis_view().reveal_dependent_views (*rv);
3312 if (!drag_info.copy) {
3314 /* the region that used to be in the old playlist is not
3315 moved to the new one - we make a copy of it. as a result,
3316 any existing editor for the region should no longer be
3320 rv->hide_region_editor();
3321 rv->fake_set_opaque (false);
3323 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3324 from_playlist->remove_region ((rv->region()));
3325 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3329 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3331 copies.push_back (rv);
3334 latest_regionview = 0;
3336 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3337 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3338 to_playlist->add_region (new_region, where);
3339 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3342 if (latest_regionview) {
3343 new_selection.push_back (latest_regionview);
3346 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3347 was selected in all of them, then removing it from the playlist will have removed all
3348 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3349 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3350 corresponding regionview, and the selection is now empty).
3352 this could have invalidated any and all iterators into the region selection.
3354 the heuristic we use here is: if the region selection is empty, break out of the loop
3355 here. if the region selection is not empty, then restart the loop because we know that
3356 we must have removed at least the region(view) we've just been working on as well as any
3357 that we processed on previous iterations.
3359 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3360 we can just iterate.
3363 if (drag_info.copy) {
3366 if (selection->regions.empty()) {
3369 i = selection->regions.by_layer().begin();
3374 selection->set (new_selection);
3378 /* motion within a single track */
3380 list<RegionView*> regions = selection->regions.by_layer();
3382 if (drag_info.copy) {
3383 selection->clear_regions();
3386 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3390 if (!rv->region()->can_move()) {
3394 if (regionview_x_movement) {
3395 double ownspeed = 1.0;
3396 rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3398 if (rtv && rtv->get_diskstream()) {
3399 ownspeed = rtv->get_diskstream()->speed();
3402 /* base the new region position on the current position of the regionview.*/
3404 double ix1, ix2, iy1, iy2;
3406 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3407 rv->get_canvas_group()->i2w (ix1, iy1);
3408 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3412 where = rv->region()->position();
3415 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3417 assert (to_playlist);
3421 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3423 if (drag_info.copy) {
3425 boost::shared_ptr<Region> newregion;
3426 boost::shared_ptr<Region> ar;
3428 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3429 newregion = RegionFactory::create (ar);
3431 /* XXX MIDI HERE drobilla */
3437 latest_regionview = 0;
3438 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3439 to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
3442 if (latest_regionview) {
3443 rtv->reveal_dependent_views (*latest_regionview);
3444 selection->add (latest_regionview);
3449 /* just change the model */
3451 rv->region()->set_position (where, (void*) this);
3457 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3459 if (drag_info.copy) {
3460 copies.push_back (rv);
3468 commit_reversible_command ();
3471 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3477 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3479 /* Either add to or set the set the region selection, unless
3480 this is an alignment click (control used)
3483 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3484 TimeAxisView* tv = &rv.get_time_axis_view();
3485 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3487 if (rtv && rtv->is_track()) {
3488 speed = rtv->get_diskstream()->speed();
3491 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3493 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3495 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3497 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3501 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3507 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3513 nframes_t frame_rate;
3520 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3521 case AudioClock::BBT:
3522 session->bbt_time (frame, bbt);
3523 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3526 case AudioClock::SMPTE:
3527 session->smpte_time (frame, smpte);
3528 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3531 case AudioClock::MinSec:
3532 /* XXX this is copied from show_verbose_duration_cursor() */
3533 frame_rate = session->frame_rate();
3534 hours = frame / (frame_rate * 3600);
3535 frame = frame % (frame_rate * 3600);
3536 mins = frame / (frame_rate * 60);
3537 frame = frame % (frame_rate * 60);
3538 secs = (float) frame / (float) frame_rate;
3539 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3543 snprintf (buf, sizeof(buf), "%u", frame);
3547 if (xpos >= 0 && ypos >=0) {
3548 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3551 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3553 show_verbose_canvas_cursor ();
3557 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3564 nframes_t distance, frame_rate;
3566 Meter meter_at_start(session->tempo_map().meter_at(start));
3572 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3573 case AudioClock::BBT:
3574 session->bbt_time (start, sbbt);
3575 session->bbt_time (end, ebbt);
3578 /* XXX this computation won't work well if the
3579 user makes a selection that spans any meter changes.
3582 ebbt.bars -= sbbt.bars;
3583 if (ebbt.beats >= sbbt.beats) {
3584 ebbt.beats -= sbbt.beats;
3587 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3589 if (ebbt.ticks >= sbbt.ticks) {
3590 ebbt.ticks -= sbbt.ticks;
3593 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3596 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3599 case AudioClock::SMPTE:
3600 session->smpte_duration (end - start, smpte);
3601 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3604 case AudioClock::MinSec:
3605 /* XXX this stuff should be elsewhere.. */
3606 distance = end - start;
3607 frame_rate = session->frame_rate();
3608 hours = distance / (frame_rate * 3600);
3609 distance = distance % (frame_rate * 3600);
3610 mins = distance / (frame_rate * 60);
3611 distance = distance % (frame_rate * 60);
3612 secs = (float) distance / (float) frame_rate;
3613 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3617 snprintf (buf, sizeof(buf), "%u", end - start);
3621 if (xpos >= 0 && ypos >=0) {
3622 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3625 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3627 show_verbose_canvas_cursor ();
3631 Editor::collect_new_region_view (RegionView* rv)
3633 latest_regionview = rv;
3637 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3639 if (clicked_regionview == 0) {
3643 /* lets try to create new Region for the selection */
3645 vector<boost::shared_ptr<AudioRegion> > new_regions;
3646 create_region_from_selection (new_regions);
3648 if (new_regions.empty()) {
3652 /* XXX fix me one day to use all new regions */
3654 boost::shared_ptr<Region> region (new_regions.front());
3656 /* add it to the current stream/playlist.
3658 tricky: the streamview for the track will add a new regionview. we will
3659 catch the signal it sends when it creates the regionview to
3660 set the regionview we want to then drag.
3663 latest_regionview = 0;
3664 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3666 /* A selection grab currently creates two undo/redo operations, one for
3667 creating the new region and another for moving it.
3670 begin_reversible_command (_("selection grab"));
3672 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3674 XMLNode *before = &(playlist->get_state());
3675 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3676 XMLNode *after = &(playlist->get_state());
3677 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3679 commit_reversible_command ();
3683 if (latest_regionview == 0) {
3684 /* something went wrong */
3688 /* we need to deselect all other regionviews, and select this one
3689 i'm ignoring undo stuff, because the region creation will take care of it */
3690 selection->set (latest_regionview);
3692 drag_info.item = latest_regionview->get_canvas_group();
3693 drag_info.data = latest_regionview;
3694 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3695 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3699 drag_info.last_trackview = clicked_axisview;
3700 drag_info.last_frame_position = latest_regionview->region()->position();
3701 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3703 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3707 Editor::cancel_selection ()
3709 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3710 (*i)->hide_selection ();
3712 begin_reversible_command (_("cancel selection"));
3713 selection->clear ();
3714 clicked_selection = 0;
3715 commit_reversible_command ();
3719 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3721 nframes_t start = 0;
3728 drag_info.item = item;
3729 drag_info.motion_callback = &Editor::drag_selection;
3730 drag_info.finished_callback = &Editor::end_selection_op;
3735 case CreateSelection:
3736 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3737 drag_info.copy = true;
3739 drag_info.copy = false;
3741 start_grab (event, selector_cursor);
3744 case SelectionStartTrim:
3745 if (clicked_axisview) {
3746 clicked_axisview->order_selection_trims (item, true);
3748 start_grab (event, trimmer_cursor);
3749 start = selection->time[clicked_selection].start;
3750 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3753 case SelectionEndTrim:
3754 if (clicked_axisview) {
3755 clicked_axisview->order_selection_trims (item, false);
3757 start_grab (event, trimmer_cursor);
3758 end = selection->time[clicked_selection].end;
3759 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3763 start = selection->time[clicked_selection].start;
3765 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3769 if (selection_op == SelectionMove) {
3770 show_verbose_time_cursor(start, 10);
3772 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3777 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3779 nframes_t start = 0;
3782 nframes_t pending_position;
3784 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3785 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3788 pending_position = 0;
3791 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3792 snap_to (pending_position);
3795 /* only alter selection if the current frame is
3796 different from the last frame position (adjusted)
3799 if (pending_position == drag_info.last_pointer_frame) return;
3801 switch (selection_op) {
3802 case CreateSelection:
3804 if (drag_info.first_move) {
3805 snap_to (drag_info.grab_frame);
3808 if (pending_position < drag_info.grab_frame) {
3809 start = pending_position;
3810 end = drag_info.grab_frame;
3812 end = pending_position;
3813 start = drag_info.grab_frame;
3816 /* first drag: Either add to the selection
3817 or create a new selection->
3820 if (drag_info.first_move) {
3822 begin_reversible_command (_("range selection"));
3824 if (drag_info.copy) {
3825 /* adding to the selection */
3826 clicked_selection = selection->add (start, end);
3827 drag_info.copy = false;
3829 /* new selection-> */
3830 clicked_selection = selection->set (clicked_axisview, start, end);
3835 case SelectionStartTrim:
3837 if (drag_info.first_move) {
3838 begin_reversible_command (_("trim selection start"));
3841 start = selection->time[clicked_selection].start;
3842 end = selection->time[clicked_selection].end;
3844 if (pending_position > end) {
3847 start = pending_position;
3851 case SelectionEndTrim:
3853 if (drag_info.first_move) {
3854 begin_reversible_command (_("trim selection end"));
3857 start = selection->time[clicked_selection].start;
3858 end = selection->time[clicked_selection].end;
3860 if (pending_position < start) {
3863 end = pending_position;
3870 if (drag_info.first_move) {
3871 begin_reversible_command (_("move selection"));
3874 start = selection->time[clicked_selection].start;
3875 end = selection->time[clicked_selection].end;
3877 length = end - start;
3879 start = pending_position;
3882 end = start + length;
3887 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3888 start_canvas_autoscroll (1);
3892 selection->replace (clicked_selection, start, end);
3895 drag_info.last_pointer_frame = pending_position;
3896 drag_info.first_move = false;
3898 if (selection_op == SelectionMove) {
3899 show_verbose_time_cursor(start, 10);
3901 show_verbose_time_cursor(pending_position, 10);
3906 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3908 if (!drag_info.first_move) {
3909 drag_selection (item, event);
3910 /* XXX this is not object-oriented programming at all. ick */
3911 if (selection->time.consolidate()) {
3912 selection->TimeChanged ();
3914 commit_reversible_command ();
3916 /* just a click, no pointer movement.*/
3918 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3920 selection->clear_time();
3925 /* XXX what happens if its a music selection? */
3926 session->set_audio_range (selection->time);
3927 stop_canvas_autoscroll ();
3931 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3934 TimeAxisView* tvp = clicked_axisview;
3935 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3937 if (tv && tv->is_track()) {
3938 speed = tv->get_diskstream()->speed();
3941 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3942 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3943 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3945 //drag_info.item = clicked_regionview->get_name_highlight();
3946 drag_info.item = item;
3947 drag_info.motion_callback = &Editor::trim_motion_callback;
3948 drag_info.finished_callback = &Editor::trim_finished_callback;
3950 start_grab (event, trimmer_cursor);
3952 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3953 trim_op = ContentsTrim;
3955 /* These will get overridden for a point trim.*/
3956 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3957 /* closer to start */
3958 trim_op = StartTrim;
3959 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3967 show_verbose_time_cursor(region_start, 10);
3970 show_verbose_time_cursor(region_end, 10);
3973 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3979 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3981 RegionView* rv = clicked_regionview;
3982 nframes_t frame_delta = 0;
3983 bool left_direction;
3984 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3986 /* snap modifier works differently here..
3987 its' current state has to be passed to the
3988 various trim functions in order to work properly
3992 TimeAxisView* tvp = clicked_axisview;
3993 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3994 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3996 if (tv && tv->is_track()) {
3997 speed = tv->get_diskstream()->speed();
4000 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4001 left_direction = true;
4003 left_direction = false;
4007 snap_to (drag_info.current_pointer_frame);
4010 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4014 if (drag_info.first_move) {
4020 trim_type = "Region start trim";
4023 trim_type = "Region end trim";
4026 trim_type = "Region content trim";
4030 begin_reversible_command (trim_type);
4032 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4033 (*i)->fake_set_opaque(false);
4034 (*i)->region()->freeze ();
4036 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4038 arv->temporarily_hide_envelope ();
4040 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4041 insert_result = motion_frozen_playlists.insert (pl);
4042 if (insert_result.second) {
4043 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4048 if (left_direction) {
4049 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4051 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4056 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4059 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4060 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4066 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4069 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4070 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4077 bool swap_direction = false;
4079 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4080 swap_direction = true;
4083 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4084 i != selection->regions.by_layer().end(); ++i)
4086 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4094 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4097 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4100 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4104 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4105 drag_info.first_move = false;
4109 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4111 boost::shared_ptr<Region> region (rv.region());
4113 if (region->locked()) {
4117 nframes_t new_bound;
4120 TimeAxisView* tvp = clicked_axisview;
4121 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4123 if (tv && tv->is_track()) {
4124 speed = tv->get_diskstream()->speed();
4127 if (left_direction) {
4128 if (swap_direction) {
4129 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4131 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4134 if (swap_direction) {
4135 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4137 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4142 snap_to (new_bound);
4144 region->trim_start ((nframes_t) (new_bound * speed), this);
4145 rv.region_changed (StartChanged);
4149 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4151 boost::shared_ptr<Region> region (rv.region());
4153 if (region->locked()) {
4157 nframes_t new_bound;
4160 TimeAxisView* tvp = clicked_axisview;
4161 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4163 if (tv && tv->is_track()) {
4164 speed = tv->get_diskstream()->speed();
4167 if (left_direction) {
4168 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4170 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4174 snap_to (new_bound, (left_direction ? 0 : 1));
4177 region->trim_front ((nframes_t) (new_bound * speed), this);
4179 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4183 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4185 boost::shared_ptr<Region> region (rv.region());
4187 if (region->locked()) {
4191 nframes_t new_bound;
4194 TimeAxisView* tvp = clicked_axisview;
4195 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4197 if (tv && tv->is_track()) {
4198 speed = tv->get_diskstream()->speed();
4201 if (left_direction) {
4202 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4204 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4208 snap_to (new_bound);
4210 region->trim_end ((nframes_t) (new_bound * speed), this);
4211 rv.region_changed (LengthChanged);
4215 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4217 if (!drag_info.first_move) {
4218 trim_motion_callback (item, event);
4220 if (!clicked_regionview->get_selected()) {
4221 thaw_region_after_trim (*clicked_regionview);
4224 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4225 i != selection->regions.by_layer().end(); ++i)
4227 thaw_region_after_trim (**i);
4228 (*i)->fake_set_opaque (true);
4232 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4234 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4237 motion_frozen_playlists.clear ();
4239 commit_reversible_command();
4241 /* no mouse movement */
4247 Editor::point_trim (GdkEvent* event)
4249 RegionView* rv = clicked_regionview;
4250 nframes_t new_bound = drag_info.current_pointer_frame;
4252 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4253 snap_to (new_bound);
4256 /* Choose action dependant on which button was pressed */
4257 switch (event->button.button) {
4259 trim_op = StartTrim;
4260 begin_reversible_command (_("Start point trim"));
4262 if (rv->get_selected()) {
4264 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4265 i != selection->regions.by_layer().end(); ++i)
4267 if (!(*i)->region()->locked()) {
4268 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4269 XMLNode &before = pl->get_state();
4270 (*i)->region()->trim_front (new_bound, this);
4271 XMLNode &after = pl->get_state();
4272 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4278 if (!rv->region()->locked()) {
4279 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4280 XMLNode &before = pl->get_state();
4281 rv->region()->trim_front (new_bound, this);
4282 XMLNode &after = pl->get_state();
4283 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4287 commit_reversible_command();
4292 begin_reversible_command (_("End point trim"));
4294 if (rv->get_selected()) {
4296 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4298 if (!(*i)->region()->locked()) {
4299 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4300 XMLNode &before = pl->get_state();
4301 (*i)->region()->trim_end (new_bound, this);
4302 XMLNode &after = pl->get_state();
4303 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4309 if (!rv->region()->locked()) {
4310 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4311 XMLNode &before = pl->get_state();
4312 rv->region()->trim_end (new_bound, this);
4313 XMLNode &after = pl->get_state();
4314 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4318 commit_reversible_command();
4327 Editor::thaw_region_after_trim (RegionView& rv)
4329 boost::shared_ptr<Region> region (rv.region());
4331 if (region->locked()) {
4335 region->thaw (_("trimmed region"));
4336 XMLNode &after = region->playlist()->get_state();
4337 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4339 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4341 arv->unhide_envelope ();
4345 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4350 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4351 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4355 Location* location = find_location_from_marker (marker, is_start);
4356 location->set_hidden (true, this);
4361 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4367 drag_info.item = item;
4368 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4369 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4371 range_marker_op = op;
4373 if (!temp_location) {
4374 temp_location = new Location;
4378 case CreateRangeMarker:
4379 case CreateTransportMarker:
4381 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4382 drag_info.copy = true;
4384 drag_info.copy = false;
4386 start_grab (event, selector_cursor);
4390 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4395 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4397 nframes_t start = 0;
4399 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4401 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4402 snap_to (drag_info.current_pointer_frame);
4405 /* only alter selection if the current frame is
4406 different from the last frame position.
4409 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4411 switch (range_marker_op) {
4412 case CreateRangeMarker:
4413 case CreateTransportMarker:
4414 if (drag_info.first_move) {
4415 snap_to (drag_info.grab_frame);
4418 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4419 start = drag_info.current_pointer_frame;
4420 end = drag_info.grab_frame;
4422 end = drag_info.current_pointer_frame;
4423 start = drag_info.grab_frame;
4426 /* first drag: Either add to the selection
4427 or create a new selection.
4430 if (drag_info.first_move) {
4432 temp_location->set (start, end);
4436 update_marker_drag_item (temp_location);
4437 range_marker_drag_rect->show();
4438 range_marker_drag_rect->raise_to_top();
4444 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4445 start_canvas_autoscroll (1);
4449 temp_location->set (start, end);
4451 double x1 = frame_to_pixel (start);
4452 double x2 = frame_to_pixel (end);
4453 crect->property_x1() = x1;
4454 crect->property_x2() = x2;
4456 update_marker_drag_item (temp_location);
4459 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4460 drag_info.first_move = false;
4462 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4467 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4469 Location * newloc = 0;
4472 if (!drag_info.first_move) {
4473 drag_range_markerbar_op (item, event);
4475 switch (range_marker_op) {
4476 case CreateRangeMarker:
4478 begin_reversible_command (_("new range marker"));
4479 XMLNode &before = session->locations()->get_state();
4480 session->locations()->next_available_name(rangename,"unnamed");
4481 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4482 session->locations()->add (newloc, true);
4483 XMLNode &after = session->locations()->get_state();
4484 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4485 commit_reversible_command ();
4487 range_bar_drag_rect->hide();
4488 range_marker_drag_rect->hide();
4492 case CreateTransportMarker:
4493 // popup menu to pick loop or punch
4494 new_transport_marker_context_menu (&event->button, item);
4499 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4501 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4506 start = session->locations()->first_mark_before (drag_info.grab_frame);
4507 end = session->locations()->first_mark_after (drag_info.grab_frame);
4509 if (end == max_frames) {
4510 end = session->current_end_frame ();
4514 start = session->current_start_frame ();
4517 switch (mouse_mode) {
4519 /* find the two markers on either side and then make the selection from it */
4520 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4524 /* find the two markers on either side of the click and make the range out of it */
4525 selection->set (0, start, end);
4534 stop_canvas_autoscroll ();
4540 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4542 drag_info.item = item;
4543 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4544 drag_info.finished_callback = &Editor::end_mouse_zoom;
4546 start_grab (event, zoom_cursor);
4548 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4552 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4557 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4558 snap_to (drag_info.current_pointer_frame);
4560 if (drag_info.first_move) {
4561 snap_to (drag_info.grab_frame);
4565 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4567 /* base start and end on initial click position */
4568 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4569 start = drag_info.current_pointer_frame;
4570 end = drag_info.grab_frame;
4572 end = drag_info.current_pointer_frame;
4573 start = drag_info.grab_frame;
4578 if (drag_info.first_move) {
4580 zoom_rect->raise_to_top();
4583 reposition_zoom_rect(start, end);
4585 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4586 drag_info.first_move = false;
4588 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4593 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4595 if (!drag_info.first_move) {
4596 drag_mouse_zoom (item, event);
4598 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4599 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4601 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4604 temporal_zoom_to_frame (false, drag_info.grab_frame);
4606 temporal_zoom_step (false);
4607 center_screen (drag_info.grab_frame);
4615 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4617 double x1 = frame_to_pixel (start);
4618 double x2 = frame_to_pixel (end);
4619 double y2 = full_canvas_height - 1.0;
4621 zoom_rect->property_x1() = x1;
4622 zoom_rect->property_y1() = 1.0;
4623 zoom_rect->property_x2() = x2;
4624 zoom_rect->property_y2() = y2;
4628 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4630 drag_info.item = item;
4631 drag_info.motion_callback = &Editor::drag_rubberband_select;
4632 drag_info.finished_callback = &Editor::end_rubberband_select;
4634 start_grab (event, cross_hair_cursor);
4636 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4640 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4647 /* use a bigger drag threshold than the default */
4649 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4653 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4654 if (drag_info.first_move) {
4655 snap_to (drag_info.grab_frame);
4657 snap_to (drag_info.current_pointer_frame);
4660 /* base start and end on initial click position */
4662 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4663 start = drag_info.current_pointer_frame;
4664 end = drag_info.grab_frame;
4666 end = drag_info.current_pointer_frame;
4667 start = drag_info.grab_frame;
4670 if (drag_info.current_pointer_y < drag_info.grab_y) {
4671 y1 = drag_info.current_pointer_y;
4672 y2 = drag_info.grab_y;
4674 y2 = drag_info.current_pointer_y;
4675 y1 = drag_info.grab_y;
4679 if (start != end || y1 != y2) {
4681 double x1 = frame_to_pixel (start);
4682 double x2 = frame_to_pixel (end);
4684 rubberband_rect->property_x1() = x1;
4685 rubberband_rect->property_y1() = y1;
4686 rubberband_rect->property_x2() = x2;
4687 rubberband_rect->property_y2() = y2;
4689 rubberband_rect->show();
4690 rubberband_rect->raise_to_top();
4692 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4693 drag_info.first_move = false;
4695 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4700 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4702 if (!drag_info.first_move) {
4704 drag_rubberband_select (item, event);
4707 if (drag_info.current_pointer_y < drag_info.grab_y) {
4708 y1 = drag_info.current_pointer_y;
4709 y2 = drag_info.grab_y;
4712 y2 = drag_info.current_pointer_y;
4713 y1 = drag_info.grab_y;
4717 Selection::Operation op = Keyboard::selection_type (event->button.state);
4720 begin_reversible_command (_("rubberband selection"));
4722 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4723 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4725 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4729 commit_reversible_command ();
4733 selection->clear_tracks();
4734 selection->clear_regions();
4735 selection->clear_points ();
4736 selection->clear_lines ();
4739 rubberband_rect->hide();
4744 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4746 using namespace Gtkmm2ext;
4748 ArdourPrompter prompter (false);
4750 prompter.set_prompt (_("Name for region:"));
4751 prompter.set_initial_text (clicked_regionview->region()->name());
4752 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4753 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4754 prompter.show_all ();
4755 switch (prompter.run ()) {
4756 case Gtk::RESPONSE_ACCEPT:
4758 prompter.get_result(str);
4760 clicked_regionview->region()->set_name (str);
4768 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4770 drag_info.item = item;
4771 drag_info.motion_callback = &Editor::time_fx_motion;
4772 drag_info.finished_callback = &Editor::end_time_fx;
4776 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4780 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4782 RegionView* rv = clicked_regionview;
4784 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4785 snap_to (drag_info.current_pointer_frame);
4788 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4792 if (drag_info.current_pointer_frame > rv->region()->position()) {
4793 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4796 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4797 drag_info.first_move = false;
4799 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4803 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4805 clicked_regionview->get_time_axis_view().hide_timestretch ();
4807 if (drag_info.first_move) {
4811 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4812 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4814 begin_reversible_command (_("timestretch"));
4816 if (run_timestretch (selection->regions, percentage) == 0) {
4817 session->commit_reversible_command ();
4822 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4824 /* no brushing without a useful snap setting */
4827 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4830 switch (snap_mode) {
4832 return; /* can't work because it allows region to be placed anywhere */
4837 switch (snap_type) {
4840 case SnapToEditCursor:
4847 /* don't brush a copy over the original */
4849 if (pos == rv->region()->position()) {
4853 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4855 if (rtv == 0 || !rtv->is_track()) {
4859 boost::shared_ptr<Playlist> playlist = rtv->playlist();
4860 double speed = rtv->get_diskstream()->speed();
4862 XMLNode &before = playlist->get_state();
4863 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4864 XMLNode &after = playlist->get_state();
4865 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4867 // playlist is frozen, so we have to update manually
4869 playlist->Modified(); /* EMIT SIGNAL */
4873 Editor::track_height_step_timeout ()
4876 struct timeval delta;
4878 gettimeofday (&now, 0);
4879 timersub (&now, &last_track_height_step_timestamp, &delta);
4881 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4882 current_stepping_trackview = 0;