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"
42 #include "control_point.h"
45 #include "selection.h"
48 #include "rgb_macros.h"
50 #include <ardour/types.h>
51 #include <ardour/profile.h>
52 #include <ardour/route.h>
53 #include <ardour/audio_track.h>
54 #include <ardour/audio_diskstream.h>
55 #include <ardour/playlist.h>
56 #include <ardour/audioplaylist.h>
57 #include <ardour/audioregion.h>
58 #include <ardour/midi_region.h>
59 #include <ardour/dB.h>
60 #include <ardour/utils.h>
61 #include <ardour/region_factory.h>
68 using namespace ARDOUR;
72 using namespace Editing;
75 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
89 switch (event->type) {
90 case GDK_BUTTON_RELEASE:
91 case GDK_BUTTON_PRESS:
92 case GDK_2BUTTON_PRESS:
93 case GDK_3BUTTON_PRESS:
94 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
96 case GDK_MOTION_NOTIFY:
97 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
99 case GDK_ENTER_NOTIFY:
100 case GDK_LEAVE_NOTIFY:
101 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
104 case GDK_KEY_RELEASE:
105 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
108 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
112 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
113 position is negative (as can be the case with motion events in particular),
114 the frame location is always positive.
117 return pixel_to_frame (*pcx);
121 Editor::mouse_mode_toggled (MouseMode m)
123 if (ignore_mouse_mode_toggle) {
129 if (mouse_select_button.get_active()) {
135 if (mouse_move_button.get_active()) {
141 if (mouse_gain_button.get_active()) {
147 if (mouse_zoom_button.get_active()) {
153 if (mouse_timefx_button.get_active()) {
159 if (mouse_audition_button.get_active()) {
165 if (mouse_note_button.get_active()) {
176 Editor::set_mouse_mode (MouseMode m, bool force)
178 if (drag_info.item) {
182 if (!force && m == mouse_mode) {
190 if (mouse_mode != MouseRange) {
192 /* in all modes except range, hide the range selection,
193 show the object (region) selection.
196 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
197 (*i)->set_should_show_selection (true);
199 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
200 (*i)->hide_selection ();
206 in range mode,show the range selection.
209 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
210 if ((*i)->get_selected()) {
211 (*i)->show_selection (selection->time);
216 /* XXX the hack of unsetting all other buttons should go
217 away once GTK2 allows us to use regular radio buttons drawn like
218 normal buttons, rather than my silly GroupedButton hack.
221 ignore_mouse_mode_toggle = true;
223 switch (mouse_mode) {
225 mouse_select_button.set_active (true);
226 current_canvas_cursor = selector_cursor;
230 mouse_move_button.set_active (true);
231 current_canvas_cursor = grabber_cursor;
235 mouse_gain_button.set_active (true);
236 current_canvas_cursor = cross_hair_cursor;
240 mouse_zoom_button.set_active (true);
241 current_canvas_cursor = zoom_cursor;
245 mouse_timefx_button.set_active (true);
246 current_canvas_cursor = time_fx_cursor; // just use playhead
250 mouse_audition_button.set_active (true);
251 current_canvas_cursor = speaker_cursor;
255 mouse_note_button.set_active (true);
256 set_midi_edit_cursor (current_midi_edit_mode());
260 if (mouse_mode == MouseNote)
261 midi_toolbar_frame.show();
263 midi_toolbar_frame.hide();
265 ignore_mouse_mode_toggle = false;
268 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
273 Editor::step_mouse_mode (bool next)
275 switch (current_mouse_mode()) {
277 if (next) set_mouse_mode (MouseRange);
278 else set_mouse_mode (MouseTimeFX);
282 if (next) set_mouse_mode (MouseZoom);
283 else set_mouse_mode (MouseObject);
287 if (next) set_mouse_mode (MouseGain);
288 else set_mouse_mode (MouseRange);
292 if (next) set_mouse_mode (MouseTimeFX);
293 else set_mouse_mode (MouseZoom);
297 if (next) set_mouse_mode (MouseAudition);
298 else set_mouse_mode (MouseGain);
302 if (next) set_mouse_mode (MouseObject);
303 else set_mouse_mode (MouseTimeFX);
307 if (next) set_mouse_mode (MouseObject);
308 else set_mouse_mode (MouseAudition);
314 Editor::midi_edit_mode_toggled (MidiEditMode m)
316 if (ignore_midi_edit_mode_toggle) {
322 if (midi_tool_select_button.get_active()) {
323 set_midi_edit_mode (m);
328 if (midi_tool_pencil_button.get_active()) {
329 set_midi_edit_mode (m);
334 if (midi_tool_erase_button.get_active()) {
335 set_midi_edit_mode (m);
343 set_midi_edit_cursor(m);
348 Editor::set_midi_edit_mode (MidiEditMode m, bool force)
350 if (drag_info.item) {
354 if (!force && m == midi_edit_mode) {
362 ignore_midi_edit_mode_toggle = true;
364 switch (midi_edit_mode) {
366 midi_tool_select_button.set_active (true);
370 midi_tool_pencil_button.set_active (true);
374 midi_tool_erase_button.set_active (true);
378 if (mouse_mode == MouseNote)
379 midi_toolbar_frame.show();
381 midi_toolbar_frame.hide();
383 ignore_midi_edit_mode_toggle = false;
385 set_midi_edit_cursor (current_midi_edit_mode());
388 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
393 Editor::set_midi_edit_cursor (MidiEditMode m)
395 switch (midi_edit_mode) {
397 current_canvas_cursor = midi_select_cursor;
400 current_canvas_cursor = midi_pencil_cursor;
403 current_canvas_cursor = midi_erase_cursor;
409 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
413 /* in object/audition/timefx mode, any button press sets
414 the selection if the object can be selected. this is a
415 bit of hack, because we want to avoid this if the
416 mouse operation is a region alignment.
418 note: not dbl-click or triple-click
421 if (((mouse_mode != MouseObject) &&
422 (mouse_mode != MouseAudition || item_type != RegionItem) &&
423 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
424 (mouse_mode != MouseRange)) ||
426 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
431 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
433 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
435 /* almost no selection action on modified button-2 or button-3 events */
437 if (item_type != RegionItem && event->button.button != 2) {
443 Selection::Operation op = Keyboard::selection_type (event->button.state);
444 bool press = (event->type == GDK_BUTTON_PRESS);
446 // begin_reversible_command (_("select on click"));
450 case RegionViewNameHighlight:
452 case FadeInHandleItem:
454 case FadeOutHandleItem:
456 if (mouse_mode != MouseRange) {
457 commit = set_selected_regionview_from_click (press, op, true);
458 } else if (event->type == GDK_BUTTON_PRESS) {
459 commit = set_selected_track_from_click (press, op, false);
463 case CrossfadeViewItem:
464 commit = set_selected_track_from_click (press, op, false);
467 case ControlPointItem:
468 commit = set_selected_track_from_click (press, op, true);
469 if (mouse_mode != MouseRange) {
470 commit |= set_selected_control_point_from_click (op, false);
475 /* for context click or range selection, select track */
476 if (event->button.button == 3) {
477 commit = set_selected_track_from_click (press, op, true);
478 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
479 commit = set_selected_track_from_click (press, op, false);
483 case AutomationTrackItem:
484 commit = set_selected_track_from_click (press, op, true);
492 // commit_reversible_command ();
497 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
499 track_canvas.grab_focus();
501 if (session && session->actively_recording()) {
505 button_selection (item, event, item_type);
507 if (drag_info.item == 0 &&
508 (Keyboard::is_delete_event (&event->button) ||
509 Keyboard::is_context_menu_event (&event->button) ||
510 Keyboard::is_edit_event (&event->button))) {
512 /* handled by button release */
516 switch (event->button.button) {
519 if (event->type == GDK_BUTTON_PRESS) {
521 if (drag_info.item) {
522 drag_info.item->ungrab (event->button.time);
525 /* single mouse clicks on any of these item types operate
526 independent of mouse mode, mostly because they are
527 not on the main track canvas or because we want
533 case PlayheadCursorItem:
534 start_cursor_grab (item, event);
538 if (Keyboard::modifier_state_equals (event->button.state,
539 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
540 hide_marker (item, event);
542 start_marker_grab (item, event);
546 case TempoMarkerItem:
547 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
548 start_tempo_marker_copy_grab (item, event);
550 start_tempo_marker_grab (item, event);
554 case MeterMarkerItem:
555 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
556 start_meter_marker_copy_grab (item, event);
558 start_meter_marker_grab (item, event);
568 case RangeMarkerBarItem:
569 start_range_markerbar_op (item, event, CreateRangeMarker);
573 case TransportMarkerBarItem:
574 start_range_markerbar_op (item, event, CreateTransportMarker);
583 switch (mouse_mode) {
586 case StartSelectionTrimItem:
587 start_selection_op (item, event, SelectionStartTrim);
590 case EndSelectionTrimItem:
591 start_selection_op (item, event, SelectionEndTrim);
595 if (Keyboard::modifier_state_contains
596 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
597 // contains and not equals because I can't use alt as a modifier alone.
598 start_selection_grab (item, event);
599 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
600 /* grab selection for moving */
601 start_selection_op (item, event, SelectionMove);
604 /* this was debated, but decided the more common action was to
605 make a new selection */
606 start_selection_op (item, event, CreateSelection);
611 start_selection_op (item, event, CreateSelection);
617 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
618 event->type == GDK_BUTTON_PRESS) {
620 start_rubberband_select (item, event);
622 } else if (event->type == GDK_BUTTON_PRESS) {
625 case FadeInHandleItem:
626 start_fade_in_grab (item, event);
629 case FadeOutHandleItem:
630 start_fade_out_grab (item, event);
634 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
635 start_region_copy_grab (item, event);
636 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
637 start_region_brush_grab (item, event);
639 start_region_grab (item, event);
643 case RegionViewNameHighlight:
644 start_trim (item, event);
649 /* rename happens on edit clicks */
650 start_trim (clicked_regionview->get_name_highlight(), event);
654 case ControlPointItem:
655 start_control_point_grab (item, event);
659 case AutomationLineItem:
660 start_line_grab_from_line (item, event);
665 case AutomationTrackItem:
666 start_rubberband_select (item, event);
670 case ImageFrameHandleStartItem:
671 imageframe_start_handle_op(item, event) ;
674 case ImageFrameHandleEndItem:
675 imageframe_end_handle_op(item, event) ;
678 case MarkerViewHandleStartItem:
679 markerview_item_start_handle_op(item, event) ;
682 case MarkerViewHandleEndItem:
683 markerview_item_end_handle_op(item, event) ;
687 start_markerview_grab(item, event) ;
690 start_imageframe_grab(item, event) ;
708 // start_line_grab_from_regionview (item, event);
712 start_line_grab_from_line (item, event);
715 case ControlPointItem:
716 start_control_point_grab (item, event);
727 case ControlPointItem:
728 start_control_point_grab (item, event);
731 case AutomationLineItem:
732 start_line_grab_from_line (item, event);
736 // XXX need automation mode to identify which
738 // start_line_grab_from_regionview (item, event);
748 if (event->type == GDK_BUTTON_PRESS) {
749 start_mouse_zoom (item, event);
756 if (item_type == RegionItem) {
757 start_time_fx (item, event);
762 /* handled in release */
771 switch (mouse_mode) {
773 if (event->type == GDK_BUTTON_PRESS) {
776 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
777 start_region_copy_grab (item, event);
779 start_region_grab (item, event);
783 case ControlPointItem:
784 start_control_point_grab (item, event);
795 case RegionViewNameHighlight:
796 start_trim (item, event);
801 start_trim (clicked_regionview->get_name_highlight(), event);
812 if (event->type == GDK_BUTTON_PRESS) {
813 /* relax till release */
820 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
821 temporal_zoom_session();
823 temporal_zoom_to_frame (true, event_frame(event));
846 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
848 nframes_t where = event_frame (event, 0, 0);
850 /* no action if we're recording */
852 if (session && session->actively_recording()) {
856 /* first, see if we're finishing a drag ... */
858 if (drag_info.item) {
859 if (end_grab (item, event)) {
860 /* grab dragged, so do nothing else */
865 button_selection (item, event, item_type);
867 /* edit events get handled here */
869 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
875 case TempoMarkerItem:
876 edit_tempo_marker (item);
879 case MeterMarkerItem:
880 edit_meter_marker (item);
884 if (clicked_regionview->name_active()) {
885 return mouse_rename_region (item, event);
895 /* context menu events get handled here */
897 if (Keyboard::is_context_menu_event (&event->button)) {
899 if (drag_info.item == 0) {
901 /* no matter which button pops up the context menu, tell the menu
902 widget to use button 1 to drive menu selection.
907 case FadeInHandleItem:
909 case FadeOutHandleItem:
910 popup_fade_context_menu (1, event->button.time, item, item_type);
915 case RegionViewNameHighlight:
918 case AutomationTrackItem:
919 case CrossfadeViewItem:
920 popup_track_context_menu (1, event->button.time, where);
924 case RangeMarkerBarItem:
925 case TransportMarkerBarItem:
928 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
932 marker_context_menu (&event->button, item);
935 case TempoMarkerItem:
936 tm_marker_context_menu (&event->button, item);
939 case MeterMarkerItem:
940 tm_marker_context_menu (&event->button, item);
945 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
947 case ImageFrameTimeAxisItem:
948 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
951 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
953 case MarkerTimeAxisItem:
954 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
966 /* delete events get handled here */
968 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
971 case TempoMarkerItem:
972 remove_tempo_marker (item);
975 case MeterMarkerItem:
976 remove_meter_marker (item);
980 remove_marker (*item, event);
984 if (mouse_mode == MouseObject) {
985 remove_clicked_region ();
989 case ControlPointItem:
990 if (mouse_mode == MouseGain) {
991 remove_gain_control_point (item, event);
993 remove_control_point (item, event);
1003 switch (event->button.button) {
1006 switch (item_type) {
1007 /* see comments in button_press_handler */
1008 case EditCursorItem:
1009 case PlayheadCursorItem:
1012 case AutomationLineItem:
1013 case StartSelectionTrimItem:
1014 case EndSelectionTrimItem:
1018 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1019 snap_to (where, 0, true);
1021 mouse_add_new_marker (where);
1025 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1028 mouse_add_new_tempo_event (where);
1032 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1040 switch (mouse_mode) {
1042 switch (item_type) {
1043 case AutomationTrackItem:
1044 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
1058 // Gain only makes sense for audio regions
1060 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1064 switch (item_type) {
1066 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1070 case AutomationTrackItem:
1071 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1072 add_automation_event (item, event, where, event->button.y);
1081 switch (item_type) {
1083 audition_selected_region ();
1100 switch (mouse_mode) {
1104 // x_style_paste (where, 1.0);
1124 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1130 switch (item_type) {
1131 case ControlPointItem:
1132 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1133 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1134 cp->set_visible (true);
1138 at_y = cp->get_y ();
1139 cp->item()->i2w (at_x, at_y);
1143 fraction = 1.0 - (cp->get_y() / cp->line().height());
1145 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1146 show_verbose_canvas_cursor ();
1148 if (is_drawable()) {
1149 track_canvas.get_window()->set_cursor (*fader_cursor);
1155 if (mouse_mode == MouseGain) {
1156 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1158 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1159 if (is_drawable()) {
1160 track_canvas.get_window()->set_cursor (*fader_cursor);
1165 case AutomationLineItem:
1166 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1168 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1170 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1172 if (is_drawable()) {
1173 track_canvas.get_window()->set_cursor (*fader_cursor);
1178 case RegionViewNameHighlight:
1179 if (is_drawable() && mouse_mode == MouseObject) {
1180 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1184 case StartSelectionTrimItem:
1185 case EndSelectionTrimItem:
1188 case ImageFrameHandleStartItem:
1189 case ImageFrameHandleEndItem:
1190 case MarkerViewHandleStartItem:
1191 case MarkerViewHandleEndItem:
1194 if (is_drawable()) {
1195 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1199 case EditCursorItem:
1200 case PlayheadCursorItem:
1201 if (is_drawable()) {
1202 track_canvas.get_window()->set_cursor (*grabber_cursor);
1206 case RegionViewName:
1208 /* when the name is not an active item, the entire name highlight is for trimming */
1210 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1211 if (mouse_mode == MouseObject && is_drawable()) {
1212 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1218 case AutomationTrackItem:
1219 if (is_drawable()) {
1220 Gdk::Cursor *cursor;
1221 switch (mouse_mode) {
1223 cursor = selector_cursor;
1226 cursor = zoom_cursor;
1229 cursor = cross_hair_cursor;
1233 track_canvas.get_window()->set_cursor (*cursor);
1235 AutomationTimeAxisView* atv;
1236 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1237 clear_entered_track = false;
1238 set_entered_track (atv);
1244 case RangeMarkerBarItem:
1245 case TransportMarkerBarItem:
1248 if (is_drawable()) {
1249 time_canvas.get_window()->set_cursor (*timebar_cursor);
1254 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1257 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1259 case MeterMarkerItem:
1260 case TempoMarkerItem:
1261 if (is_drawable()) {
1262 time_canvas.get_window()->set_cursor (*timebar_cursor);
1265 case FadeInHandleItem:
1266 case FadeOutHandleItem:
1267 if (mouse_mode == MouseObject) {
1268 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1270 rect->property_fill_color_rgba() = 0;
1271 rect->property_outline_pixels() = 1;
1280 /* second pass to handle entered track status in a comprehensible way.
1283 switch (item_type) {
1285 case AutomationLineItem:
1286 case ControlPointItem:
1287 /* these do not affect the current entered track state */
1288 clear_entered_track = false;
1291 case AutomationTrackItem:
1292 /* handled above already */
1296 set_entered_track (0);
1304 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1313 switch (item_type) {
1314 case ControlPointItem:
1315 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1316 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1317 if (cp->line().npoints() > 1 && !cp->selected()) {
1318 cp->set_visible (false);
1322 if (is_drawable()) {
1323 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1326 hide_verbose_canvas_cursor ();
1329 case RegionViewNameHighlight:
1330 case StartSelectionTrimItem:
1331 case EndSelectionTrimItem:
1332 case EditCursorItem:
1333 case PlayheadCursorItem:
1336 case ImageFrameHandleStartItem:
1337 case ImageFrameHandleEndItem:
1338 case MarkerViewHandleStartItem:
1339 case MarkerViewHandleEndItem:
1342 if (is_drawable()) {
1343 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1348 case AutomationLineItem:
1349 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1351 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1353 line->property_fill_color_rgba() = al->get_line_color();
1355 if (is_drawable()) {
1356 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1360 case RegionViewName:
1361 /* see enter_handler() for notes */
1362 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1363 if (is_drawable() && mouse_mode == MouseObject) {
1364 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1369 case RangeMarkerBarItem:
1370 case TransportMarkerBarItem:
1374 if (is_drawable()) {
1375 time_canvas.get_window()->set_cursor (*timebar_cursor);
1380 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1383 loc = find_location_from_marker (marker, is_start);
1384 if (loc) location_flags_changed (loc, this);
1386 case MeterMarkerItem:
1387 case TempoMarkerItem:
1389 if (is_drawable()) {
1390 time_canvas.get_window()->set_cursor (*timebar_cursor);
1395 case FadeInHandleItem:
1396 case FadeOutHandleItem:
1397 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1399 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1401 rect->property_fill_color_rgba() = rv->get_fill_color();
1402 rect->property_outline_pixels() = 0;
1407 case AutomationTrackItem:
1408 if (is_drawable()) {
1409 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1410 clear_entered_track = true;
1411 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1423 Editor::left_automation_track ()
1425 if (clear_entered_track) {
1426 set_entered_track (0);
1427 clear_entered_track = false;
1433 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1437 /* We call this so that MOTION_NOTIFY events continue to be
1438 delivered to the canvas. We need to do this because we set
1439 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1440 the density of the events, at the expense of a round-trip
1441 to the server. Given that this will mostly occur on cases
1442 where DISPLAY = :0.0, and given the cost of what the motion
1443 event might do, its a good tradeoff.
1446 track_canvas.get_pointer (x, y);
1448 if (current_stepping_trackview) {
1449 /* don't keep the persistent stepped trackview if the mouse moves */
1450 current_stepping_trackview = 0;
1451 step_timeout.disconnect ();
1454 if (session && session->actively_recording()) {
1455 /* Sorry. no dragging stuff around while we record */
1459 drag_info.item_type = item_type;
1460 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1461 &drag_info.current_pointer_y);
1463 if (!from_autoscroll && drag_info.item) {
1464 /* item != 0 is the best test i can think of for dragging.
1466 if (!drag_info.move_threshold_passed) {
1468 bool x_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1469 bool y_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1471 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1473 // and change the initial grab loc/frame if this drag info wants us to
1475 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1476 drag_info.grab_frame = drag_info.current_pointer_frame;
1477 drag_info.grab_x = drag_info.current_pointer_x;
1478 drag_info.grab_y = drag_info.current_pointer_y;
1479 drag_info.last_pointer_frame = drag_info.grab_frame;
1480 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1485 switch (item_type) {
1486 case PlayheadCursorItem:
1487 case EditCursorItem:
1489 case ControlPointItem:
1490 case TempoMarkerItem:
1491 case MeterMarkerItem:
1492 case RegionViewNameHighlight:
1493 case StartSelectionTrimItem:
1494 case EndSelectionTrimItem:
1497 case AutomationLineItem:
1498 case FadeInHandleItem:
1499 case FadeOutHandleItem:
1502 case ImageFrameHandleStartItem:
1503 case ImageFrameHandleEndItem:
1504 case MarkerViewHandleStartItem:
1505 case MarkerViewHandleEndItem:
1508 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1509 (event->motion.state & Gdk::BUTTON2_MASK))) {
1510 if (!from_autoscroll) {
1511 maybe_autoscroll (event);
1513 (this->*(drag_info.motion_callback)) (item, event);
1522 switch (mouse_mode) {
1527 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1528 (event->motion.state & GDK_BUTTON2_MASK))) {
1529 if (!from_autoscroll) {
1530 maybe_autoscroll (event);
1532 (this->*(drag_info.motion_callback)) (item, event);
1543 track_canvas_motion (event);
1544 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1552 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1554 if (drag_info.item == 0) {
1555 fatal << _("programming error: start_grab called without drag item") << endmsg;
1561 cursor = grabber_cursor;
1564 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1566 if (event->button.button == 2) {
1567 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1568 drag_info.y_constrained = true;
1569 drag_info.x_constrained = false;
1571 drag_info.y_constrained = false;
1572 drag_info.x_constrained = true;
1575 drag_info.x_constrained = false;
1576 drag_info.y_constrained = false;
1579 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1580 drag_info.last_pointer_frame = drag_info.grab_frame;
1581 drag_info.current_pointer_frame = drag_info.grab_frame;
1582 drag_info.current_pointer_x = drag_info.grab_x;
1583 drag_info.current_pointer_y = drag_info.grab_y;
1584 drag_info.cumulative_x_drag = 0;
1585 drag_info.cumulative_y_drag = 0;
1586 drag_info.first_move = true;
1587 drag_info.move_threshold_passed = false;
1588 drag_info.want_move_threshold = false;
1589 drag_info.pointer_frame_offset = 0;
1590 drag_info.brushing = false;
1591 drag_info.copied_location = 0;
1593 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1595 event->button.time);
1597 if (session && session->transport_rolling()) {
1598 drag_info.was_rolling = true;
1600 drag_info.was_rolling = false;
1603 switch (snap_type) {
1604 case SnapToRegionStart:
1605 case SnapToRegionEnd:
1606 case SnapToRegionSync:
1607 case SnapToRegionBoundary:
1608 build_region_boundary_cache ();
1616 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1618 drag_info.item->ungrab (0);
1619 drag_info.item = new_item;
1622 cursor = grabber_cursor;
1625 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1629 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1631 bool did_drag = false;
1633 stop_canvas_autoscroll ();
1635 if (drag_info.item == 0) {
1639 drag_info.item->ungrab (event->button.time);
1641 if (drag_info.finished_callback) {
1642 (this->*(drag_info.finished_callback)) (item, event);
1645 did_drag = !drag_info.first_move;
1647 hide_verbose_canvas_cursor();
1650 drag_info.copy = false;
1651 drag_info.motion_callback = 0;
1652 drag_info.finished_callback = 0;
1653 drag_info.last_trackview = 0;
1654 drag_info.last_frame_position = 0;
1655 drag_info.grab_frame = 0;
1656 drag_info.last_pointer_frame = 0;
1657 drag_info.current_pointer_frame = 0;
1658 drag_info.brushing = false;
1660 if (drag_info.copied_location) {
1661 delete drag_info.copied_location;
1662 drag_info.copied_location = 0;
1669 Editor::set_edit_cursor (GdkEvent* event)
1671 nframes_t pointer_frame = event_frame (event);
1673 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1674 if (snap_type != SnapToEditCursor) {
1675 snap_to (pointer_frame);
1679 edit_cursor->set_position (pointer_frame);
1680 edit_cursor_clock.set (pointer_frame);
1684 Editor::set_playhead_cursor (GdkEvent* event)
1686 nframes_t pointer_frame = event_frame (event);
1688 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1689 snap_to (pointer_frame);
1693 session->request_locate (pointer_frame, session->transport_rolling());
1698 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1700 drag_info.item = item;
1701 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1702 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1706 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1707 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1711 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1713 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
1717 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1719 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1721 nframes_t fade_length;
1723 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1724 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1730 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1734 if (pos < (arv->region()->position() + 64)) {
1735 fade_length = 64; // this should be a minimum defined somewhere
1736 } else if (pos > arv->region()->last_frame()) {
1737 fade_length = arv->region()->length();
1739 fade_length = pos - arv->region()->position();
1741 /* mapover the region selection */
1743 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1745 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1751 tmp->reset_fade_in_shape_width (fade_length);
1754 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1756 drag_info.first_move = false;
1760 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1762 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1764 nframes_t fade_length;
1766 if (drag_info.first_move) return;
1768 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1769 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1774 if (pos < (arv->region()->position() + 64)) {
1775 fade_length = 64; // this should be a minimum defined somewhere
1776 } else if (pos > arv->region()->last_frame()) {
1777 fade_length = arv->region()->length();
1779 fade_length = pos - arv->region()->position();
1782 begin_reversible_command (_("change fade in length"));
1784 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1786 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1792 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
1793 XMLNode &before = alist->get_state();
1795 tmp->audio_region()->set_fade_in_length (fade_length);
1797 XMLNode &after = alist->get_state();
1798 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1801 commit_reversible_command ();
1805 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1807 drag_info.item = item;
1808 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1809 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1813 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1814 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1818 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1820 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
1824 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1826 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1828 nframes_t fade_length;
1830 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1831 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1836 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1840 if (pos > (arv->region()->last_frame() - 64)) {
1841 fade_length = 64; // this should really be a minimum fade defined somewhere
1843 else if (pos < arv->region()->position()) {
1844 fade_length = arv->region()->length();
1847 fade_length = arv->region()->last_frame() - pos;
1850 /* mapover the region selection */
1852 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1854 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1860 tmp->reset_fade_out_shape_width (fade_length);
1863 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1865 drag_info.first_move = false;
1869 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1871 if (drag_info.first_move) return;
1873 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1875 nframes_t fade_length;
1877 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1878 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1884 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1888 if (pos > (arv->region()->last_frame() - 64)) {
1889 fade_length = 64; // this should really be a minimum fade defined somewhere
1891 else if (pos < arv->region()->position()) {
1892 fade_length = arv->region()->length();
1895 fade_length = arv->region()->last_frame() - pos;
1898 begin_reversible_command (_("change fade out length"));
1900 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1902 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1908 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
1909 XMLNode &before = alist->get_state();
1911 tmp->audio_region()->set_fade_out_length (fade_length);
1913 XMLNode &after = alist->get_state();
1914 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1917 commit_reversible_command ();
1921 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1923 drag_info.item = item;
1924 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1925 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1929 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1930 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1934 Cursor* cursor = (Cursor *) drag_info.data;
1936 if (cursor == playhead_cursor) {
1937 _dragging_playhead = true;
1939 if (session && drag_info.was_rolling) {
1940 session->request_stop ();
1943 if (session && session->is_auditioning()) {
1944 session->cancel_audition ();
1948 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1950 show_verbose_time_cursor (cursor->current_frame, 10);
1954 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1956 Cursor* cursor = (Cursor *) drag_info.data;
1957 nframes_t adjusted_frame;
1959 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1960 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1966 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1967 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1968 snap_to (adjusted_frame);
1972 if (adjusted_frame == drag_info.last_pointer_frame) return;
1974 cursor->set_position (adjusted_frame);
1976 if (cursor == edit_cursor) {
1977 edit_cursor_clock.set (cursor->current_frame);
1979 UpdateAllTransportClocks (cursor->current_frame);
1982 show_verbose_time_cursor (cursor->current_frame, 10);
1984 drag_info.last_pointer_frame = adjusted_frame;
1985 drag_info.first_move = false;
1989 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1991 if (drag_info.first_move) return;
1993 cursor_drag_motion_callback (item, event);
1995 _dragging_playhead = false;
1997 if (item == &playhead_cursor->canvas_item) {
1999 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2001 } else if (item == &edit_cursor->canvas_item) {
2002 edit_cursor->set_position (edit_cursor->current_frame);
2003 edit_cursor_clock.set (edit_cursor->current_frame);
2008 Editor::update_marker_drag_item (Location *location)
2010 double x1 = frame_to_pixel (location->start());
2011 double x2 = frame_to_pixel (location->end());
2013 if (location->is_mark()) {
2014 marker_drag_line_points.front().set_x(x1);
2015 marker_drag_line_points.back().set_x(x1);
2016 marker_drag_line->property_points() = marker_drag_line_points;
2019 range_marker_drag_rect->property_x1() = x1;
2020 range_marker_drag_rect->property_x2() = x2;
2025 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2029 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2030 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2036 Location *location = find_location_from_marker (marker, is_start);
2038 drag_info.item = item;
2039 drag_info.data = marker;
2040 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2041 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2045 drag_info.copied_location = new Location (*location);
2046 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2048 update_marker_drag_item (location);
2050 if (location->is_mark()) {
2051 marker_drag_line->show();
2052 marker_drag_line->raise_to_top();
2055 range_marker_drag_rect->show();
2056 range_marker_drag_rect->raise_to_top();
2059 if (is_start) show_verbose_time_cursor (location->start(), 10);
2060 else show_verbose_time_cursor (location->end(), 10);
2064 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2067 Marker* marker = (Marker *) drag_info.data;
2068 Location *real_location;
2069 Location *copy_location;
2071 bool move_both = false;
2075 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2076 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2081 nframes_t next = newframe;
2083 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2084 snap_to (newframe, 0, true);
2087 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2091 /* call this to find out if its the start or end */
2093 real_location = find_location_from_marker (marker, is_start);
2095 /* use the copy that we're "dragging" around */
2097 copy_location = drag_info.copied_location;
2099 f_delta = copy_location->end() - copy_location->start();
2101 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2105 if (copy_location->is_mark()) {
2108 copy_location->set_start (newframe);
2112 if (is_start) { // start-of-range marker
2115 copy_location->set_start (newframe);
2116 copy_location->set_end (newframe + f_delta);
2117 } else if (newframe < copy_location->end()) {
2118 copy_location->set_start (newframe);
2120 snap_to (next, 1, true);
2121 copy_location->set_end (next);
2122 copy_location->set_start (newframe);
2125 } else { // end marker
2128 copy_location->set_end (newframe);
2129 copy_location->set_start (newframe - f_delta);
2130 } else if (newframe > copy_location->start()) {
2131 copy_location->set_end (newframe);
2133 } else if (newframe > 0) {
2134 snap_to (next, -1, true);
2135 copy_location->set_start (next);
2136 copy_location->set_end (newframe);
2141 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2142 drag_info.first_move = false;
2144 update_marker_drag_item (copy_location);
2146 LocationMarkers* lm = find_location_markers (real_location);
2147 lm->set_position (copy_location->start(), copy_location->end());
2149 show_verbose_time_cursor (newframe, 10);
2153 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2155 if (drag_info.first_move) {
2156 marker_drag_motion_callback (item, event);
2160 Marker* marker = (Marker *) drag_info.data;
2164 begin_reversible_command ( _("move marker") );
2165 XMLNode &before = session->locations()->get_state();
2167 Location * location = find_location_from_marker (marker, is_start);
2170 if (location->is_mark()) {
2171 location->set_start (drag_info.copied_location->start());
2173 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2177 XMLNode &after = session->locations()->get_state();
2178 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2179 commit_reversible_command ();
2181 marker_drag_line->hide();
2182 range_marker_drag_rect->hide();
2186 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2189 MeterMarker* meter_marker;
2191 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2192 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2196 meter_marker = dynamic_cast<MeterMarker*> (marker);
2198 MetricSection& section (meter_marker->meter());
2200 if (!section.movable()) {
2204 drag_info.item = item;
2205 drag_info.data = marker;
2206 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2207 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2211 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2213 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2217 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2220 MeterMarker* meter_marker;
2222 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2223 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2227 meter_marker = dynamic_cast<MeterMarker*> (marker);
2229 // create a dummy marker for visual representation of moving the copy.
2230 // The actual copying is not done before we reach the finish callback.
2232 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2233 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2234 *new MeterSection(meter_marker->meter()));
2236 drag_info.item = &new_marker->the_item();
2237 drag_info.copy = true;
2238 drag_info.data = new_marker;
2239 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2240 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2244 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2246 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2250 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2252 MeterMarker* marker = (MeterMarker *) drag_info.data;
2253 nframes_t adjusted_frame;
2255 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2256 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2262 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2263 snap_to (adjusted_frame);
2266 if (adjusted_frame == drag_info.last_pointer_frame) return;
2268 marker->set_position (adjusted_frame);
2271 drag_info.last_pointer_frame = adjusted_frame;
2272 drag_info.first_move = false;
2274 show_verbose_time_cursor (adjusted_frame, 10);
2278 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2280 if (drag_info.first_move) return;
2282 meter_marker_drag_motion_callback (drag_info.item, event);
2284 MeterMarker* marker = (MeterMarker *) drag_info.data;
2287 TempoMap& map (session->tempo_map());
2288 map.bbt_time (drag_info.last_pointer_frame, when);
2290 if (drag_info.copy == true) {
2291 begin_reversible_command (_("copy meter mark"));
2292 XMLNode &before = map.get_state();
2293 map.add_meter (marker->meter(), when);
2294 XMLNode &after = map.get_state();
2295 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2296 commit_reversible_command ();
2298 // delete the dummy marker we used for visual representation of copying.
2299 // a new visual marker will show up automatically.
2302 begin_reversible_command (_("move meter mark"));
2303 XMLNode &before = map.get_state();
2304 map.move_meter (marker->meter(), when);
2305 XMLNode &after = map.get_state();
2306 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2307 commit_reversible_command ();
2312 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2315 TempoMarker* tempo_marker;
2317 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2318 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2322 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2323 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2327 MetricSection& section (tempo_marker->tempo());
2329 if (!section.movable()) {
2333 drag_info.item = item;
2334 drag_info.data = marker;
2335 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2336 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2340 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2341 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2345 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2348 TempoMarker* tempo_marker;
2350 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2351 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2355 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2356 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2360 // create a dummy marker for visual representation of moving the copy.
2361 // The actual copying is not done before we reach the finish callback.
2363 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2364 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2365 *new TempoSection(tempo_marker->tempo()));
2367 drag_info.item = &new_marker->the_item();
2368 drag_info.copy = true;
2369 drag_info.data = new_marker;
2370 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2371 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2375 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2377 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2381 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2383 TempoMarker* marker = (TempoMarker *) drag_info.data;
2384 nframes_t adjusted_frame;
2386 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2387 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2393 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2394 snap_to (adjusted_frame);
2397 if (adjusted_frame == drag_info.last_pointer_frame) return;
2399 /* OK, we've moved far enough to make it worth actually move the thing. */
2401 marker->set_position (adjusted_frame);
2403 show_verbose_time_cursor (adjusted_frame, 10);
2405 drag_info.last_pointer_frame = adjusted_frame;
2406 drag_info.first_move = false;
2410 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2412 if (drag_info.first_move) return;
2414 tempo_marker_drag_motion_callback (drag_info.item, event);
2416 TempoMarker* marker = (TempoMarker *) drag_info.data;
2419 TempoMap& map (session->tempo_map());
2420 map.bbt_time (drag_info.last_pointer_frame, when);
2422 if (drag_info.copy == true) {
2423 begin_reversible_command (_("copy tempo mark"));
2424 XMLNode &before = map.get_state();
2425 map.add_tempo (marker->tempo(), when);
2426 XMLNode &after = map.get_state();
2427 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2428 commit_reversible_command ();
2430 // delete the dummy marker we used for visual representation of copying.
2431 // a new visual marker will show up automatically.
2434 begin_reversible_command (_("move tempo mark"));
2435 XMLNode &before = map.get_state();
2436 map.move_tempo (marker->tempo(), when);
2437 XMLNode &after = map.get_state();
2438 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2439 commit_reversible_command ();
2444 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2446 ControlPoint* control_point;
2448 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2449 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2453 // We shouldn't remove the first or last gain point
2454 if (control_point->line().is_last_point(*control_point) ||
2455 control_point->line().is_first_point(*control_point)) {
2459 control_point->line().remove_point (*control_point);
2463 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2465 ControlPoint* control_point;
2467 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2468 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2472 control_point->line().remove_point (*control_point);
2476 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2478 ControlPoint* control_point;
2480 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2481 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2485 drag_info.item = item;
2486 drag_info.data = control_point;
2487 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2488 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2490 start_grab (event, fader_cursor);
2492 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2494 double fraction = 1.0 - ((control_point->get_y() - control_point->line().y_position()) / (double)control_point->line().height());
2495 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
2496 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2498 show_verbose_canvas_cursor ();
2502 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2504 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2506 double cx = drag_info.current_pointer_x;
2507 double cy = drag_info.current_pointer_y;
2509 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2510 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2512 if (drag_info.x_constrained) {
2513 cx = drag_info.grab_x;
2515 if (drag_info.y_constrained) {
2516 cy = drag_info.grab_y;
2519 cp->line().parent_group().w2i (cx, cy);
2523 cy = min ((double) (cp->line().y_position() + cp->line().height()), cy);
2525 //translate cx to frames
2526 nframes_t cx_frames = unit_to_frame (cx);
2528 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2529 snap_to (cx_frames);
2532 const double fraction = 1.0 - ((cy - cp->line().y_position()) / (double)cp->line().height());
2536 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2542 cp->line().point_drag (*cp, cx_frames , fraction, push);
2544 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
2546 drag_info.first_move = false;
2550 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2552 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2554 if (drag_info.first_move) {
2558 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2559 reset_point_selection ();
2563 control_point_drag_motion_callback (item, event);
2565 cp->line().end_drag (cp);
2569 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2571 switch (mouse_mode) {
2573 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2574 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2582 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2586 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2587 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2591 start_line_grab (al, event);
2595 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2599 nframes_t frame_within_region;
2601 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2605 cx = event->button.x;
2606 cy = event->button.y;
2607 line->parent_group().w2i (cx, cy);
2608 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2610 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2611 current_line_drag_info.after)) {
2612 /* no adjacent points */
2616 drag_info.item = &line->grab_item();
2617 drag_info.data = line;
2618 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2619 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2621 start_grab (event, fader_cursor);
2623 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2625 line->start_drag (0, drag_info.grab_frame, fraction);
2627 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2628 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2629 show_verbose_canvas_cursor ();
2633 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2635 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2636 double cx = drag_info.current_pointer_x;
2637 double cy = drag_info.current_pointer_y;
2639 line->parent_group().w2i (cx, cy);
2641 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2645 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2651 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2653 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2657 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2659 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2660 line_drag_motion_callback (item, event);
2665 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2667 if (selection->regions.empty() || clicked_regionview == 0) {
2671 drag_info.copy = false;
2672 drag_info.item = item;
2673 drag_info.data = clicked_regionview;
2674 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2675 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2680 TimeAxisView* tvp = clicked_axisview;
2681 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2683 if (tv && tv->is_track()) {
2684 speed = tv->get_diskstream()->speed();
2687 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2688 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2689 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2690 // we want a move threshold
2691 drag_info.want_move_threshold = true;
2693 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2695 begin_reversible_command (_("move region(s)"));
2699 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2701 if (selection->regions.empty() || clicked_regionview == 0) {
2705 drag_info.copy = true;
2706 drag_info.item = item;
2707 drag_info.data = clicked_regionview;
2711 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2712 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2715 if (rtv && rtv->is_track()) {
2716 speed = rtv->get_diskstream()->speed();
2719 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2720 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2721 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2722 // we want a move threshold
2723 drag_info.want_move_threshold = true;
2724 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2725 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2726 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2730 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2732 if (selection->regions.empty() || clicked_regionview == 0) {
2736 drag_info.copy = false;
2737 drag_info.item = item;
2738 drag_info.data = clicked_regionview;
2739 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2740 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2745 TimeAxisView* tvp = clicked_axisview;
2746 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2748 if (tv && tv->is_track()) {
2749 speed = tv->get_diskstream()->speed();
2752 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2753 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2754 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2755 // we want a move threshold
2756 drag_info.want_move_threshold = true;
2757 drag_info.brushing = true;
2759 begin_reversible_command (_("Drag region brush"));
2763 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2767 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2768 nframes_t pending_region_position = 0;
2769 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2770 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2771 bool clamp_y_axis = false;
2772 vector<int32_t> height_list(512) ;
2773 vector<int32_t>::iterator j;
2775 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2777 drag_info.want_move_threshold = false; // don't copy again
2779 /* duplicate the region(s) */
2781 vector<RegionView*> new_regionviews;
2783 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2789 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2790 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2793 nrv = new AudioRegionView (*arv);
2795 nrv = new MidiRegionView (*mrv);
2800 nrv->get_canvas_group()->show ();
2802 new_regionviews.push_back (nrv);
2805 if (new_regionviews.empty()) {
2809 /* reset selection to new regionviews */
2811 selection->set (new_regionviews);
2813 /* reset drag_info data to reflect the fact that we are dragging the copies */
2815 drag_info.data = new_regionviews.front();
2817 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2820 /* Which trackview is this ? */
2822 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2823 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2825 /* The region motion is only processed if the pointer is over
2829 if (!tv || !tv->is_track()) {
2830 /* To make sure we hide the verbose canvas cursor when the mouse is
2831 not held over a track.
2833 hide_verbose_canvas_cursor ();
2837 original_pointer_order = drag_info.last_trackview->order;
2839 /************************************************************
2841 ************************************************************/
2843 if (drag_info.brushing) {
2844 clamp_y_axis = true;
2849 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2851 int32_t children = 0, numtracks = 0;
2852 // XXX hard coding track limit, oh my, so very very bad
2853 bitset <1024> tracks (0x00);
2854 /* get a bitmask representing the visible tracks */
2856 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2857 TimeAxisView *tracklist_timeview;
2858 tracklist_timeview = (*i);
2859 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2860 TimeAxisView::Children children_list;
2862 /* zeroes are audio tracks. ones are other types. */
2864 if (!rtv2->hidden()) {
2866 if (visible_y_high < rtv2->order) {
2867 visible_y_high = rtv2->order;
2869 if (visible_y_low > rtv2->order) {
2870 visible_y_low = rtv2->order;
2873 if (!rtv2->is_track()) {
2874 tracks = tracks |= (0x01 << rtv2->order);
2877 height_list[rtv2->order] = (*i)->height;
2879 if ((children_list = rtv2->get_child_list()).size() > 0) {
2880 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2881 tracks = tracks |= (0x01 << (rtv2->order + children));
2882 height_list[rtv2->order + children] = (*j)->height;
2890 /* find the actual span according to the canvas */
2892 canvas_pointer_y_span = pointer_y_span;
2893 if (drag_info.last_trackview->order >= tv->order) {
2895 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2896 if (height_list[y] == 0 ) {
2897 canvas_pointer_y_span--;
2902 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2903 if ( height_list[y] == 0 ) {
2904 canvas_pointer_y_span++;
2909 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2910 RegionView* rv2 = (*i);
2911 double ix1, ix2, iy1, iy2;
2914 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2915 rv2->get_canvas_group()->i2w (ix1, iy1);
2916 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2917 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2919 if (rtv2->order != original_pointer_order) {
2920 /* this isn't the pointer track */
2922 if (canvas_pointer_y_span > 0) {
2924 /* moving up the canvas */
2925 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2927 int32_t visible_tracks = 0;
2928 while (visible_tracks < canvas_pointer_y_span ) {
2931 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2932 /* we're passing through a hidden track */
2937 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2938 clamp_y_axis = true;
2942 clamp_y_axis = true;
2945 } else if (canvas_pointer_y_span < 0) {
2947 /*moving down the canvas*/
2949 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2952 int32_t visible_tracks = 0;
2954 while (visible_tracks > canvas_pointer_y_span ) {
2957 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2961 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2962 clamp_y_axis = true;
2967 clamp_y_axis = true;
2973 /* this is the pointer's track */
2974 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2975 clamp_y_axis = true;
2976 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2977 clamp_y_axis = true;
2985 } else if (drag_info.last_trackview == tv) {
2986 clamp_y_axis = true;
2990 if (!clamp_y_axis) {
2991 drag_info.last_trackview = tv;
2994 /************************************************************
2996 ************************************************************/
2998 /* compute the amount of pointer motion in frames, and where
2999 the region would be if we moved it by that much.
3002 if (drag_info.move_threshold_passed) {
3004 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3006 nframes_t sync_frame;
3007 nframes_t sync_offset;
3010 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3012 sync_offset = rv->region()->sync_offset (sync_dir);
3013 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3015 /* we snap if the snap modifier is not enabled.
3018 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3019 snap_to (sync_frame);
3022 if (sync_frame - sync_offset <= sync_frame) {
3023 pending_region_position = sync_frame - (sync_dir*sync_offset);
3025 pending_region_position = 0;
3029 pending_region_position = 0;
3032 if (pending_region_position > max_frames - rv->region()->length()) {
3033 pending_region_position = drag_info.last_frame_position;
3036 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3038 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3040 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3041 to make it appear at the new location.
3044 if (pending_region_position > drag_info.last_frame_position) {
3045 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3047 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3050 drag_info.last_frame_position = pending_region_position;
3057 /* threshold not passed */
3062 /*************************************************************
3064 ************************************************************/
3066 if (x_delta == 0 && (pointer_y_span == 0)) {
3067 /* haven't reached next snap point, and we're not switching
3068 trackviews. nothing to do.
3075 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3077 RegionView* rv2 = (*i);
3079 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3081 double ix1, ix2, iy1, iy2;
3082 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3083 rv2->get_canvas_group()->i2w (ix1, iy1);
3092 /*************************************************************
3094 ************************************************************/
3098 if (drag_info.first_move) {
3099 if (drag_info.move_threshold_passed) {
3110 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3111 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3113 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3115 RegionView* rv = (*i);
3116 double ix1, ix2, iy1, iy2;
3117 int32_t temp_pointer_y_span = pointer_y_span;
3119 /* get item BBox, which will be relative to parent. so we have
3120 to query on a child, then convert to world coordinates using
3124 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3125 rv->get_canvas_group()->i2w (ix1, iy1);
3126 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3127 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3128 RouteTimeAxisView* temp_rtv;
3130 if ((pointer_y_span != 0) && !clamp_y_axis) {
3133 for (j = height_list.begin(); j!= height_list.end(); j++) {
3134 if (x == canvas_rtv->order) {
3135 /* we found the track the region is on */
3136 if (x != original_pointer_order) {
3137 /*this isn't from the same track we're dragging from */
3138 temp_pointer_y_span = canvas_pointer_y_span;
3140 while (temp_pointer_y_span > 0) {
3141 /* we're moving up canvas-wise,
3142 so we need to find the next track height
3144 if (j != height_list.begin()) {
3147 if (x != original_pointer_order) {
3148 /* we're not from the dragged track, so ignore hidden tracks. */
3150 temp_pointer_y_span++;
3154 temp_pointer_y_span--;
3156 while (temp_pointer_y_span < 0) {
3158 if (x != original_pointer_order) {
3160 temp_pointer_y_span--;
3164 if (j != height_list.end()) {
3167 temp_pointer_y_span++;
3169 /* find out where we'll be when we move and set height accordingly */
3171 tvp2 = trackview_by_y_position (iy1 + y_delta);
3172 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3173 rv->set_y_position_and_height (0, temp_rtv->height);
3175 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3176 personally, i think this can confuse things, but never mind.
3179 //const GdkColor& col (temp_rtv->view->get_region_color());
3180 //rv->set_color (const_cast<GdkColor&>(col));
3187 /* prevent the regionview from being moved to before
3188 the zero position on the canvas.
3193 if (-x_delta > ix1) {
3196 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3197 x_delta = max_frames - rv->region()->last_frame();
3201 if (drag_info.first_move) {
3203 /* hide any dependent views */
3205 rv->get_time_axis_view().hide_dependent_views (*rv);
3207 /* this is subtle. raising the regionview itself won't help,
3208 because raise_to_top() just puts the item on the top of
3209 its parent's stack. so, we need to put the trackview canvas_display group
3210 on the top, since its parent is the whole canvas.
3213 rv->get_canvas_group()->raise_to_top();
3214 rv->get_time_axis_view().canvas_display->raise_to_top();
3215 cursor_group->raise_to_top();
3217 rv->fake_set_opaque (true);
3220 if (drag_info.brushing) {
3221 mouse_brush_insert_region (rv, pending_region_position);
3223 rv->move (x_delta, y_delta);
3226 } /* foreach region */
3230 if (drag_info.first_move && drag_info.move_threshold_passed) {
3231 cursor_group->raise_to_top();
3232 drag_info.first_move = false;
3235 if (x_delta != 0 && !drag_info.brushing) {
3236 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3241 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3244 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3245 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3246 bool nocommit = true;
3248 RouteTimeAxisView* rtv;
3249 bool regionview_y_movement;
3250 bool regionview_x_movement;
3251 vector<RegionView*> copies;
3253 /* first_move is set to false if the regionview has been moved in the
3257 if (drag_info.first_move) {
3264 /* The regionview has been moved at some stage during the grab so we need
3265 to account for any mouse movement between this event and the last one.
3268 region_drag_motion_callback (item, event);
3270 if (drag_info.brushing) {
3271 /* all changes were made during motion event handlers */
3273 if (drag_info.copy) {
3274 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3275 copies.push_back (*i);
3282 /* adjust for track speed */
3285 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3286 if (rtv && rtv->get_diskstream()) {
3287 speed = rtv->get_diskstream()->speed();
3290 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3291 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3293 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3294 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3298 if (drag_info.copy) {
3299 if (drag_info.x_constrained) {
3300 op_string = _("fixed time region copy");
3302 op_string = _("region copy");
3305 if (drag_info.x_constrained) {
3306 op_string = _("fixed time region drag");
3308 op_string = _("region drag");
3312 begin_reversible_command (op_string);
3314 if (regionview_y_movement) {
3316 /* moved to a different audio track. */
3318 vector<RegionView*> new_selection;
3320 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3322 RegionView* rv = (*i);
3324 double ix1, ix2, iy1, iy2;
3326 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3327 rv->get_canvas_group()->i2w (ix1, iy1);
3328 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3329 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3331 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3332 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3334 where = (nframes_t) (unit_to_frame (ix1) * speed);
3335 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3337 /* undo the previous hide_dependent_views so that xfades don't
3338 disappear on copying regions
3341 rv->get_time_axis_view().reveal_dependent_views (*rv);
3343 if (!drag_info.copy) {
3345 /* the region that used to be in the old playlist is not
3346 moved to the new one - we make a copy of it. as a result,
3347 any existing editor for the region should no longer be
3351 rv->hide_region_editor();
3352 rv->fake_set_opaque (false);
3354 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3355 from_playlist->remove_region ((rv->region()));
3356 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3360 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3362 copies.push_back (rv);
3365 latest_regionview = 0;
3367 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3368 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3369 to_playlist->add_region (new_region, where);
3370 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3373 if (latest_regionview) {
3374 new_selection.push_back (latest_regionview);
3377 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3378 was selected in all of them, then removing it from the playlist will have removed all
3379 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3380 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3381 corresponding regionview, and the selection is now empty).
3383 this could have invalidated any and all iterators into the region selection.
3385 the heuristic we use here is: if the region selection is empty, break out of the loop
3386 here. if the region selection is not empty, then restart the loop because we know that
3387 we must have removed at least the region(view) we've just been working on as well as any
3388 that we processed on previous iterations.
3390 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3391 we can just iterate.
3394 if (drag_info.copy) {
3397 if (selection->regions.empty()) {
3400 i = selection->regions.by_layer().begin();
3405 selection->set (new_selection);
3409 /* motion within a single track */
3411 list<RegionView*> regions = selection->regions.by_layer();
3413 if (drag_info.copy) {
3414 selection->clear_regions();
3417 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3421 if (!rv->region()->can_move()) {
3425 if (regionview_x_movement) {
3426 double ownspeed = 1.0;
3427 rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3429 if (rtv && rtv->get_diskstream()) {
3430 ownspeed = rtv->get_diskstream()->speed();
3433 /* base the new region position on the current position of the regionview.*/
3435 double ix1, ix2, iy1, iy2;
3437 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3438 rv->get_canvas_group()->i2w (ix1, iy1);
3439 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3443 where = rv->region()->position();
3446 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3448 assert (to_playlist);
3452 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3454 if (drag_info.copy) {
3456 boost::shared_ptr<Region> newregion;
3457 boost::shared_ptr<Region> ar;
3458 boost::shared_ptr<Region> mr;
3460 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3461 newregion = RegionFactory::create (ar);
3462 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3463 newregion = RegionFactory::create (mr);
3468 latest_regionview = 0;
3469 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3470 to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
3473 if (latest_regionview) {
3474 rtv->reveal_dependent_views (*latest_regionview);
3475 selection->add (latest_regionview);
3480 /* just change the model */
3482 rv->region()->set_position (where, (void*) this);
3488 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3490 if (drag_info.copy) {
3491 copies.push_back (rv);
3499 commit_reversible_command ();
3502 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3508 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3510 /* Either add to or set the set the region selection, unless
3511 this is an alignment click (control used)
3514 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3515 TimeAxisView* tv = &rv.get_time_axis_view();
3516 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3518 if (rtv && rtv->is_track()) {
3519 speed = rtv->get_diskstream()->speed();
3522 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3524 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3526 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3528 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3532 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3538 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3544 nframes_t frame_rate;
3551 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3552 case AudioClock::BBT:
3553 session->bbt_time (frame, bbt);
3554 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3557 case AudioClock::SMPTE:
3558 session->smpte_time (frame, smpte);
3559 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3562 case AudioClock::MinSec:
3563 /* XXX this is copied from show_verbose_duration_cursor() */
3564 frame_rate = session->frame_rate();
3565 hours = frame / (frame_rate * 3600);
3566 frame = frame % (frame_rate * 3600);
3567 mins = frame / (frame_rate * 60);
3568 frame = frame % (frame_rate * 60);
3569 secs = (float) frame / (float) frame_rate;
3570 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3574 snprintf (buf, sizeof(buf), "%u", frame);
3578 if (xpos >= 0 && ypos >=0) {
3579 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3582 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3584 show_verbose_canvas_cursor ();
3588 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3595 nframes_t distance, frame_rate;
3597 Meter meter_at_start(session->tempo_map().meter_at(start));
3603 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3604 case AudioClock::BBT:
3605 session->bbt_time (start, sbbt);
3606 session->bbt_time (end, ebbt);
3609 /* XXX this computation won't work well if the
3610 user makes a selection that spans any meter changes.
3613 ebbt.bars -= sbbt.bars;
3614 if (ebbt.beats >= sbbt.beats) {
3615 ebbt.beats -= sbbt.beats;
3618 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3620 if (ebbt.ticks >= sbbt.ticks) {
3621 ebbt.ticks -= sbbt.ticks;
3624 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3627 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3630 case AudioClock::SMPTE:
3631 session->smpte_duration (end - start, smpte);
3632 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3635 case AudioClock::MinSec:
3636 /* XXX this stuff should be elsewhere.. */
3637 distance = end - start;
3638 frame_rate = session->frame_rate();
3639 hours = distance / (frame_rate * 3600);
3640 distance = distance % (frame_rate * 3600);
3641 mins = distance / (frame_rate * 60);
3642 distance = distance % (frame_rate * 60);
3643 secs = (float) distance / (float) frame_rate;
3644 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3648 snprintf (buf, sizeof(buf), "%u", end - start);
3652 if (xpos >= 0 && ypos >=0) {
3653 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3656 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3658 show_verbose_canvas_cursor ();
3662 Editor::collect_new_region_view (RegionView* rv)
3664 latest_regionview = rv;
3668 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3670 if (clicked_regionview == 0) {
3674 /* lets try to create new Region for the selection */
3676 vector<boost::shared_ptr<AudioRegion> > new_regions;
3677 create_region_from_selection (new_regions);
3679 if (new_regions.empty()) {
3683 /* XXX fix me one day to use all new regions */
3685 boost::shared_ptr<Region> region (new_regions.front());
3687 /* add it to the current stream/playlist.
3689 tricky: the streamview for the track will add a new regionview. we will
3690 catch the signal it sends when it creates the regionview to
3691 set the regionview we want to then drag.
3694 latest_regionview = 0;
3695 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3697 /* A selection grab currently creates two undo/redo operations, one for
3698 creating the new region and another for moving it.
3701 begin_reversible_command (_("selection grab"));
3703 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3705 XMLNode *before = &(playlist->get_state());
3706 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3707 XMLNode *after = &(playlist->get_state());
3708 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3710 commit_reversible_command ();
3714 if (latest_regionview == 0) {
3715 /* something went wrong */
3719 /* we need to deselect all other regionviews, and select this one
3720 i'm ignoring undo stuff, because the region creation will take care of it */
3721 selection->set (latest_regionview);
3723 drag_info.item = latest_regionview->get_canvas_group();
3724 drag_info.data = latest_regionview;
3725 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3726 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3730 drag_info.last_trackview = clicked_axisview;
3731 drag_info.last_frame_position = latest_regionview->region()->position();
3732 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3734 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3738 Editor::cancel_selection ()
3740 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3741 (*i)->hide_selection ();
3743 begin_reversible_command (_("cancel selection"));
3744 selection->clear ();
3745 clicked_selection = 0;
3746 commit_reversible_command ();
3750 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3752 nframes_t start = 0;
3759 drag_info.item = item;
3760 drag_info.motion_callback = &Editor::drag_selection;
3761 drag_info.finished_callback = &Editor::end_selection_op;
3766 case CreateSelection:
3767 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3768 drag_info.copy = true;
3770 drag_info.copy = false;
3772 start_grab (event, selector_cursor);
3775 case SelectionStartTrim:
3776 if (clicked_axisview) {
3777 clicked_axisview->order_selection_trims (item, true);
3779 start_grab (event, trimmer_cursor);
3780 start = selection->time[clicked_selection].start;
3781 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3784 case SelectionEndTrim:
3785 if (clicked_axisview) {
3786 clicked_axisview->order_selection_trims (item, false);
3788 start_grab (event, trimmer_cursor);
3789 end = selection->time[clicked_selection].end;
3790 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3794 start = selection->time[clicked_selection].start;
3796 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3800 if (selection_op == SelectionMove) {
3801 show_verbose_time_cursor(start, 10);
3803 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3808 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3810 nframes_t start = 0;
3813 nframes_t pending_position;
3815 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3816 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3818 pending_position = 0;
3821 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3822 snap_to (pending_position);
3825 /* only alter selection if the current frame is
3826 different from the last frame position (adjusted)
3829 if (pending_position == drag_info.last_pointer_frame) return;
3831 switch (selection_op) {
3832 case CreateSelection:
3834 if (drag_info.first_move) {
3835 snap_to (drag_info.grab_frame);
3838 if (pending_position < drag_info.grab_frame) {
3839 start = pending_position;
3840 end = drag_info.grab_frame;
3842 end = pending_position;
3843 start = drag_info.grab_frame;
3846 /* first drag: Either add to the selection
3847 or create a new selection->
3850 if (drag_info.first_move) {
3852 begin_reversible_command (_("range selection"));
3854 if (drag_info.copy) {
3855 /* adding to the selection */
3856 clicked_selection = selection->add (start, end);
3857 drag_info.copy = false;
3859 /* new selection-> */
3860 clicked_selection = selection->set (clicked_axisview, start, end);
3865 case SelectionStartTrim:
3867 if (drag_info.first_move) {
3868 begin_reversible_command (_("trim selection start"));
3871 start = selection->time[clicked_selection].start;
3872 end = selection->time[clicked_selection].end;
3874 if (pending_position > end) {
3877 start = pending_position;
3881 case SelectionEndTrim:
3883 if (drag_info.first_move) {
3884 begin_reversible_command (_("trim selection end"));
3887 start = selection->time[clicked_selection].start;
3888 end = selection->time[clicked_selection].end;
3890 if (pending_position < start) {
3893 end = pending_position;
3900 if (drag_info.first_move) {
3901 begin_reversible_command (_("move selection"));
3904 start = selection->time[clicked_selection].start;
3905 end = selection->time[clicked_selection].end;
3907 length = end - start;
3909 start = pending_position;
3912 end = start + length;
3917 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3918 start_canvas_autoscroll (1);
3922 selection->replace (clicked_selection, start, end);
3925 drag_info.last_pointer_frame = pending_position;
3926 drag_info.first_move = false;
3928 if (selection_op == SelectionMove) {
3929 show_verbose_time_cursor(start, 10);
3931 show_verbose_time_cursor(pending_position, 10);
3936 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3938 if (!drag_info.first_move) {
3939 drag_selection (item, event);
3940 /* XXX this is not object-oriented programming at all. ick */
3941 if (selection->time.consolidate()) {
3942 selection->TimeChanged ();
3944 commit_reversible_command ();
3946 /* just a click, no pointer movement.*/
3948 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3950 selection->clear_time();
3955 /* XXX what happens if its a music selection? */
3956 session->set_audio_range (selection->time);
3957 stop_canvas_autoscroll ();
3961 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3964 TimeAxisView* tvp = clicked_axisview;
3965 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3967 if (tv && tv->is_track()) {
3968 speed = tv->get_diskstream()->speed();
3971 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3972 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3973 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3975 //drag_info.item = clicked_regionview->get_name_highlight();
3976 drag_info.item = item;
3977 drag_info.motion_callback = &Editor::trim_motion_callback;
3978 drag_info.finished_callback = &Editor::trim_finished_callback;
3980 start_grab (event, trimmer_cursor);
3982 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3983 trim_op = ContentsTrim;
3985 /* These will get overridden for a point trim.*/
3986 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3987 /* closer to start */
3988 trim_op = StartTrim;
3989 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3997 show_verbose_time_cursor(region_start, 10);
4000 show_verbose_time_cursor(region_end, 10);
4003 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4009 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4011 RegionView* rv = clicked_regionview;
4012 nframes_t frame_delta = 0;
4013 bool left_direction;
4014 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4016 /* snap modifier works differently here..
4017 its' current state has to be passed to the
4018 various trim functions in order to work properly
4022 TimeAxisView* tvp = clicked_axisview;
4023 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4024 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4026 if (tv && tv->is_track()) {
4027 speed = tv->get_diskstream()->speed();
4030 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4031 left_direction = true;
4033 left_direction = false;
4037 snap_to (drag_info.current_pointer_frame);
4040 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4044 if (drag_info.first_move) {
4050 trim_type = "Region start trim";
4053 trim_type = "Region end trim";
4056 trim_type = "Region content trim";
4060 begin_reversible_command (trim_type);
4062 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4063 (*i)->fake_set_opaque(false);
4064 (*i)->region()->freeze ();
4066 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4068 arv->temporarily_hide_envelope ();
4070 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4071 insert_result = motion_frozen_playlists.insert (pl);
4072 if (insert_result.second) {
4073 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4078 if (left_direction) {
4079 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4081 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4086 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4089 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4090 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4096 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4099 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4100 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4107 bool swap_direction = false;
4109 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4110 swap_direction = true;
4113 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4114 i != selection->regions.by_layer().end(); ++i)
4116 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4124 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4127 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4130 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4134 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4135 drag_info.first_move = false;
4139 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4141 boost::shared_ptr<Region> region (rv.region());
4143 if (region->locked()) {
4147 nframes_t new_bound;
4150 TimeAxisView* tvp = clicked_axisview;
4151 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4153 if (tv && tv->is_track()) {
4154 speed = tv->get_diskstream()->speed();
4157 if (left_direction) {
4158 if (swap_direction) {
4159 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4161 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4164 if (swap_direction) {
4165 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4167 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4172 snap_to (new_bound);
4174 region->trim_start ((nframes_t) (new_bound * speed), this);
4175 rv.region_changed (StartChanged);
4179 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4181 boost::shared_ptr<Region> region (rv.region());
4183 if (region->locked()) {
4187 nframes_t new_bound;
4190 TimeAxisView* tvp = clicked_axisview;
4191 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4193 if (tv && tv->is_track()) {
4194 speed = tv->get_diskstream()->speed();
4197 if (left_direction) {
4198 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4200 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4204 snap_to (new_bound, (left_direction ? 0 : 1));
4207 region->trim_front ((nframes_t) (new_bound * speed), this);
4209 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4213 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4215 boost::shared_ptr<Region> region (rv.region());
4217 if (region->locked()) {
4221 nframes_t new_bound;
4224 TimeAxisView* tvp = clicked_axisview;
4225 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4227 if (tv && tv->is_track()) {
4228 speed = tv->get_diskstream()->speed();
4231 if (left_direction) {
4232 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4234 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4238 snap_to (new_bound);
4240 region->trim_end ((nframes_t) (new_bound * speed), this);
4241 rv.region_changed (LengthChanged);
4245 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4247 if (!drag_info.first_move) {
4248 trim_motion_callback (item, event);
4250 if (!clicked_regionview->get_selected()) {
4251 thaw_region_after_trim (*clicked_regionview);
4254 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4255 i != selection->regions.by_layer().end(); ++i)
4257 thaw_region_after_trim (**i);
4258 (*i)->fake_set_opaque (true);
4262 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4264 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4267 motion_frozen_playlists.clear ();
4269 commit_reversible_command();
4271 /* no mouse movement */
4277 Editor::point_trim (GdkEvent* event)
4279 RegionView* rv = clicked_regionview;
4280 nframes_t new_bound = drag_info.current_pointer_frame;
4282 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4283 snap_to (new_bound);
4286 /* Choose action dependant on which button was pressed */
4287 switch (event->button.button) {
4289 trim_op = StartTrim;
4290 begin_reversible_command (_("Start point trim"));
4292 if (rv->get_selected()) {
4294 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4295 i != selection->regions.by_layer().end(); ++i)
4297 if (!(*i)->region()->locked()) {
4298 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4299 XMLNode &before = pl->get_state();
4300 (*i)->region()->trim_front (new_bound, this);
4301 XMLNode &after = pl->get_state();
4302 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4308 if (!rv->region()->locked()) {
4309 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4310 XMLNode &before = pl->get_state();
4311 rv->region()->trim_front (new_bound, this);
4312 XMLNode &after = pl->get_state();
4313 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4317 commit_reversible_command();
4322 begin_reversible_command (_("End point trim"));
4324 if (rv->get_selected()) {
4326 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4328 if (!(*i)->region()->locked()) {
4329 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4330 XMLNode &before = pl->get_state();
4331 (*i)->region()->trim_end (new_bound, this);
4332 XMLNode &after = pl->get_state();
4333 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4339 if (!rv->region()->locked()) {
4340 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4341 XMLNode &before = pl->get_state();
4342 rv->region()->trim_end (new_bound, this);
4343 XMLNode &after = pl->get_state();
4344 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4348 commit_reversible_command();
4357 Editor::thaw_region_after_trim (RegionView& rv)
4359 boost::shared_ptr<Region> region (rv.region());
4361 if (region->locked()) {
4365 region->thaw (_("trimmed region"));
4366 XMLNode &after = region->playlist()->get_state();
4367 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4369 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4371 arv->unhide_envelope ();
4375 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4380 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4381 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4385 Location* location = find_location_from_marker (marker, is_start);
4386 location->set_hidden (true, this);
4391 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4397 drag_info.item = item;
4398 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4399 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4401 range_marker_op = op;
4403 if (!temp_location) {
4404 temp_location = new Location;
4408 case CreateRangeMarker:
4409 case CreateTransportMarker:
4411 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4412 drag_info.copy = true;
4414 drag_info.copy = false;
4416 start_grab (event, selector_cursor);
4420 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4425 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4427 nframes_t start = 0;
4429 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4431 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4432 snap_to (drag_info.current_pointer_frame);
4435 /* only alter selection if the current frame is
4436 different from the last frame position.
4439 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4441 switch (range_marker_op) {
4442 case CreateRangeMarker:
4443 case CreateTransportMarker:
4444 if (drag_info.first_move) {
4445 snap_to (drag_info.grab_frame);
4448 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4449 start = drag_info.current_pointer_frame;
4450 end = drag_info.grab_frame;
4452 end = drag_info.current_pointer_frame;
4453 start = drag_info.grab_frame;
4456 /* first drag: Either add to the selection
4457 or create a new selection.
4460 if (drag_info.first_move) {
4462 temp_location->set (start, end);
4466 update_marker_drag_item (temp_location);
4467 range_marker_drag_rect->show();
4468 range_marker_drag_rect->raise_to_top();
4474 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4475 start_canvas_autoscroll (1);
4479 temp_location->set (start, end);
4481 double x1 = frame_to_pixel (start);
4482 double x2 = frame_to_pixel (end);
4483 crect->property_x1() = x1;
4484 crect->property_x2() = x2;
4486 update_marker_drag_item (temp_location);
4489 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4490 drag_info.first_move = false;
4492 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4497 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4499 Location * newloc = 0;
4502 if (!drag_info.first_move) {
4503 drag_range_markerbar_op (item, event);
4505 switch (range_marker_op) {
4506 case CreateRangeMarker:
4508 begin_reversible_command (_("new range marker"));
4509 XMLNode &before = session->locations()->get_state();
4510 session->locations()->next_available_name(rangename,"unnamed");
4511 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4512 session->locations()->add (newloc, true);
4513 XMLNode &after = session->locations()->get_state();
4514 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4515 commit_reversible_command ();
4517 range_bar_drag_rect->hide();
4518 range_marker_drag_rect->hide();
4522 case CreateTransportMarker:
4523 // popup menu to pick loop or punch
4524 new_transport_marker_context_menu (&event->button, item);
4529 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4531 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4536 start = session->locations()->first_mark_before (drag_info.grab_frame);
4537 end = session->locations()->first_mark_after (drag_info.grab_frame);
4539 if (end == max_frames) {
4540 end = session->current_end_frame ();
4544 start = session->current_start_frame ();
4547 switch (mouse_mode) {
4549 /* find the two markers on either side and then make the selection from it */
4550 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4554 /* find the two markers on either side of the click and make the range out of it */
4555 selection->set (0, start, end);
4564 stop_canvas_autoscroll ();
4570 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4572 drag_info.item = item;
4573 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4574 drag_info.finished_callback = &Editor::end_mouse_zoom;
4576 start_grab (event, zoom_cursor);
4578 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4582 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4587 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4588 snap_to (drag_info.current_pointer_frame);
4590 if (drag_info.first_move) {
4591 snap_to (drag_info.grab_frame);
4595 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4597 /* base start and end on initial click position */
4598 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4599 start = drag_info.current_pointer_frame;
4600 end = drag_info.grab_frame;
4602 end = drag_info.current_pointer_frame;
4603 start = drag_info.grab_frame;
4608 if (drag_info.first_move) {
4610 zoom_rect->raise_to_top();
4613 reposition_zoom_rect(start, end);
4615 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4616 drag_info.first_move = false;
4618 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4623 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4625 if (!drag_info.first_move) {
4626 drag_mouse_zoom (item, event);
4628 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4629 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4631 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4634 temporal_zoom_to_frame (false, drag_info.grab_frame);
4636 temporal_zoom_step (false);
4637 center_screen (drag_info.grab_frame);
4645 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4647 double x1 = frame_to_pixel (start);
4648 double x2 = frame_to_pixel (end);
4649 double y2 = full_canvas_height - 1.0;
4651 zoom_rect->property_x1() = x1;
4652 zoom_rect->property_y1() = 1.0;
4653 zoom_rect->property_x2() = x2;
4654 zoom_rect->property_y2() = y2;
4658 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4660 drag_info.item = item;
4661 drag_info.motion_callback = &Editor::drag_rubberband_select;
4662 drag_info.finished_callback = &Editor::end_rubberband_select;
4664 start_grab (event, cross_hair_cursor);
4666 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4670 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4677 /* use a bigger drag threshold than the default */
4679 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4683 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4684 if (drag_info.first_move) {
4685 snap_to (drag_info.grab_frame);
4687 snap_to (drag_info.current_pointer_frame);
4690 /* base start and end on initial click position */
4692 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4693 start = drag_info.current_pointer_frame;
4694 end = drag_info.grab_frame;
4696 end = drag_info.current_pointer_frame;
4697 start = drag_info.grab_frame;
4700 if (drag_info.current_pointer_y < drag_info.grab_y) {
4701 y1 = drag_info.current_pointer_y;
4702 y2 = drag_info.grab_y;
4704 y2 = drag_info.current_pointer_y;
4705 y1 = drag_info.grab_y;
4709 if (start != end || y1 != y2) {
4711 double x1 = frame_to_pixel (start);
4712 double x2 = frame_to_pixel (end);
4714 rubberband_rect->property_x1() = x1;
4715 rubberband_rect->property_y1() = y1;
4716 rubberband_rect->property_x2() = x2;
4717 rubberband_rect->property_y2() = y2;
4719 rubberband_rect->show();
4720 rubberband_rect->raise_to_top();
4722 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4723 drag_info.first_move = false;
4725 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4730 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4732 if (!drag_info.first_move) {
4734 drag_rubberband_select (item, event);
4737 if (drag_info.current_pointer_y < drag_info.grab_y) {
4738 y1 = drag_info.current_pointer_y;
4739 y2 = drag_info.grab_y;
4742 y2 = drag_info.current_pointer_y;
4743 y1 = drag_info.grab_y;
4747 Selection::Operation op = Keyboard::selection_type (event->button.state);
4750 begin_reversible_command (_("rubberband selection"));
4752 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4753 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4755 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4759 commit_reversible_command ();
4763 selection->clear_tracks();
4764 selection->clear_regions();
4765 selection->clear_points ();
4766 selection->clear_lines ();
4769 rubberband_rect->hide();
4774 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4776 using namespace Gtkmm2ext;
4778 ArdourPrompter prompter (false);
4780 prompter.set_prompt (_("Name for region:"));
4781 prompter.set_initial_text (clicked_regionview->region()->name());
4782 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4783 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4784 prompter.show_all ();
4785 switch (prompter.run ()) {
4786 case Gtk::RESPONSE_ACCEPT:
4788 prompter.get_result(str);
4790 clicked_regionview->region()->set_name (str);
4798 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4800 drag_info.item = item;
4801 drag_info.motion_callback = &Editor::time_fx_motion;
4802 drag_info.finished_callback = &Editor::end_time_fx;
4806 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4810 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4812 RegionView* rv = clicked_regionview;
4814 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4815 snap_to (drag_info.current_pointer_frame);
4818 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4822 if (drag_info.current_pointer_frame > rv->region()->position()) {
4823 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4826 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4827 drag_info.first_move = false;
4829 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4833 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4835 clicked_regionview->get_time_axis_view().hide_timestretch ();
4837 if (drag_info.first_move) {
4841 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4842 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4844 begin_reversible_command (_("timestretch"));
4846 if (run_timestretch (selection->regions, percentage) == 0) {
4847 session->commit_reversible_command ();
4852 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4854 /* no brushing without a useful snap setting */
4857 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4860 switch (snap_mode) {
4862 return; /* can't work because it allows region to be placed anywhere */
4867 switch (snap_type) {
4870 case SnapToEditCursor:
4877 /* don't brush a copy over the original */
4879 if (pos == rv->region()->position()) {
4883 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4885 if (rtv == 0 || !rtv->is_track()) {
4889 boost::shared_ptr<Playlist> playlist = rtv->playlist();
4890 double speed = rtv->get_diskstream()->speed();
4892 XMLNode &before = playlist->get_state();
4893 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4894 XMLNode &after = playlist->get_state();
4895 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4897 // playlist is frozen, so we have to update manually
4899 playlist->Modified(); /* EMIT SIGNAL */
4903 Editor::track_height_step_timeout ()
4906 struct timeval delta;
4908 gettimeofday (&now, 0);
4909 timersub (&now, &last_track_height_step_timestamp, &delta);
4911 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4912 current_stepping_trackview = 0;