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 ignore_midi_edit_mode_toggle = false;
380 set_midi_edit_cursor (current_midi_edit_mode());
383 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
388 Editor::set_midi_edit_cursor (MidiEditMode m)
390 switch (midi_edit_mode) {
392 current_canvas_cursor = midi_select_cursor;
395 current_canvas_cursor = midi_pencil_cursor;
398 current_canvas_cursor = midi_erase_cursor;
404 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
408 /* in object/audition/timefx mode, any button press sets
409 the selection if the object can be selected. this is a
410 bit of hack, because we want to avoid this if the
411 mouse operation is a region alignment.
413 note: not dbl-click or triple-click
416 if (((mouse_mode != MouseObject) &&
417 (mouse_mode != MouseAudition || item_type != RegionItem) &&
418 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
419 (mouse_mode != MouseRange)) ||
421 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
426 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
428 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
430 /* almost no selection action on modified button-2 or button-3 events */
432 if (item_type != RegionItem && event->button.button != 2) {
438 Selection::Operation op = Keyboard::selection_type (event->button.state);
439 bool press = (event->type == GDK_BUTTON_PRESS);
441 // begin_reversible_command (_("select on click"));
445 if (mouse_mode != MouseRange) {
446 commit = set_selected_regionview_from_click (press, op, true);
447 } else if (event->type == GDK_BUTTON_PRESS) {
448 commit = set_selected_track_from_click (press, op, false);
452 case RegionViewNameHighlight:
454 if (mouse_mode != MouseRange) {
455 commit = set_selected_regionview_from_click (press, op, true);
456 } else if (event->type == GDK_BUTTON_PRESS) {
457 commit = set_selected_track_from_click (press, op, false);
462 case FadeInHandleItem:
464 case FadeOutHandleItem:
466 if (mouse_mode != MouseRange) {
467 commit = set_selected_regionview_from_click (press, op, true);
468 } else if (event->type == GDK_BUTTON_PRESS) {
469 commit = set_selected_track_from_click (press, op, false);
473 case CrossfadeViewItem:
474 commit = set_selected_track_from_click (press, op, false);
477 case ControlPointItem:
478 commit = set_selected_track_from_click (press, op, true);
479 if (mouse_mode != MouseRange) {
480 commit |= set_selected_control_point_from_click (op, false);
485 /* for context click or range selection, select track */
486 if (event->button.button == 3) {
487 commit = set_selected_track_from_click (press, op, true);
488 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
489 commit = set_selected_track_from_click (press, op, false);
493 case AutomationTrackItem:
494 commit = set_selected_track_from_click (press, op, true);
502 // commit_reversible_command ();
507 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
509 track_canvas.grab_focus();
511 if (session && session->actively_recording()) {
515 button_selection (item, event, item_type);
517 if (drag_info.item == 0 &&
518 (Keyboard::is_delete_event (&event->button) ||
519 Keyboard::is_context_menu_event (&event->button) ||
520 Keyboard::is_edit_event (&event->button))) {
522 /* handled by button release */
526 switch (event->button.button) {
529 if (event->type == GDK_BUTTON_PRESS) {
531 if (drag_info.item) {
532 drag_info.item->ungrab (event->button.time);
535 /* single mouse clicks on any of these item types operate
536 independent of mouse mode, mostly because they are
537 not on the main track canvas or because we want
543 case PlayheadCursorItem:
544 start_cursor_grab (item, event);
548 if (Keyboard::modifier_state_equals (event->button.state,
549 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
550 hide_marker (item, event);
552 start_marker_grab (item, event);
556 case TempoMarkerItem:
557 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
558 start_tempo_marker_copy_grab (item, event);
560 start_tempo_marker_grab (item, event);
564 case MeterMarkerItem:
565 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
566 start_meter_marker_copy_grab (item, event);
568 start_meter_marker_grab (item, event);
578 case RangeMarkerBarItem:
579 start_range_markerbar_op (item, event, CreateRangeMarker);
583 case TransportMarkerBarItem:
584 start_range_markerbar_op (item, event, CreateTransportMarker);
593 switch (mouse_mode) {
596 case StartSelectionTrimItem:
597 start_selection_op (item, event, SelectionStartTrim);
600 case EndSelectionTrimItem:
601 start_selection_op (item, event, SelectionEndTrim);
605 if (Keyboard::modifier_state_contains
606 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
607 // contains and not equals because I can't use alt as a modifier alone.
608 start_selection_grab (item, event);
609 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
610 /* grab selection for moving */
611 start_selection_op (item, event, SelectionMove);
614 /* this was debated, but decided the more common action was to
615 make a new selection */
616 start_selection_op (item, event, CreateSelection);
621 start_selection_op (item, event, CreateSelection);
627 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
628 event->type == GDK_BUTTON_PRESS) {
630 start_rubberband_select (item, event);
632 } else if (event->type == GDK_BUTTON_PRESS) {
635 case FadeInHandleItem:
636 start_fade_in_grab (item, event);
639 case FadeOutHandleItem:
640 start_fade_out_grab (item, event);
644 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
645 start_region_copy_grab (item, event);
646 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
647 start_region_brush_grab (item, event);
649 start_region_grab (item, event);
653 case RegionViewNameHighlight:
654 start_trim (item, event);
659 /* rename happens on edit clicks */
660 start_trim (clicked_regionview->get_name_highlight(), event);
664 case ControlPointItem:
665 start_control_point_grab (item, event);
669 case AutomationLineItem:
670 start_line_grab_from_line (item, event);
675 case AutomationTrackItem:
676 start_rubberband_select (item, event);
680 case ImageFrameHandleStartItem:
681 imageframe_start_handle_op(item, event) ;
684 case ImageFrameHandleEndItem:
685 imageframe_end_handle_op(item, event) ;
688 case MarkerViewHandleStartItem:
689 markerview_item_start_handle_op(item, event) ;
692 case MarkerViewHandleEndItem:
693 markerview_item_end_handle_op(item, event) ;
697 start_markerview_grab(item, event) ;
700 start_imageframe_grab(item, event) ;
718 // start_line_grab_from_regionview (item, event);
722 start_line_grab_from_line (item, event);
725 case ControlPointItem:
726 start_control_point_grab (item, event);
737 case ControlPointItem:
738 start_control_point_grab (item, event);
741 case AutomationLineItem:
742 start_line_grab_from_line (item, event);
746 // XXX need automation mode to identify which
748 // start_line_grab_from_regionview (item, event);
758 if (event->type == GDK_BUTTON_PRESS) {
759 start_mouse_zoom (item, event);
766 if (item_type == RegionItem) {
767 start_time_fx (item, event);
773 last_scrub_frame = 0;
775 have_full_mouse_speed = false;
776 memset (mouse_speed, 0, sizeof (double) * mouse_speed_size);
777 /* rest handled in motion & release */
786 switch (mouse_mode) {
788 if (event->type == GDK_BUTTON_PRESS) {
791 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
792 start_region_copy_grab (item, event);
794 start_region_grab (item, event);
798 case ControlPointItem:
799 start_control_point_grab (item, event);
810 case RegionViewNameHighlight:
811 start_trim (item, event);
816 start_trim (clicked_regionview->get_name_highlight(), event);
827 if (event->type == GDK_BUTTON_PRESS) {
828 /* relax till release */
835 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
836 temporal_zoom_session();
838 temporal_zoom_to_frame (true, event_frame(event));
861 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
863 nframes_t where = event_frame (event, 0, 0);
865 /* no action if we're recording */
867 if (session && session->actively_recording()) {
871 /* first, see if we're finishing a drag ... */
873 if (drag_info.item) {
874 if (end_grab (item, event)) {
875 /* grab dragged, so do nothing else */
880 button_selection (item, event, item_type);
882 /* edit events get handled here */
884 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
890 case TempoMarkerItem:
891 edit_tempo_marker (item);
894 case MeterMarkerItem:
895 edit_meter_marker (item);
899 if (clicked_regionview->name_active()) {
900 return mouse_rename_region (item, event);
910 /* context menu events get handled here */
912 if (Keyboard::is_context_menu_event (&event->button)) {
914 if (drag_info.item == 0) {
916 /* no matter which button pops up the context menu, tell the menu
917 widget to use button 1 to drive menu selection.
922 case FadeInHandleItem:
924 case FadeOutHandleItem:
925 popup_fade_context_menu (1, event->button.time, item, item_type);
929 popup_track_context_menu (1, event->button.time, where);
933 case RegionViewNameHighlight:
935 popup_track_context_menu (1, event->button.time, where);
939 popup_track_context_menu (1, event->button.time, where);
942 case AutomationTrackItem:
943 case CrossfadeViewItem:
944 popup_track_context_menu (1, event->button.time, where);
948 case RangeMarkerBarItem:
949 case TransportMarkerBarItem:
952 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
956 marker_context_menu (&event->button, item);
959 case TempoMarkerItem:
960 tm_marker_context_menu (&event->button, item);
963 case MeterMarkerItem:
964 tm_marker_context_menu (&event->button, item);
969 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
971 case ImageFrameTimeAxisItem:
972 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
975 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
977 case MarkerTimeAxisItem:
978 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
990 /* delete events get handled here */
992 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
995 case TempoMarkerItem:
996 remove_tempo_marker (item);
999 case MeterMarkerItem:
1000 remove_meter_marker (item);
1004 remove_marker (*item, event);
1008 if (mouse_mode == MouseObject) {
1009 remove_clicked_region ();
1013 case ControlPointItem:
1014 if (mouse_mode == MouseGain) {
1015 remove_gain_control_point (item, event);
1017 remove_control_point (item, event);
1027 switch (event->button.button) {
1030 switch (item_type) {
1031 /* see comments in button_press_handler */
1032 case EditCursorItem:
1033 case PlayheadCursorItem:
1036 case AutomationLineItem:
1037 case StartSelectionTrimItem:
1038 case EndSelectionTrimItem:
1042 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1043 snap_to (where, 0, true);
1045 mouse_add_new_marker (where);
1049 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1052 mouse_add_new_tempo_event (where);
1056 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1064 switch (mouse_mode) {
1066 switch (item_type) {
1067 case AutomationTrackItem:
1068 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
1082 // Gain only makes sense for audio regions
1084 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1088 switch (item_type) {
1090 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1094 case AutomationTrackItem:
1095 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1096 add_automation_event (item, event, where, event->button.y);
1106 if (last_scrub_frame == 0) {
1107 /* no drag, just a click */
1108 switch (item_type) {
1110 audition_selected_region ();
1116 /* make sure we stop */
1117 session->request_transport_speed (0.0);
1131 switch (mouse_mode) {
1135 // x_style_paste (where, 1.0);
1155 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1161 switch (item_type) {
1162 case ControlPointItem:
1163 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1164 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1165 cp->set_visible (true);
1169 at_y = cp->get_y ();
1170 cp->item()->i2w (at_x, at_y);
1174 fraction = 1.0 - (cp->get_y() / cp->line().height());
1176 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1177 show_verbose_canvas_cursor ();
1179 if (is_drawable()) {
1180 track_canvas.get_window()->set_cursor (*fader_cursor);
1186 if (mouse_mode == MouseGain) {
1187 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1189 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1190 if (is_drawable()) {
1191 track_canvas.get_window()->set_cursor (*fader_cursor);
1196 case AutomationLineItem:
1197 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1199 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1201 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1203 if (is_drawable()) {
1204 track_canvas.get_window()->set_cursor (*fader_cursor);
1209 case RegionViewNameHighlight:
1210 if (is_drawable() && mouse_mode == MouseObject) {
1211 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1215 case StartSelectionTrimItem:
1216 case EndSelectionTrimItem:
1219 case ImageFrameHandleStartItem:
1220 case ImageFrameHandleEndItem:
1221 case MarkerViewHandleStartItem:
1222 case MarkerViewHandleEndItem:
1225 if (is_drawable()) {
1226 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1230 case EditCursorItem:
1231 case PlayheadCursorItem:
1232 if (is_drawable()) {
1233 track_canvas.get_window()->set_cursor (*grabber_cursor);
1237 case RegionViewName:
1239 /* when the name is not an active item, the entire name highlight is for trimming */
1241 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1242 if (mouse_mode == MouseObject && is_drawable()) {
1243 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1249 case AutomationTrackItem:
1250 if (is_drawable()) {
1251 Gdk::Cursor *cursor;
1252 switch (mouse_mode) {
1254 cursor = selector_cursor;
1257 cursor = zoom_cursor;
1260 cursor = cross_hair_cursor;
1264 track_canvas.get_window()->set_cursor (*cursor);
1266 AutomationTimeAxisView* atv;
1267 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1268 clear_entered_track = false;
1269 set_entered_track (atv);
1275 case RangeMarkerBarItem:
1276 case TransportMarkerBarItem:
1279 if (is_drawable()) {
1280 time_canvas.get_window()->set_cursor (*timebar_cursor);
1285 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1288 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1290 case MeterMarkerItem:
1291 case TempoMarkerItem:
1292 if (is_drawable()) {
1293 time_canvas.get_window()->set_cursor (*timebar_cursor);
1296 case FadeInHandleItem:
1297 case FadeOutHandleItem:
1298 if (mouse_mode == MouseObject) {
1299 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1301 rect->property_fill_color_rgba() = 0;
1302 rect->property_outline_pixels() = 1;
1311 /* second pass to handle entered track status in a comprehensible way.
1314 switch (item_type) {
1316 case AutomationLineItem:
1317 case ControlPointItem:
1318 /* these do not affect the current entered track state */
1319 clear_entered_track = false;
1322 case AutomationTrackItem:
1323 /* handled above already */
1327 set_entered_track (0);
1335 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1344 switch (item_type) {
1345 case ControlPointItem:
1346 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1347 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1348 if (cp->line().npoints() > 1 && !cp->selected()) {
1349 cp->set_visible (false);
1353 if (is_drawable()) {
1354 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1357 hide_verbose_canvas_cursor ();
1360 case RegionViewNameHighlight:
1361 case StartSelectionTrimItem:
1362 case EndSelectionTrimItem:
1363 case EditCursorItem:
1364 case PlayheadCursorItem:
1367 case ImageFrameHandleStartItem:
1368 case ImageFrameHandleEndItem:
1369 case MarkerViewHandleStartItem:
1370 case MarkerViewHandleEndItem:
1373 if (is_drawable()) {
1374 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1379 case AutomationLineItem:
1380 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1382 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1384 line->property_fill_color_rgba() = al->get_line_color();
1386 if (is_drawable()) {
1387 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1391 case RegionViewName:
1392 /* see enter_handler() for notes */
1393 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1394 if (is_drawable() && mouse_mode == MouseObject) {
1395 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1400 case RangeMarkerBarItem:
1401 case TransportMarkerBarItem:
1405 if (is_drawable()) {
1406 time_canvas.get_window()->set_cursor (*timebar_cursor);
1411 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1414 loc = find_location_from_marker (marker, is_start);
1415 if (loc) location_flags_changed (loc, this);
1417 case MeterMarkerItem:
1418 case TempoMarkerItem:
1420 if (is_drawable()) {
1421 time_canvas.get_window()->set_cursor (*timebar_cursor);
1426 case FadeInHandleItem:
1427 case FadeOutHandleItem:
1428 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1430 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1432 rect->property_fill_color_rgba() = rv->get_fill_color();
1433 rect->property_outline_pixels() = 0;
1438 case AutomationTrackItem:
1439 if (is_drawable()) {
1440 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1441 clear_entered_track = true;
1442 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1454 Editor::left_automation_track ()
1456 if (clear_entered_track) {
1457 set_entered_track (0);
1458 clear_entered_track = false;
1464 _update_mouse_speed (void *arg)
1466 return static_cast<Editor*>(arg)->update_mouse_speed ();
1470 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1474 /* We call this so that MOTION_NOTIFY events continue to be
1475 delivered to the canvas. We need to do this because we set
1476 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1477 the density of the events, at the expense of a round-trip
1478 to the server. Given that this will mostly occur on cases
1479 where DISPLAY = :0.0, and given the cost of what the motion
1480 event might do, its a good tradeoff.
1483 track_canvas.get_pointer (x, y);
1485 if (current_stepping_trackview) {
1486 /* don't keep the persistent stepped trackview if the mouse moves */
1487 current_stepping_trackview = 0;
1488 step_timeout.disconnect ();
1491 if (session && session->actively_recording()) {
1492 /* Sorry. no dragging stuff around while we record */
1496 drag_info.item_type = item_type;
1497 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1498 &drag_info.current_pointer_y);
1500 switch (mouse_mode) {
1503 struct timeval tmnow;
1505 if (last_scrub_frame == 0) {
1507 /* first motion, just set up the variables */
1509 last_scrub_frame = (nframes64_t) drag_info.current_pointer_frame;
1510 gettimeofday (&tmnow, 0);
1511 last_scrub_time = tmnow.tv_sec * 1000000.0 + tmnow.tv_usec;
1512 session->request_locate (last_scrub_frame, true);
1515 /* how fast is the mouse moving ? */
1523 if (last_scrub_frame < (nframes64_t) drag_info.current_pointer_frame) {
1524 distance = (nframes64_t) drag_info.current_pointer_frame - last_scrub_frame;
1527 distance = last_scrub_frame - (nframes64_t) drag_info.current_pointer_frame;
1531 if (drag_info.grab_x < drag_info.current_pointer_x) {
1532 distance = drag_info.current_pointer_x - drag_info.grab_x;
1535 distance = drag_info.grab_x - drag_info.current_pointer_x;
1540 gettimeofday (&tmnow, 0);
1541 time = (tmnow.tv_sec * 1000000.0 + tmnow.tv_usec) - last_scrub_time;
1542 last_scrub_frame = drag_info.current_pointer_frame;
1543 last_scrub_time = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec;
1544 speed = ((double)distance/session->frame_rate()) / (time/1000000.0); // frames/sec
1546 add_mouse_speed (speed, dir);
1548 if (mouse_speed_update < 0) {
1549 mouse_speed_update = g_timeout_add (10, _update_mouse_speed, this);
1550 update_mouse_speed ();
1560 if (!from_autoscroll && drag_info.item) {
1561 /* item != 0 is the best test i can think of for dragging.
1563 if (!drag_info.move_threshold_passed) {
1565 bool x_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1566 bool y_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1568 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1570 // and change the initial grab loc/frame if this drag info wants us to
1572 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1573 drag_info.grab_frame = drag_info.current_pointer_frame;
1574 drag_info.grab_x = drag_info.current_pointer_x;
1575 drag_info.grab_y = drag_info.current_pointer_y;
1576 drag_info.last_pointer_frame = drag_info.grab_frame;
1577 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1582 switch (item_type) {
1583 case PlayheadCursorItem:
1584 case EditCursorItem:
1586 case ControlPointItem:
1587 case TempoMarkerItem:
1588 case MeterMarkerItem:
1589 case RegionViewNameHighlight:
1590 case StartSelectionTrimItem:
1591 case EndSelectionTrimItem:
1594 case AutomationLineItem:
1595 case FadeInHandleItem:
1596 case FadeOutHandleItem:
1599 case ImageFrameHandleStartItem:
1600 case ImageFrameHandleEndItem:
1601 case MarkerViewHandleStartItem:
1602 case MarkerViewHandleEndItem:
1605 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1606 (event->motion.state & Gdk::BUTTON2_MASK))) {
1607 if (!from_autoscroll) {
1608 maybe_autoscroll (event);
1610 (this->*(drag_info.motion_callback)) (item, event);
1619 switch (mouse_mode) {
1624 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1625 (event->motion.state & GDK_BUTTON2_MASK))) {
1626 if (!from_autoscroll) {
1627 maybe_autoscroll (event);
1629 (this->*(drag_info.motion_callback)) (item, event);
1640 track_canvas_motion (event);
1641 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1649 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1651 if (drag_info.item == 0) {
1652 fatal << _("programming error: start_grab called without drag item") << endmsg;
1658 cursor = grabber_cursor;
1661 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1663 if (event->button.button == 2) {
1664 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1665 drag_info.y_constrained = true;
1666 drag_info.x_constrained = false;
1668 drag_info.y_constrained = false;
1669 drag_info.x_constrained = true;
1672 drag_info.x_constrained = false;
1673 drag_info.y_constrained = false;
1676 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1677 drag_info.last_pointer_frame = drag_info.grab_frame;
1678 drag_info.current_pointer_frame = drag_info.grab_frame;
1679 drag_info.current_pointer_x = drag_info.grab_x;
1680 drag_info.current_pointer_y = drag_info.grab_y;
1681 drag_info.cumulative_x_drag = 0;
1682 drag_info.cumulative_y_drag = 0;
1683 drag_info.first_move = true;
1684 drag_info.move_threshold_passed = false;
1685 drag_info.want_move_threshold = false;
1686 drag_info.pointer_frame_offset = 0;
1687 drag_info.brushing = false;
1688 drag_info.copied_location = 0;
1690 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1692 event->button.time);
1694 if (session && session->transport_rolling()) {
1695 drag_info.was_rolling = true;
1697 drag_info.was_rolling = false;
1700 switch (snap_type) {
1701 case SnapToRegionStart:
1702 case SnapToRegionEnd:
1703 case SnapToRegionSync:
1704 case SnapToRegionBoundary:
1705 build_region_boundary_cache ();
1713 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1715 drag_info.item->ungrab (0);
1716 drag_info.item = new_item;
1719 cursor = grabber_cursor;
1722 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1726 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1728 bool did_drag = false;
1730 stop_canvas_autoscroll ();
1732 if (drag_info.item == 0) {
1736 drag_info.item->ungrab (event->button.time);
1738 if (drag_info.finished_callback) {
1739 (this->*(drag_info.finished_callback)) (item, event);
1742 did_drag = !drag_info.first_move;
1744 hide_verbose_canvas_cursor();
1747 drag_info.copy = false;
1748 drag_info.motion_callback = 0;
1749 drag_info.finished_callback = 0;
1750 drag_info.last_trackview = 0;
1751 drag_info.last_frame_position = 0;
1752 drag_info.grab_frame = 0;
1753 drag_info.last_pointer_frame = 0;
1754 drag_info.current_pointer_frame = 0;
1755 drag_info.brushing = false;
1757 if (drag_info.copied_location) {
1758 delete drag_info.copied_location;
1759 drag_info.copied_location = 0;
1766 Editor::set_edit_cursor (GdkEvent* event)
1768 nframes_t pointer_frame = event_frame (event);
1770 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1771 if (snap_type != SnapToEditCursor) {
1772 snap_to (pointer_frame);
1776 edit_cursor->set_position (pointer_frame);
1777 edit_cursor_clock.set (pointer_frame);
1781 Editor::set_playhead_cursor (GdkEvent* event)
1783 nframes_t pointer_frame = event_frame (event);
1785 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1786 snap_to (pointer_frame);
1790 session->request_locate (pointer_frame, session->transport_rolling());
1795 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1797 drag_info.item = item;
1798 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1799 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1803 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1804 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1808 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1810 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
1814 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1816 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1818 nframes_t fade_length;
1820 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1821 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1827 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1831 if (pos < (arv->region()->position() + 64)) {
1832 fade_length = 64; // this should be a minimum defined somewhere
1833 } else if (pos > arv->region()->last_frame()) {
1834 fade_length = arv->region()->length();
1836 fade_length = pos - arv->region()->position();
1838 /* mapover the region selection */
1840 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1842 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1848 tmp->reset_fade_in_shape_width (fade_length);
1851 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1853 drag_info.first_move = false;
1857 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1859 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1861 nframes_t fade_length;
1863 if (drag_info.first_move) return;
1865 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1866 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1871 if (pos < (arv->region()->position() + 64)) {
1872 fade_length = 64; // this should be a minimum defined somewhere
1873 } else if (pos > arv->region()->last_frame()) {
1874 fade_length = arv->region()->length();
1876 fade_length = pos - arv->region()->position();
1879 begin_reversible_command (_("change fade in length"));
1881 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1883 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1889 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
1890 XMLNode &before = alist->get_state();
1892 tmp->audio_region()->set_fade_in_length (fade_length);
1894 XMLNode &after = alist->get_state();
1895 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1898 commit_reversible_command ();
1902 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1904 drag_info.item = item;
1905 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1906 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1910 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1911 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1915 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1917 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
1921 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1923 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1925 nframes_t fade_length;
1927 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1928 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1933 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1937 if (pos > (arv->region()->last_frame() - 64)) {
1938 fade_length = 64; // this should really be a minimum fade defined somewhere
1940 else if (pos < arv->region()->position()) {
1941 fade_length = arv->region()->length();
1944 fade_length = arv->region()->last_frame() - pos;
1947 /* mapover the region selection */
1949 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1951 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1957 tmp->reset_fade_out_shape_width (fade_length);
1960 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1962 drag_info.first_move = false;
1966 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1968 if (drag_info.first_move) return;
1970 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1972 nframes_t fade_length;
1974 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1975 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1981 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1985 if (pos > (arv->region()->last_frame() - 64)) {
1986 fade_length = 64; // this should really be a minimum fade defined somewhere
1988 else if (pos < arv->region()->position()) {
1989 fade_length = arv->region()->length();
1992 fade_length = arv->region()->last_frame() - pos;
1995 begin_reversible_command (_("change fade out length"));
1997 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1999 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2005 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2006 XMLNode &before = alist->get_state();
2008 tmp->audio_region()->set_fade_out_length (fade_length);
2010 XMLNode &after = alist->get_state();
2011 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2014 commit_reversible_command ();
2018 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2020 drag_info.item = item;
2021 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2022 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2026 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2027 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2031 Cursor* cursor = (Cursor *) drag_info.data;
2033 if (cursor == playhead_cursor) {
2034 _dragging_playhead = true;
2036 if (session && drag_info.was_rolling) {
2037 session->request_stop ();
2040 if (session && session->is_auditioning()) {
2041 session->cancel_audition ();
2045 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2047 show_verbose_time_cursor (cursor->current_frame, 10);
2051 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2053 Cursor* cursor = (Cursor *) drag_info.data;
2054 nframes_t adjusted_frame;
2056 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2057 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2063 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2064 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
2065 snap_to (adjusted_frame);
2069 if (adjusted_frame == drag_info.last_pointer_frame) return;
2071 cursor->set_position (adjusted_frame);
2073 if (cursor == edit_cursor) {
2074 edit_cursor_clock.set (cursor->current_frame);
2076 UpdateAllTransportClocks (cursor->current_frame);
2079 show_verbose_time_cursor (cursor->current_frame, 10);
2081 drag_info.last_pointer_frame = adjusted_frame;
2082 drag_info.first_move = false;
2086 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2088 if (drag_info.first_move) return;
2090 cursor_drag_motion_callback (item, event);
2092 _dragging_playhead = false;
2094 if (item == &playhead_cursor->canvas_item) {
2096 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2098 } else if (item == &edit_cursor->canvas_item) {
2099 edit_cursor->set_position (edit_cursor->current_frame);
2100 edit_cursor_clock.set (edit_cursor->current_frame);
2105 Editor::update_marker_drag_item (Location *location)
2107 double x1 = frame_to_pixel (location->start());
2108 double x2 = frame_to_pixel (location->end());
2110 if (location->is_mark()) {
2111 marker_drag_line_points.front().set_x(x1);
2112 marker_drag_line_points.back().set_x(x1);
2113 marker_drag_line->property_points() = marker_drag_line_points;
2116 range_marker_drag_rect->property_x1() = x1;
2117 range_marker_drag_rect->property_x2() = x2;
2122 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2126 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2127 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2133 Location *location = find_location_from_marker (marker, is_start);
2135 drag_info.item = item;
2136 drag_info.data = marker;
2137 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2138 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2142 drag_info.copied_location = new Location (*location);
2143 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2145 update_marker_drag_item (location);
2147 if (location->is_mark()) {
2148 marker_drag_line->show();
2149 marker_drag_line->raise_to_top();
2152 range_marker_drag_rect->show();
2153 range_marker_drag_rect->raise_to_top();
2156 if (is_start) show_verbose_time_cursor (location->start(), 10);
2157 else show_verbose_time_cursor (location->end(), 10);
2161 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2164 Marker* marker = (Marker *) drag_info.data;
2165 Location *real_location;
2166 Location *copy_location;
2168 bool move_both = false;
2172 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2173 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2178 nframes_t next = newframe;
2180 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2181 snap_to (newframe, 0, true);
2184 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2188 /* call this to find out if its the start or end */
2190 real_location = find_location_from_marker (marker, is_start);
2192 /* use the copy that we're "dragging" around */
2194 copy_location = drag_info.copied_location;
2196 f_delta = copy_location->end() - copy_location->start();
2198 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2202 if (copy_location->is_mark()) {
2205 copy_location->set_start (newframe);
2209 if (is_start) { // start-of-range marker
2212 copy_location->set_start (newframe);
2213 copy_location->set_end (newframe + f_delta);
2214 } else if (newframe < copy_location->end()) {
2215 copy_location->set_start (newframe);
2217 snap_to (next, 1, true);
2218 copy_location->set_end (next);
2219 copy_location->set_start (newframe);
2222 } else { // end marker
2225 copy_location->set_end (newframe);
2226 copy_location->set_start (newframe - f_delta);
2227 } else if (newframe > copy_location->start()) {
2228 copy_location->set_end (newframe);
2230 } else if (newframe > 0) {
2231 snap_to (next, -1, true);
2232 copy_location->set_start (next);
2233 copy_location->set_end (newframe);
2238 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2239 drag_info.first_move = false;
2241 update_marker_drag_item (copy_location);
2243 LocationMarkers* lm = find_location_markers (real_location);
2244 lm->set_position (copy_location->start(), copy_location->end());
2246 show_verbose_time_cursor (newframe, 10);
2250 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2252 if (drag_info.first_move) {
2253 marker_drag_motion_callback (item, event);
2257 Marker* marker = (Marker *) drag_info.data;
2261 begin_reversible_command ( _("move marker") );
2262 XMLNode &before = session->locations()->get_state();
2264 Location * location = find_location_from_marker (marker, is_start);
2267 if (location->is_mark()) {
2268 location->set_start (drag_info.copied_location->start());
2270 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2274 XMLNode &after = session->locations()->get_state();
2275 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2276 commit_reversible_command ();
2278 marker_drag_line->hide();
2279 range_marker_drag_rect->hide();
2283 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2286 MeterMarker* meter_marker;
2288 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2289 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2293 meter_marker = dynamic_cast<MeterMarker*> (marker);
2295 MetricSection& section (meter_marker->meter());
2297 if (!section.movable()) {
2301 drag_info.item = item;
2302 drag_info.data = marker;
2303 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2304 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2308 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2310 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2314 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2317 MeterMarker* meter_marker;
2319 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2320 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2324 meter_marker = dynamic_cast<MeterMarker*> (marker);
2326 // create a dummy marker for visual representation of moving the copy.
2327 // The actual copying is not done before we reach the finish callback.
2329 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2330 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2331 *new MeterSection(meter_marker->meter()));
2333 drag_info.item = &new_marker->the_item();
2334 drag_info.copy = true;
2335 drag_info.data = new_marker;
2336 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2337 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2341 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2343 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2347 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2349 MeterMarker* marker = (MeterMarker *) drag_info.data;
2350 nframes_t adjusted_frame;
2352 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2353 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2359 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2360 snap_to (adjusted_frame);
2363 if (adjusted_frame == drag_info.last_pointer_frame) return;
2365 marker->set_position (adjusted_frame);
2368 drag_info.last_pointer_frame = adjusted_frame;
2369 drag_info.first_move = false;
2371 show_verbose_time_cursor (adjusted_frame, 10);
2375 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2377 if (drag_info.first_move) return;
2379 meter_marker_drag_motion_callback (drag_info.item, event);
2381 MeterMarker* marker = (MeterMarker *) drag_info.data;
2384 TempoMap& map (session->tempo_map());
2385 map.bbt_time (drag_info.last_pointer_frame, when);
2387 if (drag_info.copy == true) {
2388 begin_reversible_command (_("copy meter mark"));
2389 XMLNode &before = map.get_state();
2390 map.add_meter (marker->meter(), when);
2391 XMLNode &after = map.get_state();
2392 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2393 commit_reversible_command ();
2395 // delete the dummy marker we used for visual representation of copying.
2396 // a new visual marker will show up automatically.
2399 begin_reversible_command (_("move meter mark"));
2400 XMLNode &before = map.get_state();
2401 map.move_meter (marker->meter(), when);
2402 XMLNode &after = map.get_state();
2403 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2404 commit_reversible_command ();
2409 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2412 TempoMarker* tempo_marker;
2414 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2415 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2419 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2420 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2424 MetricSection& section (tempo_marker->tempo());
2426 if (!section.movable()) {
2430 drag_info.item = item;
2431 drag_info.data = marker;
2432 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2433 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2437 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2438 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2442 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2445 TempoMarker* tempo_marker;
2447 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2448 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2452 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2453 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2457 // create a dummy marker for visual representation of moving the copy.
2458 // The actual copying is not done before we reach the finish callback.
2460 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2461 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2462 *new TempoSection(tempo_marker->tempo()));
2464 drag_info.item = &new_marker->the_item();
2465 drag_info.copy = true;
2466 drag_info.data = new_marker;
2467 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2468 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2472 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2474 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2478 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2480 TempoMarker* marker = (TempoMarker *) drag_info.data;
2481 nframes_t adjusted_frame;
2483 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2484 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2490 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2491 snap_to (adjusted_frame);
2494 if (adjusted_frame == drag_info.last_pointer_frame) return;
2496 /* OK, we've moved far enough to make it worth actually move the thing. */
2498 marker->set_position (adjusted_frame);
2500 show_verbose_time_cursor (adjusted_frame, 10);
2502 drag_info.last_pointer_frame = adjusted_frame;
2503 drag_info.first_move = false;
2507 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2509 if (drag_info.first_move) return;
2511 tempo_marker_drag_motion_callback (drag_info.item, event);
2513 TempoMarker* marker = (TempoMarker *) drag_info.data;
2516 TempoMap& map (session->tempo_map());
2517 map.bbt_time (drag_info.last_pointer_frame, when);
2519 if (drag_info.copy == true) {
2520 begin_reversible_command (_("copy tempo mark"));
2521 XMLNode &before = map.get_state();
2522 map.add_tempo (marker->tempo(), when);
2523 XMLNode &after = map.get_state();
2524 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2525 commit_reversible_command ();
2527 // delete the dummy marker we used for visual representation of copying.
2528 // a new visual marker will show up automatically.
2531 begin_reversible_command (_("move tempo mark"));
2532 XMLNode &before = map.get_state();
2533 map.move_tempo (marker->tempo(), when);
2534 XMLNode &after = map.get_state();
2535 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2536 commit_reversible_command ();
2541 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2543 ControlPoint* control_point;
2545 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2546 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2550 // We shouldn't remove the first or last gain point
2551 if (control_point->line().is_last_point(*control_point) ||
2552 control_point->line().is_first_point(*control_point)) {
2556 control_point->line().remove_point (*control_point);
2560 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2562 ControlPoint* control_point;
2564 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2565 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2569 control_point->line().remove_point (*control_point);
2573 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2575 ControlPoint* control_point;
2577 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2578 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2582 drag_info.item = item;
2583 drag_info.data = control_point;
2584 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2585 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2587 start_grab (event, fader_cursor);
2589 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2591 double fraction = 1.0 - ((control_point->get_y() - control_point->line().y_position()) / (double)control_point->line().height());
2592 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
2593 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2595 show_verbose_canvas_cursor ();
2599 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2601 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2603 double cx = drag_info.current_pointer_x;
2604 double cy = drag_info.current_pointer_y;
2606 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2607 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2609 if (drag_info.x_constrained) {
2610 cx = drag_info.grab_x;
2612 if (drag_info.y_constrained) {
2613 cy = drag_info.grab_y;
2616 cp->line().parent_group().w2i (cx, cy);
2620 cy = min ((double) (cp->line().y_position() + cp->line().height()), cy);
2622 //translate cx to frames
2623 nframes_t cx_frames = unit_to_frame (cx);
2625 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2626 snap_to (cx_frames);
2629 const double fraction = 1.0 - ((cy - cp->line().y_position()) / (double)cp->line().height());
2633 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2639 cp->line().point_drag (*cp, cx_frames , fraction, push);
2641 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
2643 drag_info.first_move = false;
2647 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2649 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2651 if (drag_info.first_move) {
2655 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2656 reset_point_selection ();
2660 control_point_drag_motion_callback (item, event);
2662 cp->line().end_drag (cp);
2666 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2668 switch (mouse_mode) {
2670 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2671 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2679 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2683 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2684 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2688 start_line_grab (al, event);
2692 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2696 nframes_t frame_within_region;
2698 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2702 cx = event->button.x;
2703 cy = event->button.y;
2704 line->parent_group().w2i (cx, cy);
2705 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2707 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2708 current_line_drag_info.after)) {
2709 /* no adjacent points */
2713 drag_info.item = &line->grab_item();
2714 drag_info.data = line;
2715 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2716 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2718 start_grab (event, fader_cursor);
2720 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2722 line->start_drag (0, drag_info.grab_frame, fraction);
2724 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2725 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2726 show_verbose_canvas_cursor ();
2730 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2732 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2733 double cx = drag_info.current_pointer_x;
2734 double cy = drag_info.current_pointer_y;
2736 line->parent_group().w2i (cx, cy);
2738 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2742 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2748 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2750 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2754 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2756 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2757 line_drag_motion_callback (item, event);
2762 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2764 if (selection->regions.empty() || clicked_regionview == 0) {
2768 drag_info.copy = false;
2769 drag_info.item = item;
2770 drag_info.data = clicked_regionview;
2771 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2772 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2777 TimeAxisView* tvp = clicked_axisview;
2778 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2780 if (tv && tv->is_track()) {
2781 speed = tv->get_diskstream()->speed();
2784 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2785 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2786 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2787 // we want a move threshold
2788 drag_info.want_move_threshold = true;
2790 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2792 begin_reversible_command (_("move region(s)"));
2796 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2798 if (selection->regions.empty() || clicked_regionview == 0) {
2802 drag_info.copy = true;
2803 drag_info.item = item;
2804 drag_info.data = clicked_regionview;
2808 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2809 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2812 if (rtv && rtv->is_track()) {
2813 speed = rtv->get_diskstream()->speed();
2816 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2817 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2818 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2819 // we want a move threshold
2820 drag_info.want_move_threshold = true;
2821 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2822 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2823 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2827 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2829 if (selection->regions.empty() || clicked_regionview == 0) {
2833 drag_info.copy = false;
2834 drag_info.item = item;
2835 drag_info.data = clicked_regionview;
2836 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2837 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2842 TimeAxisView* tvp = clicked_axisview;
2843 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2845 if (tv && tv->is_track()) {
2846 speed = tv->get_diskstream()->speed();
2849 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2850 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2851 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2852 // we want a move threshold
2853 drag_info.want_move_threshold = true;
2854 drag_info.brushing = true;
2856 begin_reversible_command (_("Drag region brush"));
2860 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2864 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2865 nframes_t pending_region_position = 0;
2866 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2867 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2868 bool clamp_y_axis = false;
2869 vector<int32_t> height_list(512) ;
2870 vector<int32_t>::iterator j;
2872 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2874 drag_info.want_move_threshold = false; // don't copy again
2876 /* duplicate the region(s) */
2878 vector<RegionView*> new_regionviews;
2880 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2886 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2887 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2890 nrv = new AudioRegionView (*arv);
2892 nrv = new MidiRegionView (*mrv);
2897 nrv->get_canvas_group()->show ();
2899 new_regionviews.push_back (nrv);
2902 if (new_regionviews.empty()) {
2906 /* reset selection to new regionviews */
2908 selection->set (new_regionviews);
2910 /* reset drag_info data to reflect the fact that we are dragging the copies */
2912 drag_info.data = new_regionviews.front();
2914 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2917 /* Which trackview is this ? */
2919 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2920 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2922 /* The region motion is only processed if the pointer is over
2926 if (!tv || !tv->is_track()) {
2927 /* To make sure we hide the verbose canvas cursor when the mouse is
2928 not held over a track.
2930 hide_verbose_canvas_cursor ();
2934 original_pointer_order = drag_info.last_trackview->order;
2936 /************************************************************
2938 ************************************************************/
2940 if (drag_info.brushing) {
2941 clamp_y_axis = true;
2946 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2948 int32_t children = 0, numtracks = 0;
2949 // XXX hard coding track limit, oh my, so very very bad
2950 bitset <1024> tracks (0x00);
2951 /* get a bitmask representing the visible tracks */
2953 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2954 TimeAxisView *tracklist_timeview;
2955 tracklist_timeview = (*i);
2956 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2957 TimeAxisView::Children children_list;
2959 /* zeroes are audio tracks. ones are other types. */
2961 if (!rtv2->hidden()) {
2963 if (visible_y_high < rtv2->order) {
2964 visible_y_high = rtv2->order;
2966 if (visible_y_low > rtv2->order) {
2967 visible_y_low = rtv2->order;
2970 if (!rtv2->is_track()) {
2971 tracks = tracks |= (0x01 << rtv2->order);
2974 height_list[rtv2->order] = (*i)->height;
2976 if ((children_list = rtv2->get_child_list()).size() > 0) {
2977 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2978 tracks = tracks |= (0x01 << (rtv2->order + children));
2979 height_list[rtv2->order + children] = (*j)->height;
2987 /* find the actual span according to the canvas */
2989 canvas_pointer_y_span = pointer_y_span;
2990 if (drag_info.last_trackview->order >= tv->order) {
2992 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2993 if (height_list[y] == 0 ) {
2994 canvas_pointer_y_span--;
2999 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3000 if ( height_list[y] == 0 ) {
3001 canvas_pointer_y_span++;
3006 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3007 RegionView* rv2 = (*i);
3008 double ix1, ix2, iy1, iy2;
3011 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3012 rv2->get_canvas_group()->i2w (ix1, iy1);
3013 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3014 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3016 if (rtv2->order != original_pointer_order) {
3017 /* this isn't the pointer track */
3019 if (canvas_pointer_y_span > 0) {
3021 /* moving up the canvas */
3022 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3024 int32_t visible_tracks = 0;
3025 while (visible_tracks < canvas_pointer_y_span ) {
3028 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3029 /* we're passing through a hidden track */
3034 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3035 clamp_y_axis = true;
3039 clamp_y_axis = true;
3042 } else if (canvas_pointer_y_span < 0) {
3044 /*moving down the canvas*/
3046 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3049 int32_t visible_tracks = 0;
3051 while (visible_tracks > canvas_pointer_y_span ) {
3054 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3058 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3059 clamp_y_axis = true;
3064 clamp_y_axis = true;
3070 /* this is the pointer's track */
3071 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3072 clamp_y_axis = true;
3073 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3074 clamp_y_axis = true;
3082 } else if (drag_info.last_trackview == tv) {
3083 clamp_y_axis = true;
3087 if (!clamp_y_axis) {
3088 drag_info.last_trackview = tv;
3091 /************************************************************
3093 ************************************************************/
3095 /* compute the amount of pointer motion in frames, and where
3096 the region would be if we moved it by that much.
3099 if (drag_info.move_threshold_passed) {
3101 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3103 nframes_t sync_frame;
3104 nframes_t sync_offset;
3107 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3109 sync_offset = rv->region()->sync_offset (sync_dir);
3110 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3112 /* we snap if the snap modifier is not enabled.
3115 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3116 snap_to (sync_frame);
3119 if (sync_frame - sync_offset <= sync_frame) {
3120 pending_region_position = sync_frame - (sync_dir*sync_offset);
3122 pending_region_position = 0;
3126 pending_region_position = 0;
3129 if (pending_region_position > max_frames - rv->region()->length()) {
3130 pending_region_position = drag_info.last_frame_position;
3133 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3135 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3137 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3138 to make it appear at the new location.
3141 if (pending_region_position > drag_info.last_frame_position) {
3142 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3144 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3147 drag_info.last_frame_position = pending_region_position;
3154 /* threshold not passed */
3159 /*************************************************************
3161 ************************************************************/
3163 if (x_delta == 0 && (pointer_y_span == 0)) {
3164 /* haven't reached next snap point, and we're not switching
3165 trackviews. nothing to do.
3172 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3174 RegionView* rv2 = (*i);
3176 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3178 double ix1, ix2, iy1, iy2;
3179 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3180 rv2->get_canvas_group()->i2w (ix1, iy1);
3189 /*************************************************************
3191 ************************************************************/
3195 if (drag_info.first_move) {
3196 if (drag_info.move_threshold_passed) {
3207 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3208 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3210 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3212 RegionView* rv = (*i);
3213 double ix1, ix2, iy1, iy2;
3214 int32_t temp_pointer_y_span = pointer_y_span;
3216 /* get item BBox, which will be relative to parent. so we have
3217 to query on a child, then convert to world coordinates using
3221 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3222 rv->get_canvas_group()->i2w (ix1, iy1);
3223 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3224 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3225 RouteTimeAxisView* temp_rtv;
3227 if ((pointer_y_span != 0) && !clamp_y_axis) {
3230 for (j = height_list.begin(); j!= height_list.end(); j++) {
3231 if (x == canvas_rtv->order) {
3232 /* we found the track the region is on */
3233 if (x != original_pointer_order) {
3234 /*this isn't from the same track we're dragging from */
3235 temp_pointer_y_span = canvas_pointer_y_span;
3237 while (temp_pointer_y_span > 0) {
3238 /* we're moving up canvas-wise,
3239 so we need to find the next track height
3241 if (j != height_list.begin()) {
3244 if (x != original_pointer_order) {
3245 /* we're not from the dragged track, so ignore hidden tracks. */
3247 temp_pointer_y_span++;
3251 temp_pointer_y_span--;
3253 while (temp_pointer_y_span < 0) {
3255 if (x != original_pointer_order) {
3257 temp_pointer_y_span--;
3261 if (j != height_list.end()) {
3264 temp_pointer_y_span++;
3266 /* find out where we'll be when we move and set height accordingly */
3268 tvp2 = trackview_by_y_position (iy1 + y_delta);
3269 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3270 rv->set_y_position_and_height (0, temp_rtv->height);
3272 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3273 personally, i think this can confuse things, but never mind.
3276 //const GdkColor& col (temp_rtv->view->get_region_color());
3277 //rv->set_color (const_cast<GdkColor&>(col));
3284 /* prevent the regionview from being moved to before
3285 the zero position on the canvas.
3290 if (-x_delta > ix1) {
3293 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3294 x_delta = max_frames - rv->region()->last_frame();
3298 if (drag_info.first_move) {
3300 /* hide any dependent views */
3302 rv->get_time_axis_view().hide_dependent_views (*rv);
3304 /* this is subtle. raising the regionview itself won't help,
3305 because raise_to_top() just puts the item on the top of
3306 its parent's stack. so, we need to put the trackview canvas_display group
3307 on the top, since its parent is the whole canvas.
3310 rv->get_canvas_group()->raise_to_top();
3311 rv->get_time_axis_view().canvas_display->raise_to_top();
3312 cursor_group->raise_to_top();
3314 rv->fake_set_opaque (true);
3317 if (drag_info.brushing) {
3318 mouse_brush_insert_region (rv, pending_region_position);
3320 rv->move (x_delta, y_delta);
3323 } /* foreach region */
3327 if (drag_info.first_move && drag_info.move_threshold_passed) {
3328 cursor_group->raise_to_top();
3329 drag_info.first_move = false;
3332 if (x_delta != 0 && !drag_info.brushing) {
3333 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3338 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3341 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3342 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3343 bool nocommit = true;
3345 RouteTimeAxisView* rtv;
3346 bool regionview_y_movement;
3347 bool regionview_x_movement;
3348 vector<RegionView*> copies;
3350 /* first_move is set to false if the regionview has been moved in the
3354 if (drag_info.first_move) {
3361 /* The regionview has been moved at some stage during the grab so we need
3362 to account for any mouse movement between this event and the last one.
3365 region_drag_motion_callback (item, event);
3367 if (drag_info.brushing) {
3368 /* all changes were made during motion event handlers */
3370 if (drag_info.copy) {
3371 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3372 copies.push_back (*i);
3379 /* adjust for track speed */
3382 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3383 if (rtv && rtv->get_diskstream()) {
3384 speed = rtv->get_diskstream()->speed();
3387 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3388 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3390 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3391 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3395 if (drag_info.copy) {
3396 if (drag_info.x_constrained) {
3397 op_string = _("fixed time region copy");
3399 op_string = _("region copy");
3402 if (drag_info.x_constrained) {
3403 op_string = _("fixed time region drag");
3405 op_string = _("region drag");
3409 begin_reversible_command (op_string);
3411 if (regionview_y_movement) {
3413 /* moved to a different audio track. */
3415 vector<RegionView*> new_selection;
3417 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3419 RegionView* rv = (*i);
3421 double ix1, ix2, iy1, iy2;
3423 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3424 rv->get_canvas_group()->i2w (ix1, iy1);
3425 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3426 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3428 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3429 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3431 where = (nframes_t) (unit_to_frame (ix1) * speed);
3432 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3434 /* undo the previous hide_dependent_views so that xfades don't
3435 disappear on copying regions
3438 rv->get_time_axis_view().reveal_dependent_views (*rv);
3440 if (!drag_info.copy) {
3442 /* the region that used to be in the old playlist is not
3443 moved to the new one - we make a copy of it. as a result,
3444 any existing editor for the region should no longer be
3448 rv->hide_region_editor();
3449 rv->fake_set_opaque (false);
3451 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3452 from_playlist->remove_region ((rv->region()));
3453 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3457 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3459 copies.push_back (rv);
3462 latest_regionview = 0;
3464 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3465 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3466 to_playlist->add_region (new_region, where);
3467 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3470 if (latest_regionview) {
3471 new_selection.push_back (latest_regionview);
3474 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3475 was selected in all of them, then removing it from the playlist will have removed all
3476 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3477 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3478 corresponding regionview, and the selection is now empty).
3480 this could have invalidated any and all iterators into the region selection.
3482 the heuristic we use here is: if the region selection is empty, break out of the loop
3483 here. if the region selection is not empty, then restart the loop because we know that
3484 we must have removed at least the region(view) we've just been working on as well as any
3485 that we processed on previous iterations.
3487 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3488 we can just iterate.
3491 if (drag_info.copy) {
3494 if (selection->regions.empty()) {
3497 i = selection->regions.by_layer().begin();
3502 selection->set (new_selection);
3506 /* motion within a single track */
3508 list<RegionView*> regions = selection->regions.by_layer();
3510 if (drag_info.copy) {
3511 selection->clear_regions();
3514 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3518 if (!rv->region()->can_move()) {
3522 if (regionview_x_movement) {
3523 double ownspeed = 1.0;
3524 rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3526 if (rtv && rtv->get_diskstream()) {
3527 ownspeed = rtv->get_diskstream()->speed();
3530 /* base the new region position on the current position of the regionview.*/
3532 double ix1, ix2, iy1, iy2;
3534 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3535 rv->get_canvas_group()->i2w (ix1, iy1);
3536 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3540 where = rv->region()->position();
3543 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3545 assert (to_playlist);
3549 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3551 if (drag_info.copy) {
3553 boost::shared_ptr<Region> newregion;
3554 boost::shared_ptr<Region> ar;
3555 boost::shared_ptr<Region> mr;
3557 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3558 newregion = RegionFactory::create (ar);
3559 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3560 newregion = RegionFactory::create (mr);
3565 latest_regionview = 0;
3566 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3567 to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
3570 if (latest_regionview) {
3571 rtv->reveal_dependent_views (*latest_regionview);
3572 selection->add (latest_regionview);
3575 /* if the original region was locked, we don't care for the new one */
3577 newregion->set_locked (false);
3581 /* just change the model */
3583 rv->region()->set_position (where, (void*) this);
3589 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3591 if (drag_info.copy) {
3592 copies.push_back (rv);
3600 commit_reversible_command ();
3603 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3609 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3611 /* Either add to or set the set the region selection, unless
3612 this is an alignment click (control used)
3615 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3616 TimeAxisView* tv = &rv.get_time_axis_view();
3617 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3619 if (rtv && rtv->is_track()) {
3620 speed = rtv->get_diskstream()->speed();
3623 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3625 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3627 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3629 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3633 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3639 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3645 nframes_t frame_rate;
3652 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3653 case AudioClock::BBT:
3654 session->bbt_time (frame, bbt);
3655 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3658 case AudioClock::SMPTE:
3659 session->smpte_time (frame, smpte);
3660 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3663 case AudioClock::MinSec:
3664 /* XXX this is copied from show_verbose_duration_cursor() */
3665 frame_rate = session->frame_rate();
3666 hours = frame / (frame_rate * 3600);
3667 frame = frame % (frame_rate * 3600);
3668 mins = frame / (frame_rate * 60);
3669 frame = frame % (frame_rate * 60);
3670 secs = (float) frame / (float) frame_rate;
3671 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3675 snprintf (buf, sizeof(buf), "%u", frame);
3679 if (xpos >= 0 && ypos >=0) {
3680 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3683 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3685 show_verbose_canvas_cursor ();
3689 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3696 nframes_t distance, frame_rate;
3698 Meter meter_at_start(session->tempo_map().meter_at(start));
3704 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3705 case AudioClock::BBT:
3706 session->bbt_time (start, sbbt);
3707 session->bbt_time (end, ebbt);
3710 /* XXX this computation won't work well if the
3711 user makes a selection that spans any meter changes.
3714 ebbt.bars -= sbbt.bars;
3715 if (ebbt.beats >= sbbt.beats) {
3716 ebbt.beats -= sbbt.beats;
3719 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3721 if (ebbt.ticks >= sbbt.ticks) {
3722 ebbt.ticks -= sbbt.ticks;
3725 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3728 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3731 case AudioClock::SMPTE:
3732 session->smpte_duration (end - start, smpte);
3733 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3736 case AudioClock::MinSec:
3737 /* XXX this stuff should be elsewhere.. */
3738 distance = end - start;
3739 frame_rate = session->frame_rate();
3740 hours = distance / (frame_rate * 3600);
3741 distance = distance % (frame_rate * 3600);
3742 mins = distance / (frame_rate * 60);
3743 distance = distance % (frame_rate * 60);
3744 secs = (float) distance / (float) frame_rate;
3745 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3749 snprintf (buf, sizeof(buf), "%u", end - start);
3753 if (xpos >= 0 && ypos >=0) {
3754 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3757 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3759 show_verbose_canvas_cursor ();
3763 Editor::collect_new_region_view (RegionView* rv)
3765 latest_regionview = rv;
3769 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3771 if (clicked_regionview == 0) {
3775 /* lets try to create new Region for the selection */
3777 vector<boost::shared_ptr<AudioRegion> > new_regions;
3778 create_region_from_selection (new_regions);
3780 if (new_regions.empty()) {
3784 /* XXX fix me one day to use all new regions */
3786 boost::shared_ptr<Region> region (new_regions.front());
3788 /* add it to the current stream/playlist.
3790 tricky: the streamview for the track will add a new regionview. we will
3791 catch the signal it sends when it creates the regionview to
3792 set the regionview we want to then drag.
3795 latest_regionview = 0;
3796 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3798 /* A selection grab currently creates two undo/redo operations, one for
3799 creating the new region and another for moving it.
3802 begin_reversible_command (_("selection grab"));
3804 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3806 XMLNode *before = &(playlist->get_state());
3807 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3808 XMLNode *after = &(playlist->get_state());
3809 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3811 commit_reversible_command ();
3815 if (latest_regionview == 0) {
3816 /* something went wrong */
3820 /* we need to deselect all other regionviews, and select this one
3821 i'm ignoring undo stuff, because the region creation will take care of it */
3822 selection->set (latest_regionview);
3824 drag_info.item = latest_regionview->get_canvas_group();
3825 drag_info.data = latest_regionview;
3826 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3827 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3831 drag_info.last_trackview = clicked_axisview;
3832 drag_info.last_frame_position = latest_regionview->region()->position();
3833 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3835 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3839 Editor::cancel_selection ()
3841 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3842 (*i)->hide_selection ();
3844 begin_reversible_command (_("cancel selection"));
3845 selection->clear ();
3846 clicked_selection = 0;
3847 commit_reversible_command ();
3851 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3853 nframes_t start = 0;
3860 drag_info.item = item;
3861 drag_info.motion_callback = &Editor::drag_selection;
3862 drag_info.finished_callback = &Editor::end_selection_op;
3867 case CreateSelection:
3868 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3869 drag_info.copy = true;
3871 drag_info.copy = false;
3873 start_grab (event, selector_cursor);
3876 case SelectionStartTrim:
3877 if (clicked_axisview) {
3878 clicked_axisview->order_selection_trims (item, true);
3880 start_grab (event, trimmer_cursor);
3881 start = selection->time[clicked_selection].start;
3882 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3885 case SelectionEndTrim:
3886 if (clicked_axisview) {
3887 clicked_axisview->order_selection_trims (item, false);
3889 start_grab (event, trimmer_cursor);
3890 end = selection->time[clicked_selection].end;
3891 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3895 start = selection->time[clicked_selection].start;
3897 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3901 if (selection_op == SelectionMove) {
3902 show_verbose_time_cursor(start, 10);
3904 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3909 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3911 nframes_t start = 0;
3914 nframes_t pending_position;
3916 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3917 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3919 pending_position = 0;
3922 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3923 snap_to (pending_position);
3926 /* only alter selection if the current frame is
3927 different from the last frame position (adjusted)
3930 if (pending_position == drag_info.last_pointer_frame) return;
3932 switch (selection_op) {
3933 case CreateSelection:
3935 if (drag_info.first_move) {
3936 snap_to (drag_info.grab_frame);
3939 if (pending_position < drag_info.grab_frame) {
3940 start = pending_position;
3941 end = drag_info.grab_frame;
3943 end = pending_position;
3944 start = drag_info.grab_frame;
3947 /* first drag: Either add to the selection
3948 or create a new selection->
3951 if (drag_info.first_move) {
3953 begin_reversible_command (_("range selection"));
3955 if (drag_info.copy) {
3956 /* adding to the selection */
3957 clicked_selection = selection->add (start, end);
3958 drag_info.copy = false;
3960 /* new selection-> */
3961 clicked_selection = selection->set (clicked_axisview, start, end);
3966 case SelectionStartTrim:
3968 if (drag_info.first_move) {
3969 begin_reversible_command (_("trim selection start"));
3972 start = selection->time[clicked_selection].start;
3973 end = selection->time[clicked_selection].end;
3975 if (pending_position > end) {
3978 start = pending_position;
3982 case SelectionEndTrim:
3984 if (drag_info.first_move) {
3985 begin_reversible_command (_("trim selection end"));
3988 start = selection->time[clicked_selection].start;
3989 end = selection->time[clicked_selection].end;
3991 if (pending_position < start) {
3994 end = pending_position;
4001 if (drag_info.first_move) {
4002 begin_reversible_command (_("move selection"));
4005 start = selection->time[clicked_selection].start;
4006 end = selection->time[clicked_selection].end;
4008 length = end - start;
4010 start = pending_position;
4013 end = start + length;
4018 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4019 start_canvas_autoscroll (1);
4023 selection->replace (clicked_selection, start, end);
4026 drag_info.last_pointer_frame = pending_position;
4027 drag_info.first_move = false;
4029 if (selection_op == SelectionMove) {
4030 show_verbose_time_cursor(start, 10);
4032 show_verbose_time_cursor(pending_position, 10);
4037 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4039 if (!drag_info.first_move) {
4040 drag_selection (item, event);
4041 /* XXX this is not object-oriented programming at all. ick */
4042 if (selection->time.consolidate()) {
4043 selection->TimeChanged ();
4045 commit_reversible_command ();
4047 /* just a click, no pointer movement.*/
4049 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4051 selection->clear_time();
4056 /* XXX what happens if its a music selection? */
4057 session->set_audio_range (selection->time);
4058 stop_canvas_autoscroll ();
4062 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4065 TimeAxisView* tvp = clicked_axisview;
4066 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4068 if (tv && tv->is_track()) {
4069 speed = tv->get_diskstream()->speed();
4072 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4073 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4074 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4076 //drag_info.item = clicked_regionview->get_name_highlight();
4077 drag_info.item = item;
4078 drag_info.motion_callback = &Editor::trim_motion_callback;
4079 drag_info.finished_callback = &Editor::trim_finished_callback;
4081 start_grab (event, trimmer_cursor);
4083 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4084 trim_op = ContentsTrim;
4086 /* These will get overridden for a point trim.*/
4087 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4088 /* closer to start */
4089 trim_op = StartTrim;
4090 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4098 show_verbose_time_cursor(region_start, 10);
4101 show_verbose_time_cursor(region_end, 10);
4104 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4110 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4112 RegionView* rv = clicked_regionview;
4113 nframes_t frame_delta = 0;
4114 bool left_direction;
4115 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4117 /* snap modifier works differently here..
4118 its' current state has to be passed to the
4119 various trim functions in order to work properly
4123 TimeAxisView* tvp = clicked_axisview;
4124 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4125 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4127 if (tv && tv->is_track()) {
4128 speed = tv->get_diskstream()->speed();
4131 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4132 left_direction = true;
4134 left_direction = false;
4138 snap_to (drag_info.current_pointer_frame);
4141 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4145 if (drag_info.first_move) {
4151 trim_type = "Region start trim";
4154 trim_type = "Region end trim";
4157 trim_type = "Region content trim";
4161 begin_reversible_command (trim_type);
4163 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4164 (*i)->fake_set_opaque(false);
4165 (*i)->region()->freeze ();
4167 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4169 arv->temporarily_hide_envelope ();
4171 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4172 insert_result = motion_frozen_playlists.insert (pl);
4173 if (insert_result.second) {
4174 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4179 if (left_direction) {
4180 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4182 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4187 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4190 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4191 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4197 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4200 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4201 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4208 bool swap_direction = false;
4210 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4211 swap_direction = true;
4214 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4215 i != selection->regions.by_layer().end(); ++i)
4217 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4225 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4228 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4231 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4235 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4236 drag_info.first_move = false;
4240 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4242 boost::shared_ptr<Region> region (rv.region());
4244 if (region->locked()) {
4248 nframes_t new_bound;
4251 TimeAxisView* tvp = clicked_axisview;
4252 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4254 if (tv && tv->is_track()) {
4255 speed = tv->get_diskstream()->speed();
4258 if (left_direction) {
4259 if (swap_direction) {
4260 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4262 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4265 if (swap_direction) {
4266 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4268 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4273 snap_to (new_bound);
4275 region->trim_start ((nframes_t) (new_bound * speed), this);
4276 rv.region_changed (StartChanged);
4280 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4282 boost::shared_ptr<Region> region (rv.region());
4284 if (region->locked()) {
4288 nframes_t new_bound;
4291 TimeAxisView* tvp = clicked_axisview;
4292 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4294 if (tv && tv->is_track()) {
4295 speed = tv->get_diskstream()->speed();
4298 if (left_direction) {
4299 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4301 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4305 snap_to (new_bound, (left_direction ? 0 : 1));
4308 region->trim_front ((nframes_t) (new_bound * speed), this);
4310 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4314 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4316 boost::shared_ptr<Region> region (rv.region());
4318 if (region->locked()) {
4322 nframes_t new_bound;
4325 TimeAxisView* tvp = clicked_axisview;
4326 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4328 if (tv && tv->is_track()) {
4329 speed = tv->get_diskstream()->speed();
4332 if (left_direction) {
4333 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4335 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4339 snap_to (new_bound);
4341 region->trim_end ((nframes_t) (new_bound * speed), this);
4342 rv.region_changed (LengthChanged);
4346 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4348 if (!drag_info.first_move) {
4349 trim_motion_callback (item, event);
4351 if (!clicked_regionview->get_selected()) {
4352 thaw_region_after_trim (*clicked_regionview);
4355 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4356 i != selection->regions.by_layer().end(); ++i)
4358 thaw_region_after_trim (**i);
4359 (*i)->fake_set_opaque (true);
4363 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4365 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4368 motion_frozen_playlists.clear ();
4370 commit_reversible_command();
4372 /* no mouse movement */
4378 Editor::point_trim (GdkEvent* event)
4380 RegionView* rv = clicked_regionview;
4381 nframes_t new_bound = drag_info.current_pointer_frame;
4383 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4384 snap_to (new_bound);
4387 /* Choose action dependant on which button was pressed */
4388 switch (event->button.button) {
4390 trim_op = StartTrim;
4391 begin_reversible_command (_("Start point trim"));
4393 if (rv->get_selected()) {
4395 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4396 i != selection->regions.by_layer().end(); ++i)
4398 if (!(*i)->region()->locked()) {
4399 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4400 XMLNode &before = pl->get_state();
4401 (*i)->region()->trim_front (new_bound, this);
4402 XMLNode &after = pl->get_state();
4403 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4409 if (!rv->region()->locked()) {
4410 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4411 XMLNode &before = pl->get_state();
4412 rv->region()->trim_front (new_bound, this);
4413 XMLNode &after = pl->get_state();
4414 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4418 commit_reversible_command();
4423 begin_reversible_command (_("End point trim"));
4425 if (rv->get_selected()) {
4427 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4429 if (!(*i)->region()->locked()) {
4430 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4431 XMLNode &before = pl->get_state();
4432 (*i)->region()->trim_end (new_bound, this);
4433 XMLNode &after = pl->get_state();
4434 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4440 if (!rv->region()->locked()) {
4441 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4442 XMLNode &before = pl->get_state();
4443 rv->region()->trim_end (new_bound, this);
4444 XMLNode &after = pl->get_state();
4445 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4449 commit_reversible_command();
4458 Editor::thaw_region_after_trim (RegionView& rv)
4460 boost::shared_ptr<Region> region (rv.region());
4462 if (region->locked()) {
4466 region->thaw (_("trimmed region"));
4467 XMLNode &after = region->playlist()->get_state();
4468 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4470 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4472 arv->unhide_envelope ();
4476 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4481 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4482 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4486 Location* location = find_location_from_marker (marker, is_start);
4487 location->set_hidden (true, this);
4492 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4498 drag_info.item = item;
4499 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4500 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4502 range_marker_op = op;
4504 if (!temp_location) {
4505 temp_location = new Location;
4509 case CreateRangeMarker:
4510 case CreateTransportMarker:
4512 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4513 drag_info.copy = true;
4515 drag_info.copy = false;
4517 start_grab (event, selector_cursor);
4521 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4526 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4528 nframes_t start = 0;
4530 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4532 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4533 snap_to (drag_info.current_pointer_frame);
4536 /* only alter selection if the current frame is
4537 different from the last frame position.
4540 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4542 switch (range_marker_op) {
4543 case CreateRangeMarker:
4544 case CreateTransportMarker:
4545 if (drag_info.first_move) {
4546 snap_to (drag_info.grab_frame);
4549 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4550 start = drag_info.current_pointer_frame;
4551 end = drag_info.grab_frame;
4553 end = drag_info.current_pointer_frame;
4554 start = drag_info.grab_frame;
4557 /* first drag: Either add to the selection
4558 or create a new selection.
4561 if (drag_info.first_move) {
4563 temp_location->set (start, end);
4567 update_marker_drag_item (temp_location);
4568 range_marker_drag_rect->show();
4569 range_marker_drag_rect->raise_to_top();
4575 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4576 start_canvas_autoscroll (1);
4580 temp_location->set (start, end);
4582 double x1 = frame_to_pixel (start);
4583 double x2 = frame_to_pixel (end);
4584 crect->property_x1() = x1;
4585 crect->property_x2() = x2;
4587 update_marker_drag_item (temp_location);
4590 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4591 drag_info.first_move = false;
4593 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4598 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4600 Location * newloc = 0;
4603 if (!drag_info.first_move) {
4604 drag_range_markerbar_op (item, event);
4606 switch (range_marker_op) {
4607 case CreateRangeMarker:
4609 begin_reversible_command (_("new range marker"));
4610 XMLNode &before = session->locations()->get_state();
4611 session->locations()->next_available_name(rangename,"unnamed");
4612 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4613 session->locations()->add (newloc, true);
4614 XMLNode &after = session->locations()->get_state();
4615 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4616 commit_reversible_command ();
4618 range_bar_drag_rect->hide();
4619 range_marker_drag_rect->hide();
4623 case CreateTransportMarker:
4624 // popup menu to pick loop or punch
4625 new_transport_marker_context_menu (&event->button, item);
4630 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4632 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4637 start = session->locations()->first_mark_before (drag_info.grab_frame);
4638 end = session->locations()->first_mark_after (drag_info.grab_frame);
4640 if (end == max_frames) {
4641 end = session->current_end_frame ();
4645 start = session->current_start_frame ();
4648 switch (mouse_mode) {
4650 /* find the two markers on either side and then make the selection from it */
4651 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4655 /* find the two markers on either side of the click and make the range out of it */
4656 selection->set (0, start, end);
4665 stop_canvas_autoscroll ();
4671 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4673 drag_info.item = item;
4674 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4675 drag_info.finished_callback = &Editor::end_mouse_zoom;
4677 start_grab (event, zoom_cursor);
4679 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4683 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4688 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4689 snap_to (drag_info.current_pointer_frame);
4691 if (drag_info.first_move) {
4692 snap_to (drag_info.grab_frame);
4696 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4698 /* base start and end on initial click position */
4699 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4700 start = drag_info.current_pointer_frame;
4701 end = drag_info.grab_frame;
4703 end = drag_info.current_pointer_frame;
4704 start = drag_info.grab_frame;
4709 if (drag_info.first_move) {
4711 zoom_rect->raise_to_top();
4714 reposition_zoom_rect(start, end);
4716 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4717 drag_info.first_move = false;
4719 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4724 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4726 if (!drag_info.first_move) {
4727 drag_mouse_zoom (item, event);
4729 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4730 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4732 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4735 temporal_zoom_to_frame (false, drag_info.grab_frame);
4737 temporal_zoom_step (false);
4738 center_screen (drag_info.grab_frame);
4746 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4748 double x1 = frame_to_pixel (start);
4749 double x2 = frame_to_pixel (end);
4750 double y2 = full_canvas_height - 1.0;
4752 zoom_rect->property_x1() = x1;
4753 zoom_rect->property_y1() = 1.0;
4754 zoom_rect->property_x2() = x2;
4755 zoom_rect->property_y2() = y2;
4759 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4761 drag_info.item = item;
4762 drag_info.motion_callback = &Editor::drag_rubberband_select;
4763 drag_info.finished_callback = &Editor::end_rubberband_select;
4765 start_grab (event, cross_hair_cursor);
4767 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4771 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4778 /* use a bigger drag threshold than the default */
4780 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4784 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4785 if (drag_info.first_move) {
4786 snap_to (drag_info.grab_frame);
4788 snap_to (drag_info.current_pointer_frame);
4791 /* base start and end on initial click position */
4793 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4794 start = drag_info.current_pointer_frame;
4795 end = drag_info.grab_frame;
4797 end = drag_info.current_pointer_frame;
4798 start = drag_info.grab_frame;
4801 if (drag_info.current_pointer_y < drag_info.grab_y) {
4802 y1 = drag_info.current_pointer_y;
4803 y2 = drag_info.grab_y;
4805 y2 = drag_info.current_pointer_y;
4806 y1 = drag_info.grab_y;
4810 if (start != end || y1 != y2) {
4812 double x1 = frame_to_pixel (start);
4813 double x2 = frame_to_pixel (end);
4815 rubberband_rect->property_x1() = x1;
4816 rubberband_rect->property_y1() = y1;
4817 rubberband_rect->property_x2() = x2;
4818 rubberband_rect->property_y2() = y2;
4820 rubberband_rect->show();
4821 rubberband_rect->raise_to_top();
4823 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4824 drag_info.first_move = false;
4826 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4831 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4833 if (!drag_info.first_move) {
4835 drag_rubberband_select (item, event);
4838 if (drag_info.current_pointer_y < drag_info.grab_y) {
4839 y1 = drag_info.current_pointer_y;
4840 y2 = drag_info.grab_y;
4843 y2 = drag_info.current_pointer_y;
4844 y1 = drag_info.grab_y;
4848 Selection::Operation op = Keyboard::selection_type (event->button.state);
4851 begin_reversible_command (_("rubberband selection"));
4853 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4854 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4856 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4860 commit_reversible_command ();
4864 selection->clear_tracks();
4865 selection->clear_regions();
4866 selection->clear_points ();
4867 selection->clear_lines ();
4870 rubberband_rect->hide();
4875 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4877 using namespace Gtkmm2ext;
4879 ArdourPrompter prompter (false);
4881 prompter.set_prompt (_("Name for region:"));
4882 prompter.set_initial_text (clicked_regionview->region()->name());
4883 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4884 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4885 prompter.show_all ();
4886 switch (prompter.run ()) {
4887 case Gtk::RESPONSE_ACCEPT:
4889 prompter.get_result(str);
4891 clicked_regionview->region()->set_name (str);
4899 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4901 drag_info.item = item;
4902 drag_info.motion_callback = &Editor::time_fx_motion;
4903 drag_info.finished_callback = &Editor::end_time_fx;
4907 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4911 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4913 RegionView* rv = clicked_regionview;
4915 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4916 snap_to (drag_info.current_pointer_frame);
4919 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4923 if (drag_info.current_pointer_frame > rv->region()->position()) {
4924 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4927 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4928 drag_info.first_move = false;
4930 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4934 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4936 clicked_regionview->get_time_axis_view().hide_timestretch ();
4938 if (drag_info.first_move) {
4942 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
4943 /* backwards drag of the left edge - not usable */
4947 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4948 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4950 begin_reversible_command (_("timestretch"));
4952 if (run_timestretch (selection->regions, percentage) == 0) {
4953 session->commit_reversible_command ();
4958 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4960 /* no brushing without a useful snap setting */
4963 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4966 switch (snap_mode) {
4968 return; /* can't work because it allows region to be placed anywhere */
4973 switch (snap_type) {
4976 case SnapToEditCursor:
4983 /* don't brush a copy over the original */
4985 if (pos == rv->region()->position()) {
4989 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4991 if (rtv == 0 || !rtv->is_track()) {
4995 boost::shared_ptr<Playlist> playlist = rtv->playlist();
4996 double speed = rtv->get_diskstream()->speed();
4998 XMLNode &before = playlist->get_state();
4999 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5000 XMLNode &after = playlist->get_state();
5001 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5003 // playlist is frozen, so we have to update manually
5005 playlist->Modified(); /* EMIT SIGNAL */
5009 Editor::track_height_step_timeout ()
5012 struct timeval delta;
5014 gettimeofday (&now, 0);
5015 timersub (&now, &last_track_height_step_timestamp, &delta);
5017 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5018 current_stepping_trackview = 0;
5026 Editor::add_mouse_speed (double speed, double dir)
5030 mouse_direction = dir;
5032 index = mouse_speed_entries;
5034 if (++index >= mouse_speed_size) {
5036 have_full_mouse_speed = true;
5039 mouse_speed[index] = speed;
5040 mouse_speed_entries = index;
5044 Editor::compute_mouse_speed ()
5048 if (!have_full_mouse_speed) {
5050 /* partial speed buffer, just use whatever we have so far */
5052 if (mouse_speed_entries == 0 ) {
5056 for (size_t n = 0; n < mouse_speed_entries; ++n) {
5057 total += mouse_speed[n];
5060 return mouse_direction * total/mouse_speed_entries;
5063 /* compute the average (effectively low-pass filtering) mouse speed
5064 across the entire buffer.
5067 for (size_t n = 0; n < mouse_speed_size; ++n) {
5068 total += mouse_speed[n];
5072 return mouse_direction * total/mouse_speed_size;
5076 Editor::update_mouse_speed ()
5081 session->request_transport_speed (0.0);
5082 mouse_speed_update = -1;
5086 speed = compute_mouse_speed ();
5088 struct timeval tmnow;
5090 gettimeofday (&tmnow, 0);
5091 double now = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec;
5093 if (now - last_scrub_time > 250000) {
5095 // 0.25 seconds since last mouse motion, start to brake
5097 if (fabs (speed) < 0.1) {
5098 /* don't asymptotically approach zero */
5099 memset (mouse_speed, 0, sizeof (double) * mouse_speed_size);
5101 } else if (fabs (speed) < 0.25) {
5102 add_mouse_speed (fabs (speed * 0.2), mouse_direction);
5104 add_mouse_speed (fabs (speed * 0.6), mouse_direction);
5108 session->request_transport_speed (speed);