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>
31 #include <pbd/basename.h>
33 #include "ardour_ui.h"
35 #include "time_axis_view.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
38 #include "midi_region_view.h"
40 #include "streamview.h"
41 #include "region_gain_line.h"
42 #include "automation_time_axis.h"
43 #include "control_point.h"
46 #include "selection.h"
49 #include "rgb_macros.h"
51 #include <ardour/types.h>
52 #include <ardour/profile.h>
53 #include <ardour/route.h>
54 #include <ardour/audio_track.h>
55 #include <ardour/audio_diskstream.h>
56 #include <ardour/midi_diskstream.h>
57 #include <ardour/playlist.h>
58 #include <ardour/audioplaylist.h>
59 #include <ardour/audioregion.h>
60 #include <ardour/midi_region.h>
61 #include <ardour/dB.h>
62 #include <ardour/utils.h>
63 #include <ardour/region_factory.h>
64 #include <ardour/source_factory.h>
71 using namespace ARDOUR;
75 using namespace Editing;
78 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
92 switch (event->type) {
93 case GDK_BUTTON_RELEASE:
94 case GDK_BUTTON_PRESS:
95 case GDK_2BUTTON_PRESS:
96 case GDK_3BUTTON_PRESS:
97 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
99 case GDK_MOTION_NOTIFY:
100 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
102 case GDK_ENTER_NOTIFY:
103 case GDK_LEAVE_NOTIFY:
104 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
107 case GDK_KEY_RELEASE:
108 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
111 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
115 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
116 position is negative (as can be the case with motion events in particular),
117 the frame location is always positive.
120 return pixel_to_frame (*pcx);
124 Editor::mouse_mode_toggled (MouseMode m)
126 if (ignore_mouse_mode_toggle) {
132 if (mouse_select_button.get_active()) {
138 if (mouse_move_button.get_active()) {
144 if (mouse_gain_button.get_active()) {
150 if (mouse_zoom_button.get_active()) {
156 if (mouse_timefx_button.get_active()) {
162 if (mouse_audition_button.get_active()) {
168 if (mouse_note_button.get_active()) {
179 Editor::set_mouse_mode (MouseMode m, bool force)
181 if (drag_info.item) {
185 if (!force && m == mouse_mode) {
193 if (mouse_mode != MouseRange) {
195 /* in all modes except range, hide the range selection,
196 show the object (region) selection.
199 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
200 (*i)->set_should_show_selection (true);
202 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
203 (*i)->hide_selection ();
209 in range mode,show the range selection.
212 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
213 if ((*i)->get_selected()) {
214 (*i)->show_selection (selection->time);
219 /* XXX the hack of unsetting all other buttons should go
220 away once GTK2 allows us to use regular radio buttons drawn like
221 normal buttons, rather than my silly GroupedButton hack.
224 ignore_mouse_mode_toggle = true;
226 switch (mouse_mode) {
228 mouse_select_button.set_active (true);
229 current_canvas_cursor = selector_cursor;
233 mouse_move_button.set_active (true);
234 current_canvas_cursor = grabber_cursor;
238 mouse_gain_button.set_active (true);
239 current_canvas_cursor = cross_hair_cursor;
243 mouse_zoom_button.set_active (true);
244 current_canvas_cursor = zoom_cursor;
248 mouse_timefx_button.set_active (true);
249 current_canvas_cursor = time_fx_cursor; // just use playhead
253 mouse_audition_button.set_active (true);
254 current_canvas_cursor = speaker_cursor;
258 mouse_note_button.set_active (true);
259 set_midi_edit_cursor (current_midi_edit_mode());
263 if (mouse_mode == MouseNote)
264 midi_toolbar_frame.show();
266 midi_toolbar_frame.hide();
268 ignore_mouse_mode_toggle = false;
271 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
276 Editor::step_mouse_mode (bool next)
278 switch (current_mouse_mode()) {
280 if (next) set_mouse_mode (MouseRange);
281 else set_mouse_mode (MouseTimeFX);
285 if (next) set_mouse_mode (MouseZoom);
286 else set_mouse_mode (MouseObject);
290 if (next) set_mouse_mode (MouseGain);
291 else set_mouse_mode (MouseRange);
295 if (next) set_mouse_mode (MouseTimeFX);
296 else set_mouse_mode (MouseZoom);
300 if (next) set_mouse_mode (MouseAudition);
301 else set_mouse_mode (MouseGain);
305 if (next) set_mouse_mode (MouseObject);
306 else set_mouse_mode (MouseTimeFX);
310 if (next) set_mouse_mode (MouseObject);
311 else set_mouse_mode (MouseAudition);
317 Editor::midi_edit_mode_toggled (MidiEditMode m)
319 if (ignore_midi_edit_mode_toggle) {
325 if (midi_tool_pencil_button.get_active())
326 set_midi_edit_mode (m);
330 if (midi_tool_select_button.get_active())
331 set_midi_edit_mode (m);
335 if (midi_tool_erase_button.get_active())
336 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_pencil_button.set_active (true);
370 midi_tool_select_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_pencil_cursor;
396 current_canvas_cursor = midi_select_cursor;
400 current_canvas_cursor = midi_erase_cursor;
406 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
410 /* in object/audition/timefx mode, any button press sets
411 the selection if the object can be selected. this is a
412 bit of hack, because we want to avoid this if the
413 mouse operation is a region alignment.
415 note: not dbl-click or triple-click
418 if (((mouse_mode != MouseObject) &&
419 (mouse_mode != MouseAudition || item_type != RegionItem) &&
420 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
421 (mouse_mode != MouseRange)) ||
423 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
428 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
430 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
432 /* almost no selection action on modified button-2 or button-3 events */
434 if (item_type != RegionItem && event->button.button != 2) {
440 Selection::Operation op = Keyboard::selection_type (event->button.state);
441 bool press = (event->type == GDK_BUTTON_PRESS);
443 // begin_reversible_command (_("select on click"));
447 if (mouse_mode != MouseRange) {
448 commit = set_selected_regionview_from_click (press, op, true);
449 } else if (event->type == GDK_BUTTON_PRESS) {
450 commit = set_selected_track_from_click (press, op, false);
454 case RegionViewNameHighlight:
456 if (mouse_mode != MouseRange) {
457 commit = set_selected_regionview_from_click (press, op, true);
458 } else if (event->type == GDK_BUTTON_PRESS) {
459 commit = set_selected_track_from_click (press, op, false);
464 case FadeInHandleItem:
466 case FadeOutHandleItem:
468 if (mouse_mode != MouseRange) {
469 commit = set_selected_regionview_from_click (press, op, true);
470 } else if (event->type == GDK_BUTTON_PRESS) {
471 commit = set_selected_track_from_click (press, op, false);
475 case CrossfadeViewItem:
476 commit = set_selected_track_from_click (press, op, false);
479 case ControlPointItem:
480 commit = set_selected_track_from_click (press, op, true);
481 if (mouse_mode != MouseRange) {
482 commit |= set_selected_control_point_from_click (op, false);
487 /* for context click or range selection, select track */
488 if (event->button.button == 3) {
489 commit = set_selected_track_from_click (press, op, true);
490 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
491 commit = set_selected_track_from_click (press, op, false);
495 case AutomationTrackItem:
496 commit = set_selected_track_from_click (press, op, true);
504 // commit_reversible_command ();
509 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
511 track_canvas.grab_focus();
513 if (session && session->actively_recording()) {
517 button_selection (item, event, item_type);
519 if (drag_info.item == 0 &&
520 (Keyboard::is_delete_event (&event->button) ||
521 Keyboard::is_context_menu_event (&event->button) ||
522 Keyboard::is_edit_event (&event->button))) {
524 /* handled by button release */
528 switch (event->button.button) {
531 if (event->type == GDK_BUTTON_PRESS) {
533 if (drag_info.item) {
534 drag_info.item->ungrab (event->button.time);
537 /* single mouse clicks on any of these item types operate
538 independent of mouse mode, mostly because they are
539 not on the main track canvas or because we want
545 case PlayheadCursorItem:
546 start_cursor_grab (item, event);
550 if (Keyboard::modifier_state_equals (event->button.state,
551 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
552 hide_marker (item, event);
554 start_marker_grab (item, event);
558 case TempoMarkerItem:
559 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
560 start_tempo_marker_copy_grab (item, event);
562 start_tempo_marker_grab (item, event);
566 case MeterMarkerItem:
567 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
568 start_meter_marker_copy_grab (item, event);
570 start_meter_marker_grab (item, event);
580 case RangeMarkerBarItem:
581 start_range_markerbar_op (item, event, CreateRangeMarker);
585 case TransportMarkerBarItem:
586 start_range_markerbar_op (item, event, CreateTransportMarker);
595 switch (mouse_mode) {
598 case StartSelectionTrimItem:
599 start_selection_op (item, event, SelectionStartTrim);
602 case EndSelectionTrimItem:
603 start_selection_op (item, event, SelectionEndTrim);
607 if (Keyboard::modifier_state_contains
608 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
609 // contains and not equals because I can't use alt as a modifier alone.
610 start_selection_grab (item, event);
611 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
612 /* grab selection for moving */
613 start_selection_op (item, event, SelectionMove);
616 /* this was debated, but decided the more common action was to
617 make a new selection */
618 start_selection_op (item, event, CreateSelection);
623 start_selection_op (item, event, CreateSelection);
629 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
630 event->type == GDK_BUTTON_PRESS) {
632 start_rubberband_select (item, event);
634 } else if (event->type == GDK_BUTTON_PRESS) {
637 case FadeInHandleItem:
638 start_fade_in_grab (item, event);
641 case FadeOutHandleItem:
642 start_fade_out_grab (item, event);
646 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
647 start_region_copy_grab (item, event);
648 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
649 start_region_brush_grab (item, event);
651 start_region_grab (item, event);
655 case RegionViewNameHighlight:
656 start_trim (item, event);
661 /* rename happens on edit clicks */
662 start_trim (clicked_regionview->get_name_highlight(), event);
666 case ControlPointItem:
667 start_control_point_grab (item, event);
671 case AutomationLineItem:
672 start_line_grab_from_line (item, event);
677 case AutomationTrackItem:
678 start_rubberband_select (item, event);
682 case ImageFrameHandleStartItem:
683 imageframe_start_handle_op(item, event) ;
686 case ImageFrameHandleEndItem:
687 imageframe_end_handle_op(item, event) ;
690 case MarkerViewHandleStartItem:
691 markerview_item_start_handle_op(item, event) ;
694 case MarkerViewHandleEndItem:
695 markerview_item_end_handle_op(item, event) ;
699 start_markerview_grab(item, event) ;
702 start_imageframe_grab(item, event) ;
720 // start_line_grab_from_regionview (item, event);
724 start_line_grab_from_line (item, event);
727 case ControlPointItem:
728 start_control_point_grab (item, event);
739 case ControlPointItem:
740 start_control_point_grab (item, event);
743 case AutomationLineItem:
744 start_line_grab_from_line (item, event);
748 // XXX need automation mode to identify which
750 // start_line_grab_from_regionview (item, event);
760 if (event->type == GDK_BUTTON_PRESS) {
761 start_mouse_zoom (item, event);
768 if (item_type == RegionItem) {
769 start_time_fx (item, event);
775 last_scrub_frame = 0;
777 have_full_mouse_speed = false;
778 memset (mouse_speed, 0, sizeof (double) * mouse_speed_size);
779 /* rest handled in motion & release */
783 start_create_region_grab (item, event);
792 switch (mouse_mode) {
794 if (event->type == GDK_BUTTON_PRESS) {
797 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
798 start_region_copy_grab (item, event);
800 start_region_grab (item, event);
804 case ControlPointItem:
805 start_control_point_grab (item, event);
816 case RegionViewNameHighlight:
817 start_trim (item, event);
822 start_trim (clicked_regionview->get_name_highlight(), event);
833 if (event->type == GDK_BUTTON_PRESS) {
834 /* relax till release */
841 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
842 temporal_zoom_session();
844 temporal_zoom_to_frame (true, event_frame(event));
867 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
869 nframes_t where = event_frame (event, 0, 0);
871 /* no action if we're recording */
873 if (session && session->actively_recording()) {
877 /* first, see if we're finishing a drag ... */
879 if (drag_info.item) {
880 if (end_grab (item, event)) {
881 /* grab dragged, so do nothing else */
886 button_selection (item, event, item_type);
888 /* edit events get handled here */
890 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
896 case TempoMarkerItem:
897 edit_tempo_marker (item);
900 case MeterMarkerItem:
901 edit_meter_marker (item);
905 if (clicked_regionview->name_active()) {
906 return mouse_rename_region (item, event);
916 /* context menu events get handled here */
918 if (Keyboard::is_context_menu_event (&event->button)) {
920 if (drag_info.item == 0) {
922 /* no matter which button pops up the context menu, tell the menu
923 widget to use button 1 to drive menu selection.
928 case FadeInHandleItem:
930 case FadeOutHandleItem:
931 popup_fade_context_menu (1, event->button.time, item, item_type);
935 popup_track_context_menu (1, event->button.time, where);
939 case RegionViewNameHighlight:
941 popup_track_context_menu (1, event->button.time, where);
945 popup_track_context_menu (1, event->button.time, where);
948 case AutomationTrackItem:
949 case CrossfadeViewItem:
950 popup_track_context_menu (1, event->button.time, where);
954 case RangeMarkerBarItem:
955 case TransportMarkerBarItem:
958 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
962 marker_context_menu (&event->button, item);
965 case TempoMarkerItem:
966 tm_marker_context_menu (&event->button, item);
969 case MeterMarkerItem:
970 tm_marker_context_menu (&event->button, item);
975 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
977 case ImageFrameTimeAxisItem:
978 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
981 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
983 case MarkerTimeAxisItem:
984 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
996 /* delete events get handled here */
998 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1000 switch (item_type) {
1001 case TempoMarkerItem:
1002 remove_tempo_marker (item);
1005 case MeterMarkerItem:
1006 remove_meter_marker (item);
1010 remove_marker (*item, event);
1014 if (mouse_mode == MouseObject) {
1015 remove_clicked_region ();
1019 case ControlPointItem:
1020 if (mouse_mode == MouseGain) {
1021 remove_gain_control_point (item, event);
1023 remove_control_point (item, event);
1033 switch (event->button.button) {
1036 switch (item_type) {
1037 /* see comments in button_press_handler */
1038 case EditCursorItem:
1039 case PlayheadCursorItem:
1042 case AutomationLineItem:
1043 case StartSelectionTrimItem:
1044 case EndSelectionTrimItem:
1048 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1049 snap_to (where, 0, true);
1051 mouse_add_new_marker (where);
1055 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1058 mouse_add_new_tempo_event (where);
1062 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1070 switch (mouse_mode) {
1072 switch (item_type) {
1073 case AutomationTrackItem:
1074 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
1088 // Gain only makes sense for audio regions
1090 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1094 switch (item_type) {
1096 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1100 case AutomationTrackItem:
1101 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1102 add_automation_event (item, event, where, event->button.y);
1112 if (last_scrub_frame == 0) {
1113 /* no drag, just a click */
1114 switch (item_type) {
1116 audition_selected_region ();
1122 /* make sure we stop */
1123 session->request_transport_speed (0.0);
1137 switch (mouse_mode) {
1141 // x_style_paste (where, 1.0);
1161 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1167 switch (item_type) {
1168 case ControlPointItem:
1169 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1170 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1171 cp->set_visible (true);
1175 at_y = cp->get_y ();
1176 cp->item()->i2w (at_x, at_y);
1180 fraction = 1.0 - (cp->get_y() / cp->line().height());
1182 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1183 show_verbose_canvas_cursor ();
1185 if (is_drawable()) {
1186 track_canvas.get_window()->set_cursor (*fader_cursor);
1192 if (mouse_mode == MouseGain) {
1193 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1195 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1196 if (is_drawable()) {
1197 track_canvas.get_window()->set_cursor (*fader_cursor);
1202 case AutomationLineItem:
1203 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1205 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1207 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1209 if (is_drawable()) {
1210 track_canvas.get_window()->set_cursor (*fader_cursor);
1215 case RegionViewNameHighlight:
1216 if (is_drawable() && mouse_mode == MouseObject) {
1217 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1221 case StartSelectionTrimItem:
1222 case EndSelectionTrimItem:
1225 case ImageFrameHandleStartItem:
1226 case ImageFrameHandleEndItem:
1227 case MarkerViewHandleStartItem:
1228 case MarkerViewHandleEndItem:
1231 if (is_drawable()) {
1232 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1236 case EditCursorItem:
1237 case PlayheadCursorItem:
1238 if (is_drawable()) {
1239 track_canvas.get_window()->set_cursor (*grabber_cursor);
1243 case RegionViewName:
1245 /* when the name is not an active item, the entire name highlight is for trimming */
1247 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1248 if (mouse_mode == MouseObject && is_drawable()) {
1249 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1255 case AutomationTrackItem:
1256 if (is_drawable()) {
1257 Gdk::Cursor *cursor;
1258 switch (mouse_mode) {
1260 cursor = selector_cursor;
1263 cursor = zoom_cursor;
1266 cursor = cross_hair_cursor;
1270 track_canvas.get_window()->set_cursor (*cursor);
1272 AutomationTimeAxisView* atv;
1273 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1274 clear_entered_track = false;
1275 set_entered_track (atv);
1281 case RangeMarkerBarItem:
1282 case TransportMarkerBarItem:
1285 if (is_drawable()) {
1286 time_canvas.get_window()->set_cursor (*timebar_cursor);
1291 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1294 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1296 case MeterMarkerItem:
1297 case TempoMarkerItem:
1298 if (is_drawable()) {
1299 time_canvas.get_window()->set_cursor (*timebar_cursor);
1302 case FadeInHandleItem:
1303 case FadeOutHandleItem:
1304 if (mouse_mode == MouseObject) {
1305 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1307 rect->property_fill_color_rgba() = 0;
1308 rect->property_outline_pixels() = 1;
1317 /* second pass to handle entered track status in a comprehensible way.
1320 switch (item_type) {
1322 case AutomationLineItem:
1323 case ControlPointItem:
1324 /* these do not affect the current entered track state */
1325 clear_entered_track = false;
1328 case AutomationTrackItem:
1329 /* handled above already */
1333 set_entered_track (0);
1341 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1350 switch (item_type) {
1351 case ControlPointItem:
1352 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1353 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1354 if (cp->line().npoints() > 1 && !cp->selected()) {
1355 cp->set_visible (false);
1359 if (is_drawable()) {
1360 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1363 hide_verbose_canvas_cursor ();
1366 case RegionViewNameHighlight:
1367 case StartSelectionTrimItem:
1368 case EndSelectionTrimItem:
1369 case EditCursorItem:
1370 case PlayheadCursorItem:
1373 case ImageFrameHandleStartItem:
1374 case ImageFrameHandleEndItem:
1375 case MarkerViewHandleStartItem:
1376 case MarkerViewHandleEndItem:
1379 if (is_drawable()) {
1380 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1385 case AutomationLineItem:
1386 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1388 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1390 line->property_fill_color_rgba() = al->get_line_color();
1392 if (is_drawable()) {
1393 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1397 case RegionViewName:
1398 /* see enter_handler() for notes */
1399 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1400 if (is_drawable() && mouse_mode == MouseObject) {
1401 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1406 case RangeMarkerBarItem:
1407 case TransportMarkerBarItem:
1411 if (is_drawable()) {
1412 time_canvas.get_window()->set_cursor (*timebar_cursor);
1417 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1420 loc = find_location_from_marker (marker, is_start);
1421 if (loc) location_flags_changed (loc, this);
1423 case MeterMarkerItem:
1424 case TempoMarkerItem:
1426 if (is_drawable()) {
1427 time_canvas.get_window()->set_cursor (*timebar_cursor);
1432 case FadeInHandleItem:
1433 case FadeOutHandleItem:
1434 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1436 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1438 rect->property_fill_color_rgba() = rv->get_fill_color();
1439 rect->property_outline_pixels() = 0;
1444 case AutomationTrackItem:
1445 if (is_drawable()) {
1446 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1447 clear_entered_track = true;
1448 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1460 Editor::left_automation_track ()
1462 if (clear_entered_track) {
1463 set_entered_track (0);
1464 clear_entered_track = false;
1470 _update_mouse_speed (void *arg)
1472 return static_cast<Editor*>(arg)->update_mouse_speed ();
1476 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1480 /* We call this so that MOTION_NOTIFY events continue to be
1481 delivered to the canvas. We need to do this because we set
1482 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1483 the density of the events, at the expense of a round-trip
1484 to the server. Given that this will mostly occur on cases
1485 where DISPLAY = :0.0, and given the cost of what the motion
1486 event might do, its a good tradeoff.
1489 track_canvas.get_pointer (x, y);
1491 if (current_stepping_trackview) {
1492 /* don't keep the persistent stepped trackview if the mouse moves */
1493 current_stepping_trackview = 0;
1494 step_timeout.disconnect ();
1497 if (session && session->actively_recording()) {
1498 /* Sorry. no dragging stuff around while we record */
1502 drag_info.item_type = item_type;
1503 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1504 &drag_info.current_pointer_y);
1506 switch (mouse_mode) {
1509 struct timeval tmnow;
1511 if (last_scrub_frame == 0) {
1513 /* first motion, just set up the variables */
1515 last_scrub_frame = (nframes64_t) drag_info.current_pointer_frame;
1516 gettimeofday (&tmnow, 0);
1517 last_scrub_time = tmnow.tv_sec * 1000000.0 + tmnow.tv_usec;
1518 session->request_locate (last_scrub_frame, true);
1521 /* how fast is the mouse moving ? */
1529 if (last_scrub_frame < (nframes64_t) drag_info.current_pointer_frame) {
1530 distance = (nframes64_t) drag_info.current_pointer_frame - last_scrub_frame;
1533 distance = last_scrub_frame - (nframes64_t) drag_info.current_pointer_frame;
1537 if (drag_info.grab_x < drag_info.current_pointer_x) {
1538 distance = drag_info.current_pointer_x - drag_info.grab_x;
1541 distance = drag_info.grab_x - drag_info.current_pointer_x;
1546 gettimeofday (&tmnow, 0);
1547 time = (tmnow.tv_sec * 1000000.0 + tmnow.tv_usec) - last_scrub_time;
1548 last_scrub_frame = drag_info.current_pointer_frame;
1549 last_scrub_time = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec;
1550 speed = ((double)distance/session->frame_rate()) / (time/1000000.0); // frames/sec
1552 add_mouse_speed (speed, dir);
1554 if (mouse_speed_update < 0) {
1555 mouse_speed_update = g_timeout_add (10, _update_mouse_speed, this);
1556 update_mouse_speed ();
1566 if (!from_autoscroll && drag_info.item) {
1567 /* item != 0 is the best test i can think of for dragging.
1569 if (!drag_info.move_threshold_passed) {
1571 bool x_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1572 bool y_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1574 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1576 // and change the initial grab loc/frame if this drag info wants us to
1578 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1579 drag_info.grab_frame = drag_info.current_pointer_frame;
1580 drag_info.grab_x = drag_info.current_pointer_x;
1581 drag_info.grab_y = drag_info.current_pointer_y;
1582 drag_info.last_pointer_frame = drag_info.grab_frame;
1583 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1588 switch (item_type) {
1589 case PlayheadCursorItem:
1590 case EditCursorItem:
1592 case ControlPointItem:
1593 case TempoMarkerItem:
1594 case MeterMarkerItem:
1595 case RegionViewNameHighlight:
1596 case StartSelectionTrimItem:
1597 case EndSelectionTrimItem:
1600 case AutomationLineItem:
1601 case FadeInHandleItem:
1602 case FadeOutHandleItem:
1605 case ImageFrameHandleStartItem:
1606 case ImageFrameHandleEndItem:
1607 case MarkerViewHandleStartItem:
1608 case MarkerViewHandleEndItem:
1611 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1612 (event->motion.state & Gdk::BUTTON2_MASK))) {
1613 if (!from_autoscroll) {
1614 maybe_autoscroll (event);
1616 (this->*(drag_info.motion_callback)) (item, event);
1625 switch (mouse_mode) {
1631 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1632 (event->motion.state & GDK_BUTTON2_MASK))) {
1633 if (!from_autoscroll) {
1634 maybe_autoscroll (event);
1636 (this->*(drag_info.motion_callback)) (item, event);
1647 track_canvas_motion (event);
1648 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1656 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1658 if (drag_info.item == 0) {
1659 fatal << _("programming error: start_grab called without drag item") << endmsg;
1665 cursor = grabber_cursor;
1668 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1670 if (event->button.button == 2) {
1671 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1672 drag_info.y_constrained = true;
1673 drag_info.x_constrained = false;
1675 drag_info.y_constrained = false;
1676 drag_info.x_constrained = true;
1679 drag_info.x_constrained = false;
1680 drag_info.y_constrained = false;
1683 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1684 drag_info.last_pointer_frame = drag_info.grab_frame;
1685 drag_info.current_pointer_frame = drag_info.grab_frame;
1686 drag_info.current_pointer_x = drag_info.grab_x;
1687 drag_info.current_pointer_y = drag_info.grab_y;
1688 drag_info.cumulative_x_drag = 0;
1689 drag_info.cumulative_y_drag = 0;
1690 drag_info.first_move = true;
1691 drag_info.move_threshold_passed = false;
1692 drag_info.want_move_threshold = false;
1693 drag_info.pointer_frame_offset = 0;
1694 drag_info.brushing = false;
1695 drag_info.copied_location = 0;
1697 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1699 event->button.time);
1701 if (session && session->transport_rolling()) {
1702 drag_info.was_rolling = true;
1704 drag_info.was_rolling = false;
1707 switch (snap_type) {
1708 case SnapToRegionStart:
1709 case SnapToRegionEnd:
1710 case SnapToRegionSync:
1711 case SnapToRegionBoundary:
1712 build_region_boundary_cache ();
1720 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1722 drag_info.item->ungrab (0);
1723 drag_info.item = new_item;
1726 cursor = grabber_cursor;
1729 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1733 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1735 bool did_drag = false;
1737 stop_canvas_autoscroll ();
1739 if (drag_info.item == 0) {
1743 drag_info.item->ungrab (event->button.time);
1745 if (drag_info.finished_callback) {
1746 (this->*(drag_info.finished_callback)) (item, event);
1749 did_drag = !drag_info.first_move;
1751 hide_verbose_canvas_cursor();
1754 drag_info.copy = false;
1755 drag_info.motion_callback = 0;
1756 drag_info.finished_callback = 0;
1757 drag_info.last_trackview = 0;
1758 drag_info.last_frame_position = 0;
1759 drag_info.grab_frame = 0;
1760 drag_info.last_pointer_frame = 0;
1761 drag_info.current_pointer_frame = 0;
1762 drag_info.brushing = false;
1764 if (drag_info.copied_location) {
1765 delete drag_info.copied_location;
1766 drag_info.copied_location = 0;
1773 Editor::set_edit_cursor (GdkEvent* event)
1775 nframes_t pointer_frame = event_frame (event);
1777 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1778 if (snap_type != SnapToEditCursor) {
1779 snap_to (pointer_frame);
1783 edit_cursor->set_position (pointer_frame);
1784 edit_cursor_clock.set (pointer_frame);
1788 Editor::set_playhead_cursor (GdkEvent* event)
1790 nframes_t pointer_frame = event_frame (event);
1792 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1793 snap_to (pointer_frame);
1797 session->request_locate (pointer_frame, session->transport_rolling());
1802 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1804 drag_info.item = item;
1805 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1806 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1810 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1811 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1815 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1817 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
1821 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1823 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1825 nframes_t fade_length;
1827 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1828 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1834 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1838 if (pos < (arv->region()->position() + 64)) {
1839 fade_length = 64; // this should be a minimum defined somewhere
1840 } else if (pos > arv->region()->last_frame()) {
1841 fade_length = arv->region()->length();
1843 fade_length = pos - arv->region()->position();
1845 /* mapover the region selection */
1847 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1849 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1855 tmp->reset_fade_in_shape_width (fade_length);
1858 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1860 drag_info.first_move = false;
1864 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1866 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1868 nframes_t fade_length;
1870 if (drag_info.first_move) return;
1872 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1873 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1878 if (pos < (arv->region()->position() + 64)) {
1879 fade_length = 64; // this should be a minimum defined somewhere
1880 } else if (pos > arv->region()->last_frame()) {
1881 fade_length = arv->region()->length();
1883 fade_length = pos - arv->region()->position();
1886 begin_reversible_command (_("change fade in length"));
1888 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1890 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1896 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
1897 XMLNode &before = alist->get_state();
1899 tmp->audio_region()->set_fade_in_length (fade_length);
1901 XMLNode &after = alist->get_state();
1902 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1905 commit_reversible_command ();
1909 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1911 drag_info.item = item;
1912 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1913 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1917 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1918 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1922 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1924 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
1928 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1930 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1932 nframes_t fade_length;
1934 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1935 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1940 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1944 if (pos > (arv->region()->last_frame() - 64)) {
1945 fade_length = 64; // this should really be a minimum fade defined somewhere
1947 else if (pos < arv->region()->position()) {
1948 fade_length = arv->region()->length();
1951 fade_length = arv->region()->last_frame() - pos;
1954 /* mapover the region selection */
1956 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1958 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1964 tmp->reset_fade_out_shape_width (fade_length);
1967 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1969 drag_info.first_move = false;
1973 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1975 if (drag_info.first_move) return;
1977 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1979 nframes_t fade_length;
1981 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1982 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1988 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1992 if (pos > (arv->region()->last_frame() - 64)) {
1993 fade_length = 64; // this should really be a minimum fade defined somewhere
1995 else if (pos < arv->region()->position()) {
1996 fade_length = arv->region()->length();
1999 fade_length = arv->region()->last_frame() - pos;
2002 begin_reversible_command (_("change fade out length"));
2004 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2006 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2012 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2013 XMLNode &before = alist->get_state();
2015 tmp->audio_region()->set_fade_out_length (fade_length);
2017 XMLNode &after = alist->get_state();
2018 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2021 commit_reversible_command ();
2025 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2027 drag_info.item = item;
2028 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2029 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2033 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2034 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2038 Cursor* cursor = (Cursor *) drag_info.data;
2040 if (cursor == playhead_cursor) {
2041 _dragging_playhead = true;
2043 if (session && drag_info.was_rolling) {
2044 session->request_stop ();
2047 if (session && session->is_auditioning()) {
2048 session->cancel_audition ();
2052 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2054 show_verbose_time_cursor (cursor->current_frame, 10);
2058 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2060 Cursor* cursor = (Cursor *) drag_info.data;
2061 nframes_t adjusted_frame;
2063 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2064 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2070 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2071 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
2072 snap_to (adjusted_frame);
2076 if (adjusted_frame == drag_info.last_pointer_frame) return;
2078 cursor->set_position (adjusted_frame);
2080 if (cursor == edit_cursor) {
2081 edit_cursor_clock.set (cursor->current_frame);
2083 UpdateAllTransportClocks (cursor->current_frame);
2086 show_verbose_time_cursor (cursor->current_frame, 10);
2088 drag_info.last_pointer_frame = adjusted_frame;
2089 drag_info.first_move = false;
2093 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2095 if (drag_info.first_move) return;
2097 cursor_drag_motion_callback (item, event);
2099 _dragging_playhead = false;
2101 if (item == &playhead_cursor->canvas_item) {
2103 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2105 } else if (item == &edit_cursor->canvas_item) {
2106 edit_cursor->set_position (edit_cursor->current_frame);
2107 edit_cursor_clock.set (edit_cursor->current_frame);
2112 Editor::update_marker_drag_item (Location *location)
2114 double x1 = frame_to_pixel (location->start());
2115 double x2 = frame_to_pixel (location->end());
2117 if (location->is_mark()) {
2118 marker_drag_line_points.front().set_x(x1);
2119 marker_drag_line_points.back().set_x(x1);
2120 marker_drag_line->property_points() = marker_drag_line_points;
2123 range_marker_drag_rect->property_x1() = x1;
2124 range_marker_drag_rect->property_x2() = x2;
2129 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2133 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2134 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2140 Location *location = find_location_from_marker (marker, is_start);
2142 drag_info.item = item;
2143 drag_info.data = marker;
2144 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2145 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2149 drag_info.copied_location = new Location (*location);
2150 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2152 update_marker_drag_item (location);
2154 if (location->is_mark()) {
2155 marker_drag_line->show();
2156 marker_drag_line->raise_to_top();
2159 range_marker_drag_rect->show();
2160 range_marker_drag_rect->raise_to_top();
2163 if (is_start) show_verbose_time_cursor (location->start(), 10);
2164 else show_verbose_time_cursor (location->end(), 10);
2168 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2171 Marker* marker = (Marker *) drag_info.data;
2172 Location *real_location;
2173 Location *copy_location;
2175 bool move_both = false;
2179 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2180 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2185 nframes_t next = newframe;
2187 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2188 snap_to (newframe, 0, true);
2191 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2195 /* call this to find out if its the start or end */
2197 real_location = find_location_from_marker (marker, is_start);
2199 /* use the copy that we're "dragging" around */
2201 copy_location = drag_info.copied_location;
2203 f_delta = copy_location->end() - copy_location->start();
2205 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2209 if (copy_location->is_mark()) {
2212 copy_location->set_start (newframe);
2216 if (is_start) { // start-of-range marker
2219 copy_location->set_start (newframe);
2220 copy_location->set_end (newframe + f_delta);
2221 } else if (newframe < copy_location->end()) {
2222 copy_location->set_start (newframe);
2224 snap_to (next, 1, true);
2225 copy_location->set_end (next);
2226 copy_location->set_start (newframe);
2229 } else { // end marker
2232 copy_location->set_end (newframe);
2233 copy_location->set_start (newframe - f_delta);
2234 } else if (newframe > copy_location->start()) {
2235 copy_location->set_end (newframe);
2237 } else if (newframe > 0) {
2238 snap_to (next, -1, true);
2239 copy_location->set_start (next);
2240 copy_location->set_end (newframe);
2245 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2246 drag_info.first_move = false;
2248 update_marker_drag_item (copy_location);
2250 LocationMarkers* lm = find_location_markers (real_location);
2251 lm->set_position (copy_location->start(), copy_location->end());
2253 show_verbose_time_cursor (newframe, 10);
2257 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2259 if (drag_info.first_move) {
2260 marker_drag_motion_callback (item, event);
2264 Marker* marker = (Marker *) drag_info.data;
2268 begin_reversible_command ( _("move marker") );
2269 XMLNode &before = session->locations()->get_state();
2271 Location * location = find_location_from_marker (marker, is_start);
2274 if (location->is_mark()) {
2275 location->set_start (drag_info.copied_location->start());
2277 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2281 XMLNode &after = session->locations()->get_state();
2282 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2283 commit_reversible_command ();
2285 marker_drag_line->hide();
2286 range_marker_drag_rect->hide();
2290 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2293 MeterMarker* meter_marker;
2295 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2296 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2300 meter_marker = dynamic_cast<MeterMarker*> (marker);
2302 MetricSection& section (meter_marker->meter());
2304 if (!section.movable()) {
2308 drag_info.item = item;
2309 drag_info.data = marker;
2310 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2311 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2315 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2317 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2321 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2324 MeterMarker* meter_marker;
2326 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2327 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2331 meter_marker = dynamic_cast<MeterMarker*> (marker);
2333 // create a dummy marker for visual representation of moving the copy.
2334 // The actual copying is not done before we reach the finish callback.
2336 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2337 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2338 *new MeterSection(meter_marker->meter()));
2340 drag_info.item = &new_marker->the_item();
2341 drag_info.copy = true;
2342 drag_info.data = new_marker;
2343 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2344 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2348 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2350 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2354 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2356 MeterMarker* marker = (MeterMarker *) drag_info.data;
2357 nframes_t adjusted_frame;
2359 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2360 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2366 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2367 snap_to (adjusted_frame);
2370 if (adjusted_frame == drag_info.last_pointer_frame) return;
2372 marker->set_position (adjusted_frame);
2375 drag_info.last_pointer_frame = adjusted_frame;
2376 drag_info.first_move = false;
2378 show_verbose_time_cursor (adjusted_frame, 10);
2382 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2384 if (drag_info.first_move) return;
2386 meter_marker_drag_motion_callback (drag_info.item, event);
2388 MeterMarker* marker = (MeterMarker *) drag_info.data;
2391 TempoMap& map (session->tempo_map());
2392 map.bbt_time (drag_info.last_pointer_frame, when);
2394 if (drag_info.copy == true) {
2395 begin_reversible_command (_("copy meter mark"));
2396 XMLNode &before = map.get_state();
2397 map.add_meter (marker->meter(), when);
2398 XMLNode &after = map.get_state();
2399 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2400 commit_reversible_command ();
2402 // delete the dummy marker we used for visual representation of copying.
2403 // a new visual marker will show up automatically.
2406 begin_reversible_command (_("move meter mark"));
2407 XMLNode &before = map.get_state();
2408 map.move_meter (marker->meter(), when);
2409 XMLNode &after = map.get_state();
2410 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2411 commit_reversible_command ();
2416 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2419 TempoMarker* tempo_marker;
2421 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2422 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2426 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2427 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2431 MetricSection& section (tempo_marker->tempo());
2433 if (!section.movable()) {
2437 drag_info.item = item;
2438 drag_info.data = marker;
2439 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2440 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2444 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2445 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2449 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2452 TempoMarker* tempo_marker;
2454 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2455 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2459 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2460 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2464 // create a dummy marker for visual representation of moving the copy.
2465 // The actual copying is not done before we reach the finish callback.
2467 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2468 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2469 *new TempoSection(tempo_marker->tempo()));
2471 drag_info.item = &new_marker->the_item();
2472 drag_info.copy = true;
2473 drag_info.data = new_marker;
2474 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2475 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2479 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2481 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2485 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2487 TempoMarker* marker = (TempoMarker *) drag_info.data;
2488 nframes_t adjusted_frame;
2490 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2491 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2497 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2498 snap_to (adjusted_frame);
2501 if (adjusted_frame == drag_info.last_pointer_frame) return;
2503 /* OK, we've moved far enough to make it worth actually move the thing. */
2505 marker->set_position (adjusted_frame);
2507 show_verbose_time_cursor (adjusted_frame, 10);
2509 drag_info.last_pointer_frame = adjusted_frame;
2510 drag_info.first_move = false;
2514 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2516 if (drag_info.first_move) return;
2518 tempo_marker_drag_motion_callback (drag_info.item, event);
2520 TempoMarker* marker = (TempoMarker *) drag_info.data;
2523 TempoMap& map (session->tempo_map());
2524 map.bbt_time (drag_info.last_pointer_frame, when);
2526 if (drag_info.copy == true) {
2527 begin_reversible_command (_("copy tempo mark"));
2528 XMLNode &before = map.get_state();
2529 map.add_tempo (marker->tempo(), when);
2530 XMLNode &after = map.get_state();
2531 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2532 commit_reversible_command ();
2534 // delete the dummy marker we used for visual representation of copying.
2535 // a new visual marker will show up automatically.
2538 begin_reversible_command (_("move tempo mark"));
2539 XMLNode &before = map.get_state();
2540 map.move_tempo (marker->tempo(), when);
2541 XMLNode &after = map.get_state();
2542 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2543 commit_reversible_command ();
2548 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2550 ControlPoint* control_point;
2552 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2553 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2557 // We shouldn't remove the first or last gain point
2558 if (control_point->line().is_last_point(*control_point) ||
2559 control_point->line().is_first_point(*control_point)) {
2563 control_point->line().remove_point (*control_point);
2567 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2569 ControlPoint* control_point;
2571 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2572 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2576 control_point->line().remove_point (*control_point);
2580 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2582 ControlPoint* control_point;
2584 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2585 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2589 drag_info.item = item;
2590 drag_info.data = control_point;
2591 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2592 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2594 start_grab (event, fader_cursor);
2596 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2598 double fraction = 1.0 - ((control_point->get_y() - control_point->line().y_position()) / (double)control_point->line().height());
2599 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
2600 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2602 show_verbose_canvas_cursor ();
2606 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2608 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2610 double cx = drag_info.current_pointer_x;
2611 double cy = drag_info.current_pointer_y;
2613 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2614 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2616 if (drag_info.x_constrained) {
2617 cx = drag_info.grab_x;
2619 if (drag_info.y_constrained) {
2620 cy = drag_info.grab_y;
2623 cp->line().parent_group().w2i (cx, cy);
2627 cy = min ((double) (cp->line().y_position() + cp->line().height()), cy);
2629 //translate cx to frames
2630 nframes_t cx_frames = unit_to_frame (cx);
2632 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2633 snap_to (cx_frames);
2636 const double fraction = 1.0 - ((cy - cp->line().y_position()) / (double)cp->line().height());
2640 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2646 cp->line().point_drag (*cp, cx_frames , fraction, push);
2648 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
2650 drag_info.first_move = false;
2654 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2656 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2658 if (drag_info.first_move) {
2662 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2663 reset_point_selection ();
2667 control_point_drag_motion_callback (item, event);
2669 cp->line().end_drag (cp);
2673 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2675 switch (mouse_mode) {
2677 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2678 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2686 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2690 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2691 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2695 start_line_grab (al, event);
2699 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2703 nframes_t frame_within_region;
2705 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2709 cx = event->button.x;
2710 cy = event->button.y;
2711 line->parent_group().w2i (cx, cy);
2712 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2714 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2715 current_line_drag_info.after)) {
2716 /* no adjacent points */
2720 drag_info.item = &line->grab_item();
2721 drag_info.data = line;
2722 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2723 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2725 start_grab (event, fader_cursor);
2727 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2729 line->start_drag (0, drag_info.grab_frame, fraction);
2731 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2732 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2733 show_verbose_canvas_cursor ();
2737 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2739 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2740 double cx = drag_info.current_pointer_x;
2741 double cy = drag_info.current_pointer_y;
2743 line->parent_group().w2i (cx, cy);
2745 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2749 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2755 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2757 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2761 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2763 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2764 line_drag_motion_callback (item, event);
2769 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2771 if (selection->regions.empty() || clicked_regionview == 0) {
2775 drag_info.copy = false;
2776 drag_info.item = item;
2777 drag_info.data = clicked_regionview;
2778 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2779 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2784 TimeAxisView* tvp = clicked_axisview;
2785 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2787 if (tv && tv->is_track()) {
2788 speed = tv->get_diskstream()->speed();
2791 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2792 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2793 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2794 // we want a move threshold
2795 drag_info.want_move_threshold = true;
2797 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2799 begin_reversible_command (_("move region(s)"));
2803 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2805 drag_info.copy = false;
2806 drag_info.item = item;
2807 drag_info.data = clicked_axisview;
2808 drag_info.last_trackview = clicked_axisview;
2809 drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
2810 drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
2816 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2818 if (selection->regions.empty() || clicked_regionview == 0) {
2822 drag_info.copy = true;
2823 drag_info.item = item;
2824 drag_info.data = clicked_regionview;
2828 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2829 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2832 if (rtv && rtv->is_track()) {
2833 speed = rtv->get_diskstream()->speed();
2836 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2837 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2838 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2839 // we want a move threshold
2840 drag_info.want_move_threshold = true;
2841 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2842 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2843 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2847 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2849 if (selection->regions.empty() || clicked_regionview == 0) {
2853 drag_info.copy = false;
2854 drag_info.item = item;
2855 drag_info.data = clicked_regionview;
2856 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2857 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2862 TimeAxisView* tvp = clicked_axisview;
2863 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2865 if (tv && tv->is_track()) {
2866 speed = tv->get_diskstream()->speed();
2869 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2870 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2871 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2872 // we want a move threshold
2873 drag_info.want_move_threshold = true;
2874 drag_info.brushing = true;
2876 begin_reversible_command (_("Drag region brush"));
2880 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2884 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2885 nframes_t pending_region_position = 0;
2886 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2887 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2888 bool clamp_y_axis = false;
2889 vector<int32_t> height_list(512) ;
2890 vector<int32_t>::iterator j;
2892 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2894 drag_info.want_move_threshold = false; // don't copy again
2896 /* duplicate the region(s) */
2898 vector<RegionView*> new_regionviews;
2900 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2907 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2908 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2911 nrv = new AudioRegionView (*arv);
2913 nrv = new MidiRegionView (*mrv);
2918 nrv->get_canvas_group()->show ();
2920 new_regionviews.push_back (nrv);
2923 if (new_regionviews.empty()) {
2927 /* reset selection to new regionviews */
2929 selection->set (new_regionviews);
2931 /* reset drag_info data to reflect the fact that we are dragging the copies */
2933 drag_info.data = new_regionviews.front();
2935 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2938 /* Which trackview is this ? */
2940 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2941 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2943 /* The region motion is only processed if the pointer is over
2947 if (!tv || !tv->is_track()) {
2948 /* To make sure we hide the verbose canvas cursor when the mouse is
2949 not held over a track.
2951 hide_verbose_canvas_cursor ();
2955 original_pointer_order = drag_info.last_trackview->order;
2957 /************************************************************
2959 ************************************************************/
2961 if (drag_info.brushing) {
2962 clamp_y_axis = true;
2967 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2969 int32_t children = 0, numtracks = 0;
2970 // XXX hard coding track limit, oh my, so very very bad
2971 bitset <1024> tracks (0x00);
2972 /* get a bitmask representing the visible tracks */
2974 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2975 TimeAxisView *tracklist_timeview;
2976 tracklist_timeview = (*i);
2977 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2978 TimeAxisView::Children children_list;
2980 /* zeroes are audio tracks. ones are other types. */
2982 if (!rtv2->hidden()) {
2984 if (visible_y_high < rtv2->order) {
2985 visible_y_high = rtv2->order;
2987 if (visible_y_low > rtv2->order) {
2988 visible_y_low = rtv2->order;
2991 if (!rtv2->is_track()) {
2992 tracks = tracks |= (0x01 << rtv2->order);
2995 height_list[rtv2->order] = (*i)->height;
2997 if ((children_list = rtv2->get_child_list()).size() > 0) {
2998 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2999 tracks = tracks |= (0x01 << (rtv2->order + children));
3000 height_list[rtv2->order + children] = (*j)->height;
3008 /* find the actual span according to the canvas */
3010 canvas_pointer_y_span = pointer_y_span;
3011 if (drag_info.last_trackview->order >= tv->order) {
3013 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3014 if (height_list[y] == 0 ) {
3015 canvas_pointer_y_span--;
3020 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3021 if ( height_list[y] == 0 ) {
3022 canvas_pointer_y_span++;
3027 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3028 RegionView* rv2 = (*i);
3029 double ix1, ix2, iy1, iy2;
3032 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3033 rv2->get_canvas_group()->i2w (ix1, iy1);
3034 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3035 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3037 if (rtv2->order != original_pointer_order) {
3038 /* this isn't the pointer track */
3040 if (canvas_pointer_y_span > 0) {
3042 /* moving up the canvas */
3043 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3045 int32_t visible_tracks = 0;
3046 while (visible_tracks < canvas_pointer_y_span ) {
3049 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3050 /* we're passing through a hidden track */
3055 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3056 clamp_y_axis = true;
3060 clamp_y_axis = true;
3063 } else if (canvas_pointer_y_span < 0) {
3065 /*moving down the canvas*/
3067 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3070 int32_t visible_tracks = 0;
3072 while (visible_tracks > canvas_pointer_y_span ) {
3075 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3079 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3080 clamp_y_axis = true;
3085 clamp_y_axis = true;
3091 /* this is the pointer's track */
3092 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3093 clamp_y_axis = true;
3094 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3095 clamp_y_axis = true;
3103 } else if (drag_info.last_trackview == tv) {
3104 clamp_y_axis = true;
3108 if (!clamp_y_axis) {
3109 drag_info.last_trackview = tv;
3112 /************************************************************
3114 ************************************************************/
3116 /* compute the amount of pointer motion in frames, and where
3117 the region would be if we moved it by that much.
3120 if (drag_info.move_threshold_passed) {
3122 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3124 nframes_t sync_frame;
3125 nframes_t sync_offset;
3128 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3130 sync_offset = rv->region()->sync_offset (sync_dir);
3131 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3133 /* we snap if the snap modifier is not enabled.
3136 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3137 snap_to (sync_frame);
3140 if (sync_frame - sync_offset <= sync_frame) {
3141 pending_region_position = sync_frame - (sync_dir*sync_offset);
3143 pending_region_position = 0;
3147 pending_region_position = 0;
3150 if (pending_region_position > max_frames - rv->region()->length()) {
3151 pending_region_position = drag_info.last_frame_position;
3154 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3156 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3158 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3159 to make it appear at the new location.
3162 if (pending_region_position > drag_info.last_frame_position) {
3163 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3165 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3168 drag_info.last_frame_position = pending_region_position;
3175 /* threshold not passed */
3180 /*************************************************************
3182 ************************************************************/
3184 if (x_delta == 0 && (pointer_y_span == 0)) {
3185 /* haven't reached next snap point, and we're not switching
3186 trackviews. nothing to do.
3193 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3195 RegionView* rv2 = (*i);
3197 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3199 double ix1, ix2, iy1, iy2;
3200 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3201 rv2->get_canvas_group()->i2w (ix1, iy1);
3210 /*************************************************************
3212 ************************************************************/
3216 if (drag_info.first_move) {
3217 if (drag_info.move_threshold_passed) {
3228 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3229 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3231 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3233 RegionView* rv = (*i);
3234 double ix1, ix2, iy1, iy2;
3235 int32_t temp_pointer_y_span = pointer_y_span;
3237 /* get item BBox, which will be relative to parent. so we have
3238 to query on a child, then convert to world coordinates using
3242 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3243 rv->get_canvas_group()->i2w (ix1, iy1);
3244 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3245 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3246 RouteTimeAxisView* temp_rtv;
3248 if ((pointer_y_span != 0) && !clamp_y_axis) {
3251 for (j = height_list.begin(); j!= height_list.end(); j++) {
3252 if (x == canvas_rtv->order) {
3253 /* we found the track the region is on */
3254 if (x != original_pointer_order) {
3255 /*this isn't from the same track we're dragging from */
3256 temp_pointer_y_span = canvas_pointer_y_span;
3258 while (temp_pointer_y_span > 0) {
3259 /* we're moving up canvas-wise,
3260 so we need to find the next track height
3262 if (j != height_list.begin()) {
3265 if (x != original_pointer_order) {
3266 /* we're not from the dragged track, so ignore hidden tracks. */
3268 temp_pointer_y_span++;
3272 temp_pointer_y_span--;
3274 while (temp_pointer_y_span < 0) {
3276 if (x != original_pointer_order) {
3278 temp_pointer_y_span--;
3282 if (j != height_list.end()) {
3285 temp_pointer_y_span++;
3287 /* find out where we'll be when we move and set height accordingly */
3289 tvp2 = trackview_by_y_position (iy1 + y_delta);
3290 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3291 rv->set_y_position_and_height (0, temp_rtv->height);
3293 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3294 personally, i think this can confuse things, but never mind.
3297 //const GdkColor& col (temp_rtv->view->get_region_color());
3298 //rv->set_color (const_cast<GdkColor&>(col));
3305 /* prevent the regionview from being moved to before
3306 the zero position on the canvas.
3311 if (-x_delta > ix1) {
3314 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3315 x_delta = max_frames - rv->region()->last_frame();
3319 if (drag_info.first_move) {
3321 /* hide any dependent views */
3323 rv->get_time_axis_view().hide_dependent_views (*rv);
3325 /* this is subtle. raising the regionview itself won't help,
3326 because raise_to_top() just puts the item on the top of
3327 its parent's stack. so, we need to put the trackview canvas_display group
3328 on the top, since its parent is the whole canvas.
3331 rv->get_canvas_group()->raise_to_top();
3332 rv->get_time_axis_view().canvas_display->raise_to_top();
3333 cursor_group->raise_to_top();
3335 rv->fake_set_opaque (true);
3338 if (drag_info.brushing) {
3339 mouse_brush_insert_region (rv, pending_region_position);
3341 rv->move (x_delta, y_delta);
3344 } /* foreach region */
3348 if (drag_info.first_move && drag_info.move_threshold_passed) {
3349 cursor_group->raise_to_top();
3350 drag_info.first_move = false;
3353 if (x_delta != 0 && !drag_info.brushing) {
3354 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3359 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3362 RegionView* rvdi = reinterpret_cast<RegionView *> (drag_info.data);
3363 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3364 bool nocommit = true;
3366 RouteTimeAxisView* rtv;
3367 bool regionview_y_movement;
3368 bool regionview_x_movement;
3369 vector<RegionView*> copies;
3370 list <boost::shared_ptr<Playlist > > used_playlists;
3371 list <sigc::connection > used_connections;
3372 bool preserve_selection = false;
3374 /* first_move is set to false if the regionview has been moved in the
3378 if (drag_info.first_move) {
3385 /* The regionview has been moved at some stage during the grab so we need
3386 to account for any mouse movement between this event and the last one.
3389 region_drag_motion_callback (item, event);
3391 if (drag_info.brushing) {
3392 /* all changes were made during motion event handlers */
3394 if (drag_info.copy) {
3395 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3396 copies.push_back (*i);
3403 /* adjust for track speed */
3406 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3407 if (rtv && rtv->get_diskstream()) {
3408 speed = rtv->get_diskstream()->speed();
3411 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rvdi->region()->position()/speed));
3412 regionview_y_movement = (drag_info.last_trackview != &rvdi->get_time_axis_view());
3414 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3415 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3419 if (drag_info.copy) {
3420 if (drag_info.x_constrained) {
3421 op_string = _("fixed time region copy");
3423 op_string = _("region copy");
3426 if (drag_info.x_constrained) {
3427 op_string = _("fixed time region drag");
3429 op_string = _("region drag");
3433 begin_reversible_command (op_string);
3435 if (regionview_y_movement) {
3437 /* moved to a different audio track. */
3439 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3441 RegionView* rv = (*i);
3443 double ix1, ix2, iy1, iy2;
3445 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3446 rv->get_canvas_group()->i2w (ix1, iy1);
3448 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(trackview_by_y_position (iy1));
3450 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3452 if (! to_playlist->frozen()) {
3454 we haven't seen this playlist before.
3455 we want to freeze it because we don't want to relayer per-region.
3456 its much better to do that just once if the playlist is large.
3460 connect so the selection is changed when the new regionview finally appears (after thaw).
3461 keep track of it so we can disconnect later.
3464 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3465 used_connections.push_back (c);
3468 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3470 /* remember used playlists so we can thaw them later */
3471 used_playlists.push_back(to_playlist);
3472 to_playlist->freeze();
3475 where = (nframes_t) (unit_to_frame (ix1) * speed);
3476 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3478 if (!drag_info.copy) {
3481 /* the region that used to be in the old playlist is not
3482 moved to the new one - we make a copy of it. as a result,
3483 any existing editor for the region should no longer be
3487 RouteTimeAxisView* from_playlist_rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_trackview());
3488 boost::shared_ptr<Playlist> from_playlist = from_playlist_rtv->playlist();
3490 if (! from_playlist->frozen()) {
3491 from_playlist->freeze();
3492 used_playlists.push_back(from_playlist);
3494 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3495 used_connections.push_back (c);
3497 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3500 rv->hide_region_editor();
3501 rv->fake_set_opaque (false);
3503 from_playlist->remove_region ((rv->region()));
3507 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3509 copies.push_back (rv);
3512 latest_regionview = 0;
3514 to_playlist->add_region (new_region, where);
3516 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3517 was selected in all of them, then removing it from the playlist will have removed all
3518 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3519 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3520 corresponding regionview, and the selection is now empty).
3522 this could have invalidated any and all iterators into the region selection.
3524 the heuristic we use here is: if the region selection is empty, break out of the loop
3525 here. if the region selection is not empty, then restart the loop because we know that
3526 we must have removed at least the region(view) we've just been working on as well as any
3527 that we processed on previous iterations.
3529 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3530 we can just iterate.
3534 if (drag_info.copy) {
3537 if (selection->regions.empty()) {
3541 XXX see above .. but we just froze the playlists.. we have to keep iterating, right?
3544 //i = selection->regions.by_layer().begin();
3552 /* motion within a single track */
3554 list<RegionView*> regions = selection->regions.by_layer();
3556 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3558 RegionView* rv = (*i);
3559 boost::shared_ptr<Playlist> to_playlist = (*i)->region()->playlist();
3560 RouteTimeAxisView* from_rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3562 if (!rv->region()->can_move()) {
3566 if (regionview_x_movement) {
3567 double ownspeed = 1.0;
3569 if (from_rtv && from_rtv->get_diskstream()) {
3570 ownspeed = from_rtv->get_diskstream()->speed();
3573 /* base the new region position on the current position of the regionview.*/
3575 double ix1, ix2, iy1, iy2;
3577 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3578 rv->get_canvas_group()->i2w (ix1, iy1);
3579 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3583 where = rv->region()->position();
3586 if (! to_playlist->frozen()) {
3587 sigc::connection c = from_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3588 used_connections.push_back (c);
3591 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3593 used_playlists.push_back(to_playlist);
3594 to_playlist->freeze();
3597 if (drag_info.copy) {
3599 boost::shared_ptr<Region> newregion;
3600 boost::shared_ptr<Region> ar;
3601 boost::shared_ptr<Region> mr;
3603 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3604 newregion = RegionFactory::create (ar);
3605 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3606 newregion = RegionFactory::create (mr);
3611 to_playlist->add_region (newregion, (nframes_t) (where * from_rtv->get_diskstream()->speed()));
3613 /* if the original region was locked, we don't care for the new one */
3615 newregion->set_locked (false);
3616 copies.push_back (rv);
3620 /* just change the model */
3622 rv->region()->set_position (where, (void*) this);
3623 preserve_selection = true;
3630 if (! preserve_selection) {
3631 //selection->clear_regions();
3633 while (used_playlists.size() > 0) {
3635 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
3638 if (used_connections.size()) {
3639 sigc::connection c = used_connections.front();
3641 used_connections.pop_front();
3645 session->add_command (new MementoCommand<Playlist>(*(*i), 0, &(*i)->get_state()));
3646 used_playlists.pop_front();
3652 commit_reversible_command ();
3655 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3662 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3664 if (drag_info.move_threshold_passed) {
3665 if (drag_info.first_move) {
3666 // TODO: create region-create-drag region view here
3667 drag_info.first_move = false;
3670 // TODO: resize region-create-drag region view here
3675 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3677 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.last_trackview);
3681 const boost::shared_ptr<MidiDiskstream> diskstream =
3682 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
3685 warning << "Cannot create non-MIDI region" << endl;
3689 if (drag_info.first_move) {
3690 begin_reversible_command (_("create region"));
3691 XMLNode &before = mtv->playlist()->get_state();
3693 nframes_t start = drag_info.grab_frame;
3694 snap_to (start, -1);
3695 const Meter& m = session->tempo_map().meter_at(start);
3696 const Tempo& t = session->tempo_map().tempo_at(start);
3697 double length = m.frames_per_bar(t, session->frame_rate());
3699 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
3701 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>(RegionFactory::create(
3702 src, 0, length, PBD::basename_nosuffix(src->name()))), start);
3703 XMLNode &after = mtv->playlist()->get_state();
3704 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
3705 commit_reversible_command();
3708 create_region_drag_motion_callback (item, event);
3709 // TODO: create region-create-drag region here
3714 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3716 /* Either add to or set the set the region selection, unless
3717 this is an alignment click (control used)
3720 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3721 TimeAxisView* tv = &rv.get_time_axis_view();
3722 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3724 if (rtv && rtv->is_track()) {
3725 speed = rtv->get_diskstream()->speed();
3728 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3730 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3732 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3734 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3738 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3744 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3750 nframes_t frame_rate;
3757 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3758 case AudioClock::BBT:
3759 session->bbt_time (frame, bbt);
3760 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3763 case AudioClock::SMPTE:
3764 session->smpte_time (frame, smpte);
3765 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3768 case AudioClock::MinSec:
3769 /* XXX this is copied from show_verbose_duration_cursor() */
3770 frame_rate = session->frame_rate();
3771 hours = frame / (frame_rate * 3600);
3772 frame = frame % (frame_rate * 3600);
3773 mins = frame / (frame_rate * 60);
3774 frame = frame % (frame_rate * 60);
3775 secs = (float) frame / (float) frame_rate;
3776 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3780 snprintf (buf, sizeof(buf), "%u", frame);
3784 if (xpos >= 0 && ypos >=0) {
3785 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3788 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3790 show_verbose_canvas_cursor ();
3794 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3801 nframes_t distance, frame_rate;
3803 Meter meter_at_start(session->tempo_map().meter_at(start));
3809 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3810 case AudioClock::BBT:
3811 session->bbt_time (start, sbbt);
3812 session->bbt_time (end, ebbt);
3815 /* XXX this computation won't work well if the
3816 user makes a selection that spans any meter changes.
3819 ebbt.bars -= sbbt.bars;
3820 if (ebbt.beats >= sbbt.beats) {
3821 ebbt.beats -= sbbt.beats;
3824 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3826 if (ebbt.ticks >= sbbt.ticks) {
3827 ebbt.ticks -= sbbt.ticks;
3830 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3833 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3836 case AudioClock::SMPTE:
3837 session->smpte_duration (end - start, smpte);
3838 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3841 case AudioClock::MinSec:
3842 /* XXX this stuff should be elsewhere.. */
3843 distance = end - start;
3844 frame_rate = session->frame_rate();
3845 hours = distance / (frame_rate * 3600);
3846 distance = distance % (frame_rate * 3600);
3847 mins = distance / (frame_rate * 60);
3848 distance = distance % (frame_rate * 60);
3849 secs = (float) distance / (float) frame_rate;
3850 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3854 snprintf (buf, sizeof(buf), "%u", end - start);
3858 if (xpos >= 0 && ypos >=0) {
3859 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3862 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3864 show_verbose_canvas_cursor ();
3868 Editor::collect_new_region_view (RegionView* rv)
3870 latest_regionview = rv;
3874 Editor::collect_and_select_new_region_view (RegionView* rv)
3877 latest_regionview = rv;
3881 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3883 if (clicked_regionview == 0) {
3887 /* lets try to create new Region for the selection */
3889 vector<boost::shared_ptr<AudioRegion> > new_regions;
3890 create_region_from_selection (new_regions);
3892 if (new_regions.empty()) {
3896 /* XXX fix me one day to use all new regions */
3898 boost::shared_ptr<Region> region (new_regions.front());
3900 /* add it to the current stream/playlist.
3902 tricky: the streamview for the track will add a new regionview. we will
3903 catch the signal it sends when it creates the regionview to
3904 set the regionview we want to then drag.
3907 latest_regionview = 0;
3908 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3910 /* A selection grab currently creates two undo/redo operations, one for
3911 creating the new region and another for moving it.
3914 begin_reversible_command (_("selection grab"));
3916 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3918 XMLNode *before = &(playlist->get_state());
3919 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3920 XMLNode *after = &(playlist->get_state());
3921 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3923 commit_reversible_command ();
3927 if (latest_regionview == 0) {
3928 /* something went wrong */
3932 /* we need to deselect all other regionviews, and select this one
3933 i'm ignoring undo stuff, because the region creation will take care of it */
3934 //selection->set (latest_regionview);
3936 drag_info.item = latest_regionview->get_canvas_group();
3937 drag_info.data = latest_regionview;
3938 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3939 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3943 drag_info.last_trackview = clicked_axisview;
3944 drag_info.last_frame_position = latest_regionview->region()->position();
3945 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3947 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3951 Editor::cancel_selection ()
3953 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3954 (*i)->hide_selection ();
3956 begin_reversible_command (_("cancel selection"));
3957 selection->clear ();
3958 clicked_selection = 0;
3959 commit_reversible_command ();
3963 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3965 nframes_t start = 0;
3972 drag_info.item = item;
3973 drag_info.motion_callback = &Editor::drag_selection;
3974 drag_info.finished_callback = &Editor::end_selection_op;
3979 case CreateSelection:
3980 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3981 drag_info.copy = true;
3983 drag_info.copy = false;
3985 start_grab (event, selector_cursor);
3988 case SelectionStartTrim:
3989 if (clicked_axisview) {
3990 clicked_axisview->order_selection_trims (item, true);
3992 start_grab (event, trimmer_cursor);
3993 start = selection->time[clicked_selection].start;
3994 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3997 case SelectionEndTrim:
3998 if (clicked_axisview) {
3999 clicked_axisview->order_selection_trims (item, false);
4001 start_grab (event, trimmer_cursor);
4002 end = selection->time[clicked_selection].end;
4003 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4007 start = selection->time[clicked_selection].start;
4009 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4013 if (selection_op == SelectionMove) {
4014 show_verbose_time_cursor(start, 10);
4016 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4021 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4023 nframes_t start = 0;
4026 nframes_t pending_position;
4028 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4029 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4031 pending_position = 0;
4034 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4035 snap_to (pending_position);
4038 /* only alter selection if the current frame is
4039 different from the last frame position (adjusted)
4042 if (pending_position == drag_info.last_pointer_frame) return;
4044 switch (selection_op) {
4045 case CreateSelection:
4047 if (drag_info.first_move) {
4048 snap_to (drag_info.grab_frame);
4051 if (pending_position < drag_info.grab_frame) {
4052 start = pending_position;
4053 end = drag_info.grab_frame;
4055 end = pending_position;
4056 start = drag_info.grab_frame;
4059 /* first drag: Either add to the selection
4060 or create a new selection->
4063 if (drag_info.first_move) {
4065 begin_reversible_command (_("range selection"));
4067 if (drag_info.copy) {
4068 /* adding to the selection */
4069 clicked_selection = selection->add (start, end);
4070 drag_info.copy = false;
4072 /* new selection-> */
4073 clicked_selection = selection->set (clicked_axisview, start, end);
4078 case SelectionStartTrim:
4080 if (drag_info.first_move) {
4081 begin_reversible_command (_("trim selection start"));
4084 start = selection->time[clicked_selection].start;
4085 end = selection->time[clicked_selection].end;
4087 if (pending_position > end) {
4090 start = pending_position;
4094 case SelectionEndTrim:
4096 if (drag_info.first_move) {
4097 begin_reversible_command (_("trim selection end"));
4100 start = selection->time[clicked_selection].start;
4101 end = selection->time[clicked_selection].end;
4103 if (pending_position < start) {
4106 end = pending_position;
4113 if (drag_info.first_move) {
4114 begin_reversible_command (_("move selection"));
4117 start = selection->time[clicked_selection].start;
4118 end = selection->time[clicked_selection].end;
4120 length = end - start;
4122 start = pending_position;
4125 end = start + length;
4130 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4131 start_canvas_autoscroll (1);
4135 selection->replace (clicked_selection, start, end);
4138 drag_info.last_pointer_frame = pending_position;
4139 drag_info.first_move = false;
4141 if (selection_op == SelectionMove) {
4142 show_verbose_time_cursor(start, 10);
4144 show_verbose_time_cursor(pending_position, 10);
4149 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4151 if (!drag_info.first_move) {
4152 drag_selection (item, event);
4153 /* XXX this is not object-oriented programming at all. ick */
4154 if (selection->time.consolidate()) {
4155 selection->TimeChanged ();
4157 commit_reversible_command ();
4159 /* just a click, no pointer movement.*/
4161 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4163 selection->clear_time();
4168 /* XXX what happens if its a music selection? */
4169 session->set_audio_range (selection->time);
4170 stop_canvas_autoscroll ();
4174 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4177 TimeAxisView* tvp = clicked_axisview;
4178 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4180 if (tv && tv->is_track()) {
4181 speed = tv->get_diskstream()->speed();
4184 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4185 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4186 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4188 //drag_info.item = clicked_regionview->get_name_highlight();
4189 drag_info.item = item;
4190 drag_info.motion_callback = &Editor::trim_motion_callback;
4191 drag_info.finished_callback = &Editor::trim_finished_callback;
4193 start_grab (event, trimmer_cursor);
4195 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4196 trim_op = ContentsTrim;
4198 /* These will get overridden for a point trim.*/
4199 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4200 /* closer to start */
4201 trim_op = StartTrim;
4202 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4210 show_verbose_time_cursor(region_start, 10);
4213 show_verbose_time_cursor(region_end, 10);
4216 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4222 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4224 RegionView* rv = clicked_regionview;
4225 nframes_t frame_delta = 0;
4226 bool left_direction;
4227 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4229 /* snap modifier works differently here..
4230 its' current state has to be passed to the
4231 various trim functions in order to work properly
4235 TimeAxisView* tvp = clicked_axisview;
4236 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4237 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4239 if (tv && tv->is_track()) {
4240 speed = tv->get_diskstream()->speed();
4243 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4244 left_direction = true;
4246 left_direction = false;
4250 snap_to (drag_info.current_pointer_frame);
4253 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4257 if (drag_info.first_move) {
4263 trim_type = "Region start trim";
4266 trim_type = "Region end trim";
4269 trim_type = "Region content trim";
4273 begin_reversible_command (trim_type);
4275 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4276 (*i)->fake_set_opaque(false);
4277 (*i)->region()->freeze ();
4279 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4281 arv->temporarily_hide_envelope ();
4283 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4284 insert_result = motion_frozen_playlists.insert (pl);
4285 if (insert_result.second) {
4286 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4292 if (left_direction) {
4293 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4295 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4300 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4303 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4304 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4310 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4313 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4314 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4321 bool swap_direction = false;
4323 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4324 swap_direction = true;
4327 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4328 i != selection->regions.by_layer().end(); ++i)
4330 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4338 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4341 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4344 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4348 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4349 drag_info.first_move = false;
4353 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4355 boost::shared_ptr<Region> region (rv.region());
4357 if (region->locked()) {
4361 nframes_t new_bound;
4364 TimeAxisView* tvp = clicked_axisview;
4365 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4367 if (tv && tv->is_track()) {
4368 speed = tv->get_diskstream()->speed();
4371 if (left_direction) {
4372 if (swap_direction) {
4373 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4375 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4378 if (swap_direction) {
4379 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4381 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4386 snap_to (new_bound);
4388 region->trim_start ((nframes_t) (new_bound * speed), this);
4389 rv.region_changed (StartChanged);
4393 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4395 boost::shared_ptr<Region> region (rv.region());
4397 if (region->locked()) {
4401 nframes_t new_bound;
4404 TimeAxisView* tvp = clicked_axisview;
4405 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4407 if (tv && tv->is_track()) {
4408 speed = tv->get_diskstream()->speed();
4411 if (left_direction) {
4412 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4414 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4418 snap_to (new_bound, (left_direction ? 0 : 1));
4421 region->trim_front ((nframes_t) (new_bound * speed), this);
4423 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4427 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4429 boost::shared_ptr<Region> region (rv.region());
4431 if (region->locked()) {
4435 nframes_t new_bound;
4438 TimeAxisView* tvp = clicked_axisview;
4439 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4441 if (tv && tv->is_track()) {
4442 speed = tv->get_diskstream()->speed();
4445 if (left_direction) {
4446 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4448 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4452 snap_to (new_bound);
4454 region->trim_end ((nframes_t) (new_bound * speed), this);
4455 rv.region_changed (LengthChanged);
4459 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4461 if (!drag_info.first_move) {
4462 trim_motion_callback (item, event);
4464 if (!clicked_regionview->get_selected()) {
4465 thaw_region_after_trim (*clicked_regionview);
4468 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4469 i != selection->regions.by_layer().end(); ++i)
4471 thaw_region_after_trim (**i);
4472 (*i)->fake_set_opaque (true);
4476 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4478 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4481 motion_frozen_playlists.clear ();
4483 commit_reversible_command();
4485 /* no mouse movement */
4491 Editor::point_trim (GdkEvent* event)
4493 RegionView* rv = clicked_regionview;
4494 nframes_t new_bound = drag_info.current_pointer_frame;
4496 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4497 snap_to (new_bound);
4500 /* Choose action dependant on which button was pressed */
4501 switch (event->button.button) {
4503 trim_op = StartTrim;
4504 begin_reversible_command (_("Start point trim"));
4506 if (rv->get_selected()) {
4508 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4509 i != selection->regions.by_layer().end(); ++i)
4511 if (!(*i)->region()->locked()) {
4512 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4513 XMLNode &before = pl->get_state();
4514 (*i)->region()->trim_front (new_bound, this);
4515 XMLNode &after = pl->get_state();
4516 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4522 if (!rv->region()->locked()) {
4523 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4524 XMLNode &before = pl->get_state();
4525 rv->region()->trim_front (new_bound, this);
4526 XMLNode &after = pl->get_state();
4527 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4531 commit_reversible_command();
4536 begin_reversible_command (_("End point trim"));
4538 if (rv->get_selected()) {
4540 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4542 if (!(*i)->region()->locked()) {
4543 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4544 XMLNode &before = pl->get_state();
4545 (*i)->region()->trim_end (new_bound, this);
4546 XMLNode &after = pl->get_state();
4547 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4553 if (!rv->region()->locked()) {
4554 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4555 XMLNode &before = pl->get_state();
4556 rv->region()->trim_end (new_bound, this);
4557 XMLNode &after = pl->get_state();
4558 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4562 commit_reversible_command();
4571 Editor::thaw_region_after_trim (RegionView& rv)
4573 boost::shared_ptr<Region> region (rv.region());
4575 if (region->locked()) {
4579 region->thaw (_("trimmed region"));
4580 XMLNode &after = region->playlist()->get_state();
4581 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4583 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4585 arv->unhide_envelope ();
4589 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4594 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4595 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4599 Location* location = find_location_from_marker (marker, is_start);
4600 location->set_hidden (true, this);
4605 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4611 drag_info.item = item;
4612 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4613 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4615 range_marker_op = op;
4617 if (!temp_location) {
4618 temp_location = new Location;
4622 case CreateRangeMarker:
4623 case CreateTransportMarker:
4625 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4626 drag_info.copy = true;
4628 drag_info.copy = false;
4630 start_grab (event, selector_cursor);
4634 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4639 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4641 nframes_t start = 0;
4643 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4645 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4646 snap_to (drag_info.current_pointer_frame);
4649 /* only alter selection if the current frame is
4650 different from the last frame position.
4653 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4655 switch (range_marker_op) {
4656 case CreateRangeMarker:
4657 case CreateTransportMarker:
4658 if (drag_info.first_move) {
4659 snap_to (drag_info.grab_frame);
4662 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4663 start = drag_info.current_pointer_frame;
4664 end = drag_info.grab_frame;
4666 end = drag_info.current_pointer_frame;
4667 start = drag_info.grab_frame;
4670 /* first drag: Either add to the selection
4671 or create a new selection.
4674 if (drag_info.first_move) {
4676 temp_location->set (start, end);
4680 update_marker_drag_item (temp_location);
4681 range_marker_drag_rect->show();
4682 range_marker_drag_rect->raise_to_top();
4688 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4689 start_canvas_autoscroll (1);
4693 temp_location->set (start, end);
4695 double x1 = frame_to_pixel (start);
4696 double x2 = frame_to_pixel (end);
4697 crect->property_x1() = x1;
4698 crect->property_x2() = x2;
4700 update_marker_drag_item (temp_location);
4703 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4704 drag_info.first_move = false;
4706 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4711 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4713 Location * newloc = 0;
4716 if (!drag_info.first_move) {
4717 drag_range_markerbar_op (item, event);
4719 switch (range_marker_op) {
4720 case CreateRangeMarker:
4722 begin_reversible_command (_("new range marker"));
4723 XMLNode &before = session->locations()->get_state();
4724 session->locations()->next_available_name(rangename,"unnamed");
4725 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4726 session->locations()->add (newloc, true);
4727 XMLNode &after = session->locations()->get_state();
4728 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4729 commit_reversible_command ();
4731 range_bar_drag_rect->hide();
4732 range_marker_drag_rect->hide();
4736 case CreateTransportMarker:
4737 // popup menu to pick loop or punch
4738 new_transport_marker_context_menu (&event->button, item);
4743 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4745 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4750 start = session->locations()->first_mark_before (drag_info.grab_frame);
4751 end = session->locations()->first_mark_after (drag_info.grab_frame);
4753 if (end == max_frames) {
4754 end = session->current_end_frame ();
4758 start = session->current_start_frame ();
4761 switch (mouse_mode) {
4763 /* find the two markers on either side and then make the selection from it */
4764 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4768 /* find the two markers on either side of the click and make the range out of it */
4769 selection->set (0, start, end);
4778 stop_canvas_autoscroll ();
4784 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4786 drag_info.item = item;
4787 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4788 drag_info.finished_callback = &Editor::end_mouse_zoom;
4790 start_grab (event, zoom_cursor);
4792 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4796 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4801 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4802 snap_to (drag_info.current_pointer_frame);
4804 if (drag_info.first_move) {
4805 snap_to (drag_info.grab_frame);
4809 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4811 /* base start and end on initial click position */
4812 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4813 start = drag_info.current_pointer_frame;
4814 end = drag_info.grab_frame;
4816 end = drag_info.current_pointer_frame;
4817 start = drag_info.grab_frame;
4822 if (drag_info.first_move) {
4824 zoom_rect->raise_to_top();
4827 reposition_zoom_rect(start, end);
4829 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4830 drag_info.first_move = false;
4832 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4837 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4839 if (!drag_info.first_move) {
4840 drag_mouse_zoom (item, event);
4842 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4843 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4845 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4848 temporal_zoom_to_frame (false, drag_info.grab_frame);
4850 temporal_zoom_step (false);
4851 center_screen (drag_info.grab_frame);
4859 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4861 double x1 = frame_to_pixel (start);
4862 double x2 = frame_to_pixel (end);
4863 double y2 = full_canvas_height - 1.0;
4865 zoom_rect->property_x1() = x1;
4866 zoom_rect->property_y1() = 1.0;
4867 zoom_rect->property_x2() = x2;
4868 zoom_rect->property_y2() = y2;
4872 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4874 drag_info.item = item;
4875 drag_info.motion_callback = &Editor::drag_rubberband_select;
4876 drag_info.finished_callback = &Editor::end_rubberband_select;
4878 start_grab (event, cross_hair_cursor);
4880 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4884 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4891 /* use a bigger drag threshold than the default */
4893 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4897 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4898 if (drag_info.first_move) {
4899 snap_to (drag_info.grab_frame);
4901 snap_to (drag_info.current_pointer_frame);
4904 /* base start and end on initial click position */
4906 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4907 start = drag_info.current_pointer_frame;
4908 end = drag_info.grab_frame;
4910 end = drag_info.current_pointer_frame;
4911 start = drag_info.grab_frame;
4914 if (drag_info.current_pointer_y < drag_info.grab_y) {
4915 y1 = drag_info.current_pointer_y;
4916 y2 = drag_info.grab_y;
4918 y2 = drag_info.current_pointer_y;
4919 y1 = drag_info.grab_y;
4923 if (start != end || y1 != y2) {
4925 double x1 = frame_to_pixel (start);
4926 double x2 = frame_to_pixel (end);
4928 rubberband_rect->property_x1() = x1;
4929 rubberband_rect->property_y1() = y1;
4930 rubberband_rect->property_x2() = x2;
4931 rubberband_rect->property_y2() = y2;
4933 rubberband_rect->show();
4934 rubberband_rect->raise_to_top();
4936 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4937 drag_info.first_move = false;
4939 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4944 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4946 if (!drag_info.first_move) {
4948 drag_rubberband_select (item, event);
4951 if (drag_info.current_pointer_y < drag_info.grab_y) {
4952 y1 = drag_info.current_pointer_y;
4953 y2 = drag_info.grab_y;
4956 y2 = drag_info.current_pointer_y;
4957 y1 = drag_info.grab_y;
4961 Selection::Operation op = Keyboard::selection_type (event->button.state);
4964 begin_reversible_command (_("rubberband selection"));
4966 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4967 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4969 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4973 commit_reversible_command ();
4977 selection->clear_tracks();
4978 selection->clear_regions();
4979 selection->clear_points ();
4980 selection->clear_lines ();
4983 rubberband_rect->hide();
4988 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4990 using namespace Gtkmm2ext;
4992 ArdourPrompter prompter (false);
4994 prompter.set_prompt (_("Name for region:"));
4995 prompter.set_initial_text (clicked_regionview->region()->name());
4996 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4997 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4998 prompter.show_all ();
4999 switch (prompter.run ()) {
5000 case Gtk::RESPONSE_ACCEPT:
5002 prompter.get_result(str);
5004 clicked_regionview->region()->set_name (str);
5012 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5014 drag_info.item = item;
5015 drag_info.motion_callback = &Editor::time_fx_motion;
5016 drag_info.finished_callback = &Editor::end_time_fx;
5020 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5024 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5026 RegionView* rv = clicked_regionview;
5028 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5029 snap_to (drag_info.current_pointer_frame);
5032 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5036 if (drag_info.current_pointer_frame > rv->region()->position()) {
5037 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5040 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5041 drag_info.first_move = false;
5043 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5047 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5049 clicked_regionview->get_time_axis_view().hide_timestretch ();
5051 if (drag_info.first_move) {
5055 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5056 /* backwards drag of the left edge - not usable */
5060 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5061 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5063 begin_reversible_command (_("timestretch"));
5065 if (run_timestretch (selection->regions, percentage) == 0) {
5066 session->commit_reversible_command ();
5071 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5073 /* no brushing without a useful snap setting */
5076 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5079 switch (snap_mode) {
5081 return; /* can't work because it allows region to be placed anywhere */
5086 switch (snap_type) {
5089 case SnapToEditCursor:
5096 /* don't brush a copy over the original */
5098 if (pos == rv->region()->position()) {
5102 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5104 if (rtv == 0 || !rtv->is_track()) {
5108 boost::shared_ptr<Playlist> playlist = rtv->playlist();
5109 double speed = rtv->get_diskstream()->speed();
5111 XMLNode &before = playlist->get_state();
5112 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5113 XMLNode &after = playlist->get_state();
5114 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5116 // playlist is frozen, so we have to update manually
5118 playlist->Modified(); /* EMIT SIGNAL */
5122 Editor::track_height_step_timeout ()
5125 struct timeval delta;
5127 gettimeofday (&now, 0);
5128 timersub (&now, &last_track_height_step_timestamp, &delta);
5130 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5131 current_stepping_trackview = 0;
5139 Editor::add_mouse_speed (double speed, double dir)
5143 mouse_direction = dir;
5145 index = mouse_speed_entries;
5147 if (++index >= mouse_speed_size) {
5149 have_full_mouse_speed = true;
5152 mouse_speed[index] = speed;
5153 mouse_speed_entries = index;
5157 Editor::compute_mouse_speed ()
5161 if (!have_full_mouse_speed) {
5163 /* partial speed buffer, just use whatever we have so far */
5165 if (mouse_speed_entries == 0 ) {
5169 for (size_t n = 0; n < mouse_speed_entries; ++n) {
5170 total += mouse_speed[n];
5173 return mouse_direction * total/mouse_speed_entries;
5176 /* compute the average (effectively low-pass filtering) mouse speed
5177 across the entire buffer.
5180 for (size_t n = 0; n < mouse_speed_size; ++n) {
5181 total += mouse_speed[n];
5185 return mouse_direction * total/mouse_speed_size;
5189 Editor::update_mouse_speed ()
5194 session->request_transport_speed (0.0);
5195 mouse_speed_update = -1;
5199 speed = compute_mouse_speed ();
5201 struct timeval tmnow;
5203 gettimeofday (&tmnow, 0);
5204 double now = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec;
5206 if (now - last_scrub_time > 250000) {
5208 // 0.25 seconds since last mouse motion, start to brake
5210 if (fabs (speed) < 0.1) {
5211 /* don't asymptotically approach zero */
5212 memset (mouse_speed, 0, sizeof (double) * mouse_speed_size);
5214 } else if (fabs (speed) < 0.25) {
5215 add_mouse_speed (fabs (speed * 0.2), mouse_direction);
5217 add_mouse_speed (fabs (speed * 0.6), mouse_direction);
5221 session->request_transport_speed (speed);