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.last_pointer_x = drag_info.current_pointer_x;
1504 drag_info.last_pointer_y = drag_info.current_pointer_y;
1505 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1506 &drag_info.current_pointer_y);
1508 switch (mouse_mode) {
1511 struct timeval tmnow;
1513 if (last_scrub_frame == 0) {
1515 /* first motion, just set up the variables */
1517 last_scrub_frame = (nframes64_t) drag_info.current_pointer_frame;
1518 gettimeofday (&tmnow, 0);
1519 last_scrub_time = tmnow.tv_sec * 1000000.0 + tmnow.tv_usec;
1520 session->request_locate (last_scrub_frame, true);
1523 /* how fast is the mouse moving ? */
1531 if (last_scrub_frame < (nframes64_t) drag_info.current_pointer_frame) {
1532 distance = (nframes64_t) drag_info.current_pointer_frame - last_scrub_frame;
1535 distance = last_scrub_frame - (nframes64_t) drag_info.current_pointer_frame;
1539 if (drag_info.grab_x < drag_info.current_pointer_x) {
1540 distance = drag_info.current_pointer_x - drag_info.grab_x;
1543 distance = drag_info.grab_x - drag_info.current_pointer_x;
1548 gettimeofday (&tmnow, 0);
1549 time = (tmnow.tv_sec * 1000000.0 + tmnow.tv_usec) - last_scrub_time;
1550 last_scrub_frame = drag_info.current_pointer_frame;
1551 last_scrub_time = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec;
1552 speed = ((double)distance/session->frame_rate()) / (time/1000000.0); // frames/sec
1554 add_mouse_speed (speed, dir);
1556 if (mouse_speed_update < 0) {
1557 mouse_speed_update = g_timeout_add (10, _update_mouse_speed, this);
1558 update_mouse_speed ();
1568 if (!from_autoscroll && drag_info.item) {
1569 /* item != 0 is the best test i can think of for dragging.
1571 if (!drag_info.move_threshold_passed) {
1573 bool x_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1574 bool y_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1576 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1578 // and change the initial grab loc/frame if this drag info wants us to
1580 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1581 drag_info.grab_frame = drag_info.current_pointer_frame;
1582 drag_info.grab_x = drag_info.current_pointer_x;
1583 drag_info.grab_y = drag_info.current_pointer_y;
1584 drag_info.last_pointer_frame = drag_info.grab_frame;
1585 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1590 switch (item_type) {
1591 case PlayheadCursorItem:
1592 case EditCursorItem:
1594 case ControlPointItem:
1595 case TempoMarkerItem:
1596 case MeterMarkerItem:
1597 case RegionViewNameHighlight:
1598 case StartSelectionTrimItem:
1599 case EndSelectionTrimItem:
1602 case AutomationLineItem:
1603 case FadeInHandleItem:
1604 case FadeOutHandleItem:
1607 case ImageFrameHandleStartItem:
1608 case ImageFrameHandleEndItem:
1609 case MarkerViewHandleStartItem:
1610 case MarkerViewHandleEndItem:
1613 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1614 (event->motion.state & Gdk::BUTTON2_MASK))) {
1615 if (!from_autoscroll) {
1616 maybe_autoscroll (event);
1618 (this->*(drag_info.motion_callback)) (item, event);
1627 switch (mouse_mode) {
1633 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1634 (event->motion.state & GDK_BUTTON2_MASK))) {
1635 if (!from_autoscroll) {
1636 maybe_autoscroll (event);
1638 (this->*(drag_info.motion_callback)) (item, event);
1649 track_canvas_motion (event);
1650 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1658 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1660 if (drag_info.item == 0) {
1661 fatal << _("programming error: start_grab called without drag item") << endmsg;
1667 cursor = grabber_cursor;
1670 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1672 if (event->button.button == 2) {
1673 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1674 drag_info.y_constrained = true;
1675 drag_info.x_constrained = false;
1677 drag_info.y_constrained = false;
1678 drag_info.x_constrained = true;
1681 drag_info.x_constrained = false;
1682 drag_info.y_constrained = false;
1685 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1686 drag_info.last_pointer_frame = drag_info.grab_frame;
1687 drag_info.current_pointer_frame = drag_info.grab_frame;
1688 drag_info.current_pointer_x = drag_info.grab_x;
1689 drag_info.current_pointer_y = drag_info.grab_y;
1690 drag_info.last_pointer_x = drag_info.current_pointer_x;
1691 drag_info.last_pointer_y = drag_info.current_pointer_y;
1692 drag_info.cumulative_x_drag = 0;
1693 drag_info.cumulative_y_drag = 0;
1694 drag_info.first_move = true;
1695 drag_info.move_threshold_passed = false;
1696 drag_info.want_move_threshold = false;
1697 drag_info.pointer_frame_offset = 0;
1698 drag_info.brushing = false;
1699 drag_info.copied_location = 0;
1701 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1703 event->button.time);
1705 if (session && session->transport_rolling()) {
1706 drag_info.was_rolling = true;
1708 drag_info.was_rolling = false;
1711 switch (snap_type) {
1712 case SnapToRegionStart:
1713 case SnapToRegionEnd:
1714 case SnapToRegionSync:
1715 case SnapToRegionBoundary:
1716 build_region_boundary_cache ();
1724 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1726 drag_info.item->ungrab (0);
1727 drag_info.item = new_item;
1730 cursor = grabber_cursor;
1733 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1737 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1739 bool did_drag = false;
1741 stop_canvas_autoscroll ();
1743 if (drag_info.item == 0) {
1747 drag_info.item->ungrab (event->button.time);
1749 if (drag_info.finished_callback) {
1750 drag_info.last_pointer_x = drag_info.current_pointer_x;
1751 drag_info.last_pointer_y = drag_info.current_pointer_y;
1752 (this->*(drag_info.finished_callback)) (item, event);
1755 did_drag = !drag_info.first_move;
1757 hide_verbose_canvas_cursor();
1760 drag_info.copy = false;
1761 drag_info.motion_callback = 0;
1762 drag_info.finished_callback = 0;
1763 drag_info.last_trackview = 0;
1764 drag_info.last_frame_position = 0;
1765 drag_info.grab_frame = 0;
1766 drag_info.last_pointer_frame = 0;
1767 drag_info.current_pointer_frame = 0;
1768 drag_info.brushing = false;
1770 if (drag_info.copied_location) {
1771 delete drag_info.copied_location;
1772 drag_info.copied_location = 0;
1779 Editor::set_edit_cursor (GdkEvent* event)
1781 nframes_t pointer_frame = event_frame (event);
1783 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1784 if (snap_type != SnapToEditCursor) {
1785 snap_to (pointer_frame);
1789 edit_cursor->set_position (pointer_frame);
1790 edit_cursor_clock.set (pointer_frame);
1794 Editor::set_playhead_cursor (GdkEvent* event)
1796 nframes_t pointer_frame = event_frame (event);
1798 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1799 snap_to (pointer_frame);
1803 session->request_locate (pointer_frame, session->transport_rolling());
1808 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1810 drag_info.item = item;
1811 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1812 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1816 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1817 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1821 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1823 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
1827 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1829 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1831 nframes_t fade_length;
1833 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1834 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1840 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1844 if (pos < (arv->region()->position() + 64)) {
1845 fade_length = 64; // this should be a minimum defined somewhere
1846 } else if (pos > arv->region()->last_frame()) {
1847 fade_length = arv->region()->length();
1849 fade_length = pos - arv->region()->position();
1851 /* mapover the region selection */
1853 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1855 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1861 tmp->reset_fade_in_shape_width (fade_length);
1864 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1866 drag_info.first_move = false;
1870 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1872 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1874 nframes_t fade_length;
1876 if (drag_info.first_move) return;
1878 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1879 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1884 if (pos < (arv->region()->position() + 64)) {
1885 fade_length = 64; // this should be a minimum defined somewhere
1886 } else if (pos > arv->region()->last_frame()) {
1887 fade_length = arv->region()->length();
1889 fade_length = pos - arv->region()->position();
1892 begin_reversible_command (_("change fade in length"));
1894 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1896 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1902 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
1903 XMLNode &before = alist->get_state();
1905 tmp->audio_region()->set_fade_in_length (fade_length);
1907 XMLNode &after = alist->get_state();
1908 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1911 commit_reversible_command ();
1915 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1917 drag_info.item = item;
1918 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1919 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1923 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1924 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1928 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1930 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
1934 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1936 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1938 nframes_t fade_length;
1940 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1941 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1946 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1950 if (pos > (arv->region()->last_frame() - 64)) {
1951 fade_length = 64; // this should really be a minimum fade defined somewhere
1953 else if (pos < arv->region()->position()) {
1954 fade_length = arv->region()->length();
1957 fade_length = arv->region()->last_frame() - pos;
1960 /* mapover the region selection */
1962 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1964 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1970 tmp->reset_fade_out_shape_width (fade_length);
1973 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1975 drag_info.first_move = false;
1979 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1981 if (drag_info.first_move) return;
1983 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1985 nframes_t fade_length;
1987 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1988 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1994 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1998 if (pos > (arv->region()->last_frame() - 64)) {
1999 fade_length = 64; // this should really be a minimum fade defined somewhere
2001 else if (pos < arv->region()->position()) {
2002 fade_length = arv->region()->length();
2005 fade_length = arv->region()->last_frame() - pos;
2008 begin_reversible_command (_("change fade out length"));
2010 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2012 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2018 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2019 XMLNode &before = alist->get_state();
2021 tmp->audio_region()->set_fade_out_length (fade_length);
2023 XMLNode &after = alist->get_state();
2024 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2027 commit_reversible_command ();
2031 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2033 drag_info.item = item;
2034 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2035 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2039 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2040 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2044 Cursor* cursor = (Cursor *) drag_info.data;
2046 if (cursor == playhead_cursor) {
2047 _dragging_playhead = true;
2049 if (session && drag_info.was_rolling) {
2050 session->request_stop ();
2053 if (session && session->is_auditioning()) {
2054 session->cancel_audition ();
2058 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2060 show_verbose_time_cursor (cursor->current_frame, 10);
2064 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2066 Cursor* cursor = (Cursor *) drag_info.data;
2067 nframes_t adjusted_frame;
2069 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2070 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2076 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2077 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
2078 snap_to (adjusted_frame);
2082 if (adjusted_frame == drag_info.last_pointer_frame) return;
2084 cursor->set_position (adjusted_frame);
2086 if (cursor == edit_cursor) {
2087 edit_cursor_clock.set (cursor->current_frame);
2089 UpdateAllTransportClocks (cursor->current_frame);
2092 show_verbose_time_cursor (cursor->current_frame, 10);
2094 drag_info.last_pointer_frame = adjusted_frame;
2095 drag_info.first_move = false;
2099 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2101 if (drag_info.first_move) return;
2103 cursor_drag_motion_callback (item, event);
2105 _dragging_playhead = false;
2107 if (item == &playhead_cursor->canvas_item) {
2109 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2111 } else if (item == &edit_cursor->canvas_item) {
2112 edit_cursor->set_position (edit_cursor->current_frame);
2113 edit_cursor_clock.set (edit_cursor->current_frame);
2118 Editor::update_marker_drag_item (Location *location)
2120 double x1 = frame_to_pixel (location->start());
2121 double x2 = frame_to_pixel (location->end());
2123 if (location->is_mark()) {
2124 marker_drag_line_points.front().set_x(x1);
2125 marker_drag_line_points.back().set_x(x1);
2126 marker_drag_line->property_points() = marker_drag_line_points;
2129 range_marker_drag_rect->property_x1() = x1;
2130 range_marker_drag_rect->property_x2() = x2;
2135 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2139 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2140 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2146 Location *location = find_location_from_marker (marker, is_start);
2148 drag_info.item = item;
2149 drag_info.data = marker;
2150 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2151 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2155 drag_info.copied_location = new Location (*location);
2156 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2158 update_marker_drag_item (location);
2160 if (location->is_mark()) {
2161 marker_drag_line->show();
2162 marker_drag_line->raise_to_top();
2165 range_marker_drag_rect->show();
2166 range_marker_drag_rect->raise_to_top();
2169 if (is_start) show_verbose_time_cursor (location->start(), 10);
2170 else show_verbose_time_cursor (location->end(), 10);
2174 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2177 Marker* marker = (Marker *) drag_info.data;
2178 Location *real_location;
2179 Location *copy_location;
2181 bool move_both = false;
2185 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2186 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2191 nframes_t next = newframe;
2193 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2194 snap_to (newframe, 0, true);
2197 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2201 /* call this to find out if its the start or end */
2203 real_location = find_location_from_marker (marker, is_start);
2205 /* use the copy that we're "dragging" around */
2207 copy_location = drag_info.copied_location;
2209 f_delta = copy_location->end() - copy_location->start();
2211 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2215 if (copy_location->is_mark()) {
2218 copy_location->set_start (newframe);
2222 if (is_start) { // start-of-range marker
2225 copy_location->set_start (newframe);
2226 copy_location->set_end (newframe + f_delta);
2227 } else if (newframe < copy_location->end()) {
2228 copy_location->set_start (newframe);
2230 snap_to (next, 1, true);
2231 copy_location->set_end (next);
2232 copy_location->set_start (newframe);
2235 } else { // end marker
2238 copy_location->set_end (newframe);
2239 copy_location->set_start (newframe - f_delta);
2240 } else if (newframe > copy_location->start()) {
2241 copy_location->set_end (newframe);
2243 } else if (newframe > 0) {
2244 snap_to (next, -1, true);
2245 copy_location->set_start (next);
2246 copy_location->set_end (newframe);
2251 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2252 drag_info.first_move = false;
2254 update_marker_drag_item (copy_location);
2256 LocationMarkers* lm = find_location_markers (real_location);
2257 lm->set_position (copy_location->start(), copy_location->end());
2259 show_verbose_time_cursor (newframe, 10);
2263 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2265 if (drag_info.first_move) {
2266 marker_drag_motion_callback (item, event);
2270 Marker* marker = (Marker *) drag_info.data;
2274 begin_reversible_command ( _("move marker") );
2275 XMLNode &before = session->locations()->get_state();
2277 Location * location = find_location_from_marker (marker, is_start);
2280 if (location->is_mark()) {
2281 location->set_start (drag_info.copied_location->start());
2283 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2287 XMLNode &after = session->locations()->get_state();
2288 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2289 commit_reversible_command ();
2291 marker_drag_line->hide();
2292 range_marker_drag_rect->hide();
2296 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2299 MeterMarker* meter_marker;
2301 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2302 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2306 meter_marker = dynamic_cast<MeterMarker*> (marker);
2308 MetricSection& section (meter_marker->meter());
2310 if (!section.movable()) {
2314 drag_info.item = item;
2315 drag_info.data = marker;
2316 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2317 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2321 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2323 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2327 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2330 MeterMarker* meter_marker;
2332 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2333 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2337 meter_marker = dynamic_cast<MeterMarker*> (marker);
2339 // create a dummy marker for visual representation of moving the copy.
2340 // The actual copying is not done before we reach the finish callback.
2342 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2343 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2344 *new MeterSection(meter_marker->meter()));
2346 drag_info.item = &new_marker->the_item();
2347 drag_info.copy = true;
2348 drag_info.data = new_marker;
2349 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2350 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2354 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2356 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2360 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2362 MeterMarker* marker = (MeterMarker *) drag_info.data;
2363 nframes_t adjusted_frame;
2365 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2366 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2372 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2373 snap_to (adjusted_frame);
2376 if (adjusted_frame == drag_info.last_pointer_frame) return;
2378 marker->set_position (adjusted_frame);
2381 drag_info.last_pointer_frame = adjusted_frame;
2382 drag_info.first_move = false;
2384 show_verbose_time_cursor (adjusted_frame, 10);
2388 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2390 if (drag_info.first_move) return;
2392 meter_marker_drag_motion_callback (drag_info.item, event);
2394 MeterMarker* marker = (MeterMarker *) drag_info.data;
2397 TempoMap& map (session->tempo_map());
2398 map.bbt_time (drag_info.last_pointer_frame, when);
2400 if (drag_info.copy == true) {
2401 begin_reversible_command (_("copy meter mark"));
2402 XMLNode &before = map.get_state();
2403 map.add_meter (marker->meter(), when);
2404 XMLNode &after = map.get_state();
2405 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2406 commit_reversible_command ();
2408 // delete the dummy marker we used for visual representation of copying.
2409 // a new visual marker will show up automatically.
2412 begin_reversible_command (_("move meter mark"));
2413 XMLNode &before = map.get_state();
2414 map.move_meter (marker->meter(), when);
2415 XMLNode &after = map.get_state();
2416 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2417 commit_reversible_command ();
2422 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2425 TempoMarker* tempo_marker;
2427 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2428 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2432 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2433 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2437 MetricSection& section (tempo_marker->tempo());
2439 if (!section.movable()) {
2443 drag_info.item = item;
2444 drag_info.data = marker;
2445 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2446 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2450 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2451 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2455 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2458 TempoMarker* tempo_marker;
2460 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2461 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2465 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2466 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2470 // create a dummy marker for visual representation of moving the copy.
2471 // The actual copying is not done before we reach the finish callback.
2473 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2474 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2475 *new TempoSection(tempo_marker->tempo()));
2477 drag_info.item = &new_marker->the_item();
2478 drag_info.copy = true;
2479 drag_info.data = new_marker;
2480 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2481 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2485 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2487 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2491 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2493 TempoMarker* marker = (TempoMarker *) drag_info.data;
2494 nframes_t adjusted_frame;
2496 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2497 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2503 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2504 snap_to (adjusted_frame);
2507 if (adjusted_frame == drag_info.last_pointer_frame) return;
2509 /* OK, we've moved far enough to make it worth actually move the thing. */
2511 marker->set_position (adjusted_frame);
2513 show_verbose_time_cursor (adjusted_frame, 10);
2515 drag_info.last_pointer_frame = adjusted_frame;
2516 drag_info.first_move = false;
2520 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2522 if (drag_info.first_move) return;
2524 tempo_marker_drag_motion_callback (drag_info.item, event);
2526 TempoMarker* marker = (TempoMarker *) drag_info.data;
2529 TempoMap& map (session->tempo_map());
2530 map.bbt_time (drag_info.last_pointer_frame, when);
2532 if (drag_info.copy == true) {
2533 begin_reversible_command (_("copy tempo mark"));
2534 XMLNode &before = map.get_state();
2535 map.add_tempo (marker->tempo(), when);
2536 XMLNode &after = map.get_state();
2537 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2538 commit_reversible_command ();
2540 // delete the dummy marker we used for visual representation of copying.
2541 // a new visual marker will show up automatically.
2544 begin_reversible_command (_("move tempo mark"));
2545 XMLNode &before = map.get_state();
2546 map.move_tempo (marker->tempo(), when);
2547 XMLNode &after = map.get_state();
2548 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2549 commit_reversible_command ();
2554 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2556 ControlPoint* control_point;
2558 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2559 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2563 // We shouldn't remove the first or last gain point
2564 if (control_point->line().is_last_point(*control_point) ||
2565 control_point->line().is_first_point(*control_point)) {
2569 control_point->line().remove_point (*control_point);
2573 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2575 ControlPoint* control_point;
2577 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2578 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2582 control_point->line().remove_point (*control_point);
2586 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2588 ControlPoint* control_point;
2590 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2591 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2595 drag_info.item = item;
2596 drag_info.data = control_point;
2597 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2598 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2600 start_grab (event, fader_cursor);
2602 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2604 double fraction = 1.0 - ((control_point->get_y() - control_point->line().y_position()) / (double)control_point->line().height());
2605 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
2606 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2608 show_verbose_canvas_cursor ();
2612 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2614 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2616 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2617 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2619 if (event->button.state & Keyboard::Alt) {
2624 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2625 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2627 if (drag_info.x_constrained) {
2628 cx = drag_info.grab_x;
2630 if (drag_info.y_constrained) {
2631 cy = drag_info.grab_y;
2634 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2635 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2637 cp->line().parent_group().w2i (cx, cy);
2641 cy = min ((double) (cp->line().y_position() + cp->line().height()), cy);
2643 //translate cx to frames
2644 nframes_t cx_frames = unit_to_frame (cx);
2646 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2647 snap_to (cx_frames);
2650 const double fraction = 1.0 - ((cy - cp->line().y_position()) / (double)cp->line().height());
2654 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2660 cp->line().point_drag (*cp, cx_frames , fraction, push);
2662 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
2664 drag_info.first_move = false;
2668 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2670 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2672 if (drag_info.first_move) {
2676 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2677 reset_point_selection ();
2681 control_point_drag_motion_callback (item, event);
2683 cp->line().end_drag (cp);
2687 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2689 switch (mouse_mode) {
2691 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2692 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2700 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2704 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2705 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2709 start_line_grab (al, event);
2713 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2717 nframes_t frame_within_region;
2719 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2723 cx = event->button.x;
2724 cy = event->button.y;
2725 line->parent_group().w2i (cx, cy);
2726 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2728 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2729 current_line_drag_info.after)) {
2730 /* no adjacent points */
2734 drag_info.item = &line->grab_item();
2735 drag_info.data = line;
2736 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2737 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2739 start_grab (event, fader_cursor);
2741 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2743 line->start_drag (0, drag_info.grab_frame, fraction);
2745 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2746 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2747 show_verbose_canvas_cursor ();
2751 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2753 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2755 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2757 if (event->button.state & Keyboard::Alt) {
2761 double cx = drag_info.current_pointer_x;
2762 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2764 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2766 line->parent_group().w2i (cx, cy);
2769 cy = min ((double) line->height(), cy);
2771 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2775 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2781 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2783 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2787 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2789 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2790 line_drag_motion_callback (item, event);
2795 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2797 if (selection->regions.empty() || clicked_regionview == 0) {
2801 drag_info.copy = false;
2802 drag_info.item = item;
2803 drag_info.data = clicked_regionview;
2804 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2805 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2810 TimeAxisView* tvp = clicked_axisview;
2811 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2813 if (tv && tv->is_track()) {
2814 speed = tv->get_diskstream()->speed();
2817 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2818 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2819 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2820 // we want a move threshold
2821 drag_info.want_move_threshold = true;
2823 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2825 begin_reversible_command (_("move region(s)"));
2829 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2831 drag_info.copy = false;
2832 drag_info.item = item;
2833 drag_info.data = clicked_axisview;
2834 drag_info.last_trackview = clicked_axisview;
2835 drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
2836 drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
2842 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2844 if (selection->regions.empty() || clicked_regionview == 0) {
2848 drag_info.copy = true;
2849 drag_info.item = item;
2850 drag_info.data = clicked_regionview;
2854 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2855 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2858 if (rtv && rtv->is_track()) {
2859 speed = rtv->get_diskstream()->speed();
2862 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2863 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2864 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2865 // we want a move threshold
2866 drag_info.want_move_threshold = true;
2867 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2868 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2869 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2873 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2875 if (selection->regions.empty() || clicked_regionview == 0) {
2879 drag_info.copy = false;
2880 drag_info.item = item;
2881 drag_info.data = clicked_regionview;
2882 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2883 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2888 TimeAxisView* tvp = clicked_axisview;
2889 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2891 if (tv && tv->is_track()) {
2892 speed = tv->get_diskstream()->speed();
2895 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2896 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2897 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2898 // we want a move threshold
2899 drag_info.want_move_threshold = true;
2900 drag_info.brushing = true;
2902 begin_reversible_command (_("Drag region brush"));
2906 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2910 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2911 nframes_t pending_region_position = 0;
2912 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2913 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2914 bool clamp_y_axis = false;
2915 vector<int32_t> height_list(512) ;
2916 vector<int32_t>::iterator j;
2918 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2920 drag_info.want_move_threshold = false; // don't copy again
2922 /* duplicate the region(s) */
2924 vector<RegionView*> new_regionviews;
2926 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2933 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2934 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2937 nrv = new AudioRegionView (*arv);
2939 nrv = new MidiRegionView (*mrv);
2944 nrv->get_canvas_group()->show ();
2946 new_regionviews.push_back (nrv);
2949 if (new_regionviews.empty()) {
2953 /* reset selection to new regionviews */
2955 selection->set (new_regionviews);
2957 /* reset drag_info data to reflect the fact that we are dragging the copies */
2959 drag_info.data = new_regionviews.front();
2961 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2964 /* Which trackview is this ? */
2966 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2967 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2969 /* The region motion is only processed if the pointer is over
2973 if (!tv || !tv->is_track()) {
2974 /* To make sure we hide the verbose canvas cursor when the mouse is
2975 not held over a track.
2977 hide_verbose_canvas_cursor ();
2981 original_pointer_order = drag_info.last_trackview->order;
2983 /************************************************************
2985 ************************************************************/
2987 if (drag_info.brushing) {
2988 clamp_y_axis = true;
2993 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2995 int32_t children = 0, numtracks = 0;
2996 // XXX hard coding track limit, oh my, so very very bad
2997 bitset <1024> tracks (0x00);
2998 /* get a bitmask representing the visible tracks */
3000 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3001 TimeAxisView *tracklist_timeview;
3002 tracklist_timeview = (*i);
3003 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
3004 TimeAxisView::Children children_list;
3006 /* zeroes are audio tracks. ones are other types. */
3008 if (!rtv2->hidden()) {
3010 if (visible_y_high < rtv2->order) {
3011 visible_y_high = rtv2->order;
3013 if (visible_y_low > rtv2->order) {
3014 visible_y_low = rtv2->order;
3017 if (!rtv2->is_track()) {
3018 tracks = tracks |= (0x01 << rtv2->order);
3021 height_list[rtv2->order] = (*i)->height;
3023 if ((children_list = rtv2->get_child_list()).size() > 0) {
3024 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3025 tracks = tracks |= (0x01 << (rtv2->order + children));
3026 height_list[rtv2->order + children] = (*j)->height;
3034 /* find the actual span according to the canvas */
3036 canvas_pointer_y_span = pointer_y_span;
3037 if (drag_info.last_trackview->order >= tv->order) {
3039 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3040 if (height_list[y] == 0 ) {
3041 canvas_pointer_y_span--;
3046 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3047 if ( height_list[y] == 0 ) {
3048 canvas_pointer_y_span++;
3053 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3054 RegionView* rv2 = (*i);
3055 double ix1, ix2, iy1, iy2;
3058 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3059 rv2->get_canvas_group()->i2w (ix1, iy1);
3060 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3061 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3063 if (rtv2->order != original_pointer_order) {
3064 /* this isn't the pointer track */
3066 if (canvas_pointer_y_span > 0) {
3068 /* moving up the canvas */
3069 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3071 int32_t visible_tracks = 0;
3072 while (visible_tracks < canvas_pointer_y_span ) {
3075 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3076 /* we're passing through a hidden track */
3081 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3082 clamp_y_axis = true;
3086 clamp_y_axis = true;
3089 } else if (canvas_pointer_y_span < 0) {
3091 /*moving down the canvas*/
3093 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3096 int32_t visible_tracks = 0;
3098 while (visible_tracks > canvas_pointer_y_span ) {
3101 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3105 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3106 clamp_y_axis = true;
3111 clamp_y_axis = true;
3117 /* this is the pointer's track */
3118 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3119 clamp_y_axis = true;
3120 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3121 clamp_y_axis = true;
3129 } else if (drag_info.last_trackview == tv) {
3130 clamp_y_axis = true;
3134 if (!clamp_y_axis) {
3135 drag_info.last_trackview = tv;
3138 /************************************************************
3140 ************************************************************/
3142 /* compute the amount of pointer motion in frames, and where
3143 the region would be if we moved it by that much.
3146 if (drag_info.move_threshold_passed) {
3148 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3150 nframes_t sync_frame;
3151 nframes_t sync_offset;
3154 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3156 sync_offset = rv->region()->sync_offset (sync_dir);
3157 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3159 /* we snap if the snap modifier is not enabled.
3162 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3163 snap_to (sync_frame);
3166 if (sync_frame - sync_offset <= sync_frame) {
3167 pending_region_position = sync_frame - (sync_dir*sync_offset);
3169 pending_region_position = 0;
3173 pending_region_position = 0;
3176 if (pending_region_position > max_frames - rv->region()->length()) {
3177 pending_region_position = drag_info.last_frame_position;
3180 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3182 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3184 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3185 to make it appear at the new location.
3188 if (pending_region_position > drag_info.last_frame_position) {
3189 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3191 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3194 drag_info.last_frame_position = pending_region_position;
3201 /* threshold not passed */
3206 /*************************************************************
3208 ************************************************************/
3210 if (x_delta == 0 && (pointer_y_span == 0)) {
3211 /* haven't reached next snap point, and we're not switching
3212 trackviews. nothing to do.
3219 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3221 RegionView* rv2 = (*i);
3223 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3225 double ix1, ix2, iy1, iy2;
3226 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3227 rv2->get_canvas_group()->i2w (ix1, iy1);
3236 /*************************************************************
3238 ************************************************************/
3242 if (drag_info.first_move) {
3243 if (drag_info.move_threshold_passed) {
3254 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3255 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3257 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3259 RegionView* rv = (*i);
3260 double ix1, ix2, iy1, iy2;
3261 int32_t temp_pointer_y_span = pointer_y_span;
3263 /* get item BBox, which will be relative to parent. so we have
3264 to query on a child, then convert to world coordinates using
3268 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3269 rv->get_canvas_group()->i2w (ix1, iy1);
3270 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3271 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3272 RouteTimeAxisView* temp_rtv;
3274 if ((pointer_y_span != 0) && !clamp_y_axis) {
3277 for (j = height_list.begin(); j!= height_list.end(); j++) {
3278 if (x == canvas_rtv->order) {
3279 /* we found the track the region is on */
3280 if (x != original_pointer_order) {
3281 /*this isn't from the same track we're dragging from */
3282 temp_pointer_y_span = canvas_pointer_y_span;
3284 while (temp_pointer_y_span > 0) {
3285 /* we're moving up canvas-wise,
3286 so we need to find the next track height
3288 if (j != height_list.begin()) {
3291 if (x != original_pointer_order) {
3292 /* we're not from the dragged track, so ignore hidden tracks. */
3294 temp_pointer_y_span++;
3298 temp_pointer_y_span--;
3300 while (temp_pointer_y_span < 0) {
3302 if (x != original_pointer_order) {
3304 temp_pointer_y_span--;
3308 if (j != height_list.end()) {
3311 temp_pointer_y_span++;
3313 /* find out where we'll be when we move and set height accordingly */
3315 tvp2 = trackview_by_y_position (iy1 + y_delta);
3316 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3317 rv->set_y_position_and_height (0, temp_rtv->height);
3319 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3320 personally, i think this can confuse things, but never mind.
3323 //const GdkColor& col (temp_rtv->view->get_region_color());
3324 //rv->set_color (const_cast<GdkColor&>(col));
3331 /* prevent the regionview from being moved to before
3332 the zero position on the canvas.
3337 if (-x_delta > ix1) {
3340 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3341 x_delta = max_frames - rv->region()->last_frame();
3345 if (drag_info.first_move) {
3347 /* hide any dependent views */
3349 rv->get_time_axis_view().hide_dependent_views (*rv);
3351 /* this is subtle. raising the regionview itself won't help,
3352 because raise_to_top() just puts the item on the top of
3353 its parent's stack. so, we need to put the trackview canvas_display group
3354 on the top, since its parent is the whole canvas.
3357 rv->get_canvas_group()->raise_to_top();
3358 rv->get_time_axis_view().canvas_display->raise_to_top();
3359 cursor_group->raise_to_top();
3361 rv->fake_set_opaque (true);
3364 if (drag_info.brushing) {
3365 mouse_brush_insert_region (rv, pending_region_position);
3367 rv->move (x_delta, y_delta);
3370 } /* foreach region */
3374 if (drag_info.first_move && drag_info.move_threshold_passed) {
3375 cursor_group->raise_to_top();
3376 drag_info.first_move = false;
3379 if (x_delta != 0 && !drag_info.brushing) {
3380 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3385 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3388 RegionView* rvdi = reinterpret_cast<RegionView *> (drag_info.data);
3389 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3390 bool nocommit = true;
3392 RouteTimeAxisView* rtv;
3393 bool regionview_y_movement;
3394 bool regionview_x_movement;
3395 vector<RegionView*> copies;
3396 list <boost::shared_ptr<Playlist > > used_playlists;
3397 list <sigc::connection > used_connections;
3398 bool preserve_selection = false;
3400 /* first_move is set to false if the regionview has been moved in the
3404 if (drag_info.first_move) {
3411 /* The regionview has been moved at some stage during the grab so we need
3412 to account for any mouse movement between this event and the last one.
3415 region_drag_motion_callback (item, event);
3417 if (drag_info.brushing) {
3418 /* all changes were made during motion event handlers */
3420 if (drag_info.copy) {
3421 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3422 copies.push_back (*i);
3429 /* adjust for track speed */
3432 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3433 if (rtv && rtv->get_diskstream()) {
3434 speed = rtv->get_diskstream()->speed();
3437 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rvdi->region()->position()/speed));
3438 regionview_y_movement = (drag_info.last_trackview != &rvdi->get_time_axis_view());
3440 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3441 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3445 if (drag_info.copy) {
3446 if (drag_info.x_constrained) {
3447 op_string = _("fixed time region copy");
3449 op_string = _("region copy");
3452 if (drag_info.x_constrained) {
3453 op_string = _("fixed time region drag");
3455 op_string = _("region drag");
3459 begin_reversible_command (op_string);
3461 if (regionview_y_movement) {
3463 /* moved to a different audio track. */
3465 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3467 RegionView* rv = (*i);
3469 double ix1, ix2, iy1, iy2;
3471 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3472 rv->get_canvas_group()->i2w (ix1, iy1);
3474 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(trackview_by_y_position (iy1));
3476 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3478 if (! to_playlist->frozen()) {
3480 we haven't seen this playlist before.
3481 we want to freeze it because we don't want to relayer per-region.
3482 its much better to do that just once if the playlist is large.
3486 connect so the selection is changed when the new regionview finally appears (after thaw).
3487 keep track of it so we can disconnect later.
3490 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3491 used_connections.push_back (c);
3494 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3496 /* remember used playlists so we can thaw them later */
3497 used_playlists.push_back(to_playlist);
3498 to_playlist->freeze();
3501 where = (nframes_t) (unit_to_frame (ix1) * speed);
3502 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3504 if (!drag_info.copy) {
3507 /* the region that used to be in the old playlist is not
3508 moved to the new one - we make a copy of it. as a result,
3509 any existing editor for the region should no longer be
3513 RouteTimeAxisView* from_playlist_rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_trackview());
3514 boost::shared_ptr<Playlist> from_playlist = from_playlist_rtv->playlist();
3516 if (! from_playlist->frozen()) {
3517 from_playlist->freeze();
3518 used_playlists.push_back(from_playlist);
3520 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3521 used_connections.push_back (c);
3523 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3526 rv->hide_region_editor();
3527 rv->fake_set_opaque (false);
3529 from_playlist->remove_region ((rv->region()));
3533 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3535 copies.push_back (rv);
3538 latest_regionview = 0;
3540 to_playlist->add_region (new_region, where);
3542 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3543 was selected in all of them, then removing it from the playlist will have removed all
3544 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3545 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3546 corresponding regionview, and the selection is now empty).
3548 this could have invalidated any and all iterators into the region selection.
3550 the heuristic we use here is: if the region selection is empty, break out of the loop
3551 here. if the region selection is not empty, then restart the loop because we know that
3552 we must have removed at least the region(view) we've just been working on as well as any
3553 that we processed on previous iterations.
3555 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3556 we can just iterate.
3560 if (drag_info.copy) {
3563 if (selection->regions.empty()) {
3567 XXX see above .. but we just froze the playlists.. we have to keep iterating, right?
3570 //i = selection->regions.by_layer().begin();
3578 /* motion within a single track */
3580 list<RegionView*> regions = selection->regions.by_layer();
3582 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3584 RegionView* rv = (*i);
3585 boost::shared_ptr<Playlist> to_playlist = (*i)->region()->playlist();
3586 RouteTimeAxisView* from_rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3588 if (!rv->region()->can_move()) {
3592 if (regionview_x_movement) {
3593 double ownspeed = 1.0;
3595 if (from_rtv && from_rtv->get_diskstream()) {
3596 ownspeed = from_rtv->get_diskstream()->speed();
3599 /* base the new region position on the current position of the regionview.*/
3601 double ix1, ix2, iy1, iy2;
3603 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3604 rv->get_canvas_group()->i2w (ix1, iy1);
3605 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3609 where = rv->region()->position();
3612 if (! to_playlist->frozen()) {
3613 sigc::connection c = from_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3614 used_connections.push_back (c);
3617 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3619 used_playlists.push_back(to_playlist);
3620 to_playlist->freeze();
3623 if (drag_info.copy) {
3625 boost::shared_ptr<Region> newregion;
3626 boost::shared_ptr<Region> ar;
3627 boost::shared_ptr<Region> mr;
3629 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3630 newregion = RegionFactory::create (ar);
3631 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3632 newregion = RegionFactory::create (mr);
3637 to_playlist->add_region (newregion, (nframes_t) (where * from_rtv->get_diskstream()->speed()));
3639 /* if the original region was locked, we don't care for the new one */
3641 newregion->set_locked (false);
3642 copies.push_back (rv);
3646 /* just change the model */
3648 rv->region()->set_position (where, (void*) this);
3649 preserve_selection = true;
3656 if (! preserve_selection) {
3657 //selection->clear_regions();
3659 while (used_playlists.size() > 0) {
3661 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
3664 if (used_connections.size()) {
3665 sigc::connection c = used_connections.front();
3667 used_connections.pop_front();
3671 session->add_command (new MementoCommand<Playlist>(*(*i), 0, &(*i)->get_state()));
3672 used_playlists.pop_front();
3678 commit_reversible_command ();
3681 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3688 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3690 if (drag_info.move_threshold_passed) {
3691 if (drag_info.first_move) {
3692 // TODO: create region-create-drag region view here
3693 drag_info.first_move = false;
3696 // TODO: resize region-create-drag region view here
3701 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3703 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.last_trackview);
3707 const boost::shared_ptr<MidiDiskstream> diskstream =
3708 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
3711 warning << "Cannot create non-MIDI region" << endl;
3715 if (drag_info.first_move) {
3716 begin_reversible_command (_("create region"));
3717 XMLNode &before = mtv->playlist()->get_state();
3719 nframes_t start = drag_info.grab_frame;
3720 snap_to (start, -1);
3721 const Meter& m = session->tempo_map().meter_at(start);
3722 const Tempo& t = session->tempo_map().tempo_at(start);
3723 double length = m.frames_per_bar(t, session->frame_rate());
3725 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
3727 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>(RegionFactory::create(
3728 src, 0, length, PBD::basename_nosuffix(src->name()))), start);
3729 XMLNode &after = mtv->playlist()->get_state();
3730 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
3731 commit_reversible_command();
3734 create_region_drag_motion_callback (item, event);
3735 // TODO: create region-create-drag region here
3740 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3742 /* Either add to or set the set the region selection, unless
3743 this is an alignment click (control used)
3746 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3747 TimeAxisView* tv = &rv.get_time_axis_view();
3748 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3750 if (rtv && rtv->is_track()) {
3751 speed = rtv->get_diskstream()->speed();
3754 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3756 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3758 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3760 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3764 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3770 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3776 nframes_t frame_rate;
3783 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3784 case AudioClock::BBT:
3785 session->bbt_time (frame, bbt);
3786 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3789 case AudioClock::SMPTE:
3790 session->smpte_time (frame, smpte);
3791 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3794 case AudioClock::MinSec:
3795 /* XXX this is copied from show_verbose_duration_cursor() */
3796 frame_rate = session->frame_rate();
3797 hours = frame / (frame_rate * 3600);
3798 frame = frame % (frame_rate * 3600);
3799 mins = frame / (frame_rate * 60);
3800 frame = frame % (frame_rate * 60);
3801 secs = (float) frame / (float) frame_rate;
3802 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3806 snprintf (buf, sizeof(buf), "%u", frame);
3810 if (xpos >= 0 && ypos >=0) {
3811 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3814 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3816 show_verbose_canvas_cursor ();
3820 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3827 nframes_t distance, frame_rate;
3829 Meter meter_at_start(session->tempo_map().meter_at(start));
3835 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3836 case AudioClock::BBT:
3837 session->bbt_time (start, sbbt);
3838 session->bbt_time (end, ebbt);
3841 /* XXX this computation won't work well if the
3842 user makes a selection that spans any meter changes.
3845 ebbt.bars -= sbbt.bars;
3846 if (ebbt.beats >= sbbt.beats) {
3847 ebbt.beats -= sbbt.beats;
3850 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3852 if (ebbt.ticks >= sbbt.ticks) {
3853 ebbt.ticks -= sbbt.ticks;
3856 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3859 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3862 case AudioClock::SMPTE:
3863 session->smpte_duration (end - start, smpte);
3864 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3867 case AudioClock::MinSec:
3868 /* XXX this stuff should be elsewhere.. */
3869 distance = end - start;
3870 frame_rate = session->frame_rate();
3871 hours = distance / (frame_rate * 3600);
3872 distance = distance % (frame_rate * 3600);
3873 mins = distance / (frame_rate * 60);
3874 distance = distance % (frame_rate * 60);
3875 secs = (float) distance / (float) frame_rate;
3876 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3880 snprintf (buf, sizeof(buf), "%u", end - start);
3884 if (xpos >= 0 && ypos >=0) {
3885 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3888 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3890 show_verbose_canvas_cursor ();
3894 Editor::collect_new_region_view (RegionView* rv)
3896 latest_regionview = rv;
3900 Editor::collect_and_select_new_region_view (RegionView* rv)
3903 latest_regionview = rv;
3907 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3909 if (clicked_regionview == 0) {
3913 /* lets try to create new Region for the selection */
3915 vector<boost::shared_ptr<AudioRegion> > new_regions;
3916 create_region_from_selection (new_regions);
3918 if (new_regions.empty()) {
3922 /* XXX fix me one day to use all new regions */
3924 boost::shared_ptr<Region> region (new_regions.front());
3926 /* add it to the current stream/playlist.
3928 tricky: the streamview for the track will add a new regionview. we will
3929 catch the signal it sends when it creates the regionview to
3930 set the regionview we want to then drag.
3933 latest_regionview = 0;
3934 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3936 /* A selection grab currently creates two undo/redo operations, one for
3937 creating the new region and another for moving it.
3940 begin_reversible_command (_("selection grab"));
3942 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3944 XMLNode *before = &(playlist->get_state());
3945 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3946 XMLNode *after = &(playlist->get_state());
3947 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3949 commit_reversible_command ();
3953 if (latest_regionview == 0) {
3954 /* something went wrong */
3958 /* we need to deselect all other regionviews, and select this one
3959 i'm ignoring undo stuff, because the region creation will take care of it */
3960 //selection->set (latest_regionview);
3962 drag_info.item = latest_regionview->get_canvas_group();
3963 drag_info.data = latest_regionview;
3964 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3965 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3969 drag_info.last_trackview = clicked_axisview;
3970 drag_info.last_frame_position = latest_regionview->region()->position();
3971 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3973 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3977 Editor::cancel_selection ()
3979 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3980 (*i)->hide_selection ();
3982 begin_reversible_command (_("cancel selection"));
3983 selection->clear ();
3984 clicked_selection = 0;
3985 commit_reversible_command ();
3989 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3991 nframes_t start = 0;
3998 drag_info.item = item;
3999 drag_info.motion_callback = &Editor::drag_selection;
4000 drag_info.finished_callback = &Editor::end_selection_op;
4005 case CreateSelection:
4006 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4007 drag_info.copy = true;
4009 drag_info.copy = false;
4011 start_grab (event, selector_cursor);
4014 case SelectionStartTrim:
4015 if (clicked_axisview) {
4016 clicked_axisview->order_selection_trims (item, true);
4018 start_grab (event, trimmer_cursor);
4019 start = selection->time[clicked_selection].start;
4020 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4023 case SelectionEndTrim:
4024 if (clicked_axisview) {
4025 clicked_axisview->order_selection_trims (item, false);
4027 start_grab (event, trimmer_cursor);
4028 end = selection->time[clicked_selection].end;
4029 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4033 start = selection->time[clicked_selection].start;
4035 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4039 if (selection_op == SelectionMove) {
4040 show_verbose_time_cursor(start, 10);
4042 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4047 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4049 nframes_t start = 0;
4052 nframes_t pending_position;
4054 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4055 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4057 pending_position = 0;
4060 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4061 snap_to (pending_position);
4064 /* only alter selection if the current frame is
4065 different from the last frame position (adjusted)
4068 if (pending_position == drag_info.last_pointer_frame) return;
4070 switch (selection_op) {
4071 case CreateSelection:
4073 if (drag_info.first_move) {
4074 snap_to (drag_info.grab_frame);
4077 if (pending_position < drag_info.grab_frame) {
4078 start = pending_position;
4079 end = drag_info.grab_frame;
4081 end = pending_position;
4082 start = drag_info.grab_frame;
4085 /* first drag: Either add to the selection
4086 or create a new selection->
4089 if (drag_info.first_move) {
4091 begin_reversible_command (_("range selection"));
4093 if (drag_info.copy) {
4094 /* adding to the selection */
4095 clicked_selection = selection->add (start, end);
4096 drag_info.copy = false;
4098 /* new selection-> */
4099 clicked_selection = selection->set (clicked_axisview, start, end);
4104 case SelectionStartTrim:
4106 if (drag_info.first_move) {
4107 begin_reversible_command (_("trim selection start"));
4110 start = selection->time[clicked_selection].start;
4111 end = selection->time[clicked_selection].end;
4113 if (pending_position > end) {
4116 start = pending_position;
4120 case SelectionEndTrim:
4122 if (drag_info.first_move) {
4123 begin_reversible_command (_("trim selection end"));
4126 start = selection->time[clicked_selection].start;
4127 end = selection->time[clicked_selection].end;
4129 if (pending_position < start) {
4132 end = pending_position;
4139 if (drag_info.first_move) {
4140 begin_reversible_command (_("move selection"));
4143 start = selection->time[clicked_selection].start;
4144 end = selection->time[clicked_selection].end;
4146 length = end - start;
4148 start = pending_position;
4151 end = start + length;
4156 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4157 start_canvas_autoscroll (1);
4161 selection->replace (clicked_selection, start, end);
4164 drag_info.last_pointer_frame = pending_position;
4165 drag_info.first_move = false;
4167 if (selection_op == SelectionMove) {
4168 show_verbose_time_cursor(start, 10);
4170 show_verbose_time_cursor(pending_position, 10);
4175 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4177 if (!drag_info.first_move) {
4178 drag_selection (item, event);
4179 /* XXX this is not object-oriented programming at all. ick */
4180 if (selection->time.consolidate()) {
4181 selection->TimeChanged ();
4183 commit_reversible_command ();
4185 /* just a click, no pointer movement.*/
4187 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4189 selection->clear_time();
4194 /* XXX what happens if its a music selection? */
4195 session->set_audio_range (selection->time);
4196 stop_canvas_autoscroll ();
4200 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4203 TimeAxisView* tvp = clicked_axisview;
4204 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4206 if (tv && tv->is_track()) {
4207 speed = tv->get_diskstream()->speed();
4210 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4211 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4212 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4214 //drag_info.item = clicked_regionview->get_name_highlight();
4215 drag_info.item = item;
4216 drag_info.motion_callback = &Editor::trim_motion_callback;
4217 drag_info.finished_callback = &Editor::trim_finished_callback;
4219 start_grab (event, trimmer_cursor);
4221 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4222 trim_op = ContentsTrim;
4224 /* These will get overridden for a point trim.*/
4225 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4226 /* closer to start */
4227 trim_op = StartTrim;
4228 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4236 show_verbose_time_cursor(region_start, 10);
4239 show_verbose_time_cursor(region_end, 10);
4242 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4248 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4250 RegionView* rv = clicked_regionview;
4251 nframes_t frame_delta = 0;
4252 bool left_direction;
4253 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4255 /* snap modifier works differently here..
4256 its' current state has to be passed to the
4257 various trim functions in order to work properly
4261 TimeAxisView* tvp = clicked_axisview;
4262 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4263 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4265 if (tv && tv->is_track()) {
4266 speed = tv->get_diskstream()->speed();
4269 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4270 left_direction = true;
4272 left_direction = false;
4276 snap_to (drag_info.current_pointer_frame);
4279 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4283 if (drag_info.first_move) {
4289 trim_type = "Region start trim";
4292 trim_type = "Region end trim";
4295 trim_type = "Region content trim";
4299 begin_reversible_command (trim_type);
4301 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4302 (*i)->fake_set_opaque(false);
4303 (*i)->region()->freeze ();
4305 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4307 arv->temporarily_hide_envelope ();
4309 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4310 insert_result = motion_frozen_playlists.insert (pl);
4311 if (insert_result.second) {
4312 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4318 if (left_direction) {
4319 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4321 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4326 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4329 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4330 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4336 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4339 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4340 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4347 bool swap_direction = false;
4349 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4350 swap_direction = true;
4353 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4354 i != selection->regions.by_layer().end(); ++i)
4356 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4364 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4367 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4370 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4374 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4375 drag_info.first_move = false;
4379 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4381 boost::shared_ptr<Region> region (rv.region());
4383 if (region->locked()) {
4387 nframes_t new_bound;
4390 TimeAxisView* tvp = clicked_axisview;
4391 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4393 if (tv && tv->is_track()) {
4394 speed = tv->get_diskstream()->speed();
4397 if (left_direction) {
4398 if (swap_direction) {
4399 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4401 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4404 if (swap_direction) {
4405 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4407 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4412 snap_to (new_bound);
4414 region->trim_start ((nframes_t) (new_bound * speed), this);
4415 rv.region_changed (StartChanged);
4419 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4421 boost::shared_ptr<Region> region (rv.region());
4423 if (region->locked()) {
4427 nframes_t new_bound;
4430 TimeAxisView* tvp = clicked_axisview;
4431 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4433 if (tv && tv->is_track()) {
4434 speed = tv->get_diskstream()->speed();
4437 if (left_direction) {
4438 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4440 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4444 snap_to (new_bound, (left_direction ? 0 : 1));
4447 region->trim_front ((nframes_t) (new_bound * speed), this);
4449 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4453 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4455 boost::shared_ptr<Region> region (rv.region());
4457 if (region->locked()) {
4461 nframes_t new_bound;
4464 TimeAxisView* tvp = clicked_axisview;
4465 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4467 if (tv && tv->is_track()) {
4468 speed = tv->get_diskstream()->speed();
4471 if (left_direction) {
4472 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4474 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4478 snap_to (new_bound);
4480 region->trim_end ((nframes_t) (new_bound * speed), this);
4481 rv.region_changed (LengthChanged);
4485 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4487 if (!drag_info.first_move) {
4488 trim_motion_callback (item, event);
4490 if (!clicked_regionview->get_selected()) {
4491 thaw_region_after_trim (*clicked_regionview);
4494 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4495 i != selection->regions.by_layer().end(); ++i)
4497 thaw_region_after_trim (**i);
4498 (*i)->fake_set_opaque (true);
4502 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4504 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4507 motion_frozen_playlists.clear ();
4509 commit_reversible_command();
4511 /* no mouse movement */
4517 Editor::point_trim (GdkEvent* event)
4519 RegionView* rv = clicked_regionview;
4520 nframes_t new_bound = drag_info.current_pointer_frame;
4522 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4523 snap_to (new_bound);
4526 /* Choose action dependant on which button was pressed */
4527 switch (event->button.button) {
4529 trim_op = StartTrim;
4530 begin_reversible_command (_("Start point trim"));
4532 if (rv->get_selected()) {
4534 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4535 i != selection->regions.by_layer().end(); ++i)
4537 if (!(*i)->region()->locked()) {
4538 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4539 XMLNode &before = pl->get_state();
4540 (*i)->region()->trim_front (new_bound, this);
4541 XMLNode &after = pl->get_state();
4542 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4548 if (!rv->region()->locked()) {
4549 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4550 XMLNode &before = pl->get_state();
4551 rv->region()->trim_front (new_bound, this);
4552 XMLNode &after = pl->get_state();
4553 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4557 commit_reversible_command();
4562 begin_reversible_command (_("End point trim"));
4564 if (rv->get_selected()) {
4566 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4568 if (!(*i)->region()->locked()) {
4569 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4570 XMLNode &before = pl->get_state();
4571 (*i)->region()->trim_end (new_bound, this);
4572 XMLNode &after = pl->get_state();
4573 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4579 if (!rv->region()->locked()) {
4580 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4581 XMLNode &before = pl->get_state();
4582 rv->region()->trim_end (new_bound, this);
4583 XMLNode &after = pl->get_state();
4584 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4588 commit_reversible_command();
4597 Editor::thaw_region_after_trim (RegionView& rv)
4599 boost::shared_ptr<Region> region (rv.region());
4601 if (region->locked()) {
4605 region->thaw (_("trimmed region"));
4606 XMLNode &after = region->playlist()->get_state();
4607 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4609 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4611 arv->unhide_envelope ();
4615 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4620 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4621 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4625 Location* location = find_location_from_marker (marker, is_start);
4626 location->set_hidden (true, this);
4631 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4637 drag_info.item = item;
4638 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4639 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4641 range_marker_op = op;
4643 if (!temp_location) {
4644 temp_location = new Location;
4648 case CreateRangeMarker:
4649 case CreateTransportMarker:
4651 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4652 drag_info.copy = true;
4654 drag_info.copy = false;
4656 start_grab (event, selector_cursor);
4660 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4665 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4667 nframes_t start = 0;
4669 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4671 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4672 snap_to (drag_info.current_pointer_frame);
4675 /* only alter selection if the current frame is
4676 different from the last frame position.
4679 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4681 switch (range_marker_op) {
4682 case CreateRangeMarker:
4683 case CreateTransportMarker:
4684 if (drag_info.first_move) {
4685 snap_to (drag_info.grab_frame);
4688 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4689 start = drag_info.current_pointer_frame;
4690 end = drag_info.grab_frame;
4692 end = drag_info.current_pointer_frame;
4693 start = drag_info.grab_frame;
4696 /* first drag: Either add to the selection
4697 or create a new selection.
4700 if (drag_info.first_move) {
4702 temp_location->set (start, end);
4706 update_marker_drag_item (temp_location);
4707 range_marker_drag_rect->show();
4708 range_marker_drag_rect->raise_to_top();
4714 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4715 start_canvas_autoscroll (1);
4719 temp_location->set (start, end);
4721 double x1 = frame_to_pixel (start);
4722 double x2 = frame_to_pixel (end);
4723 crect->property_x1() = x1;
4724 crect->property_x2() = x2;
4726 update_marker_drag_item (temp_location);
4729 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4730 drag_info.first_move = false;
4732 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4737 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4739 Location * newloc = 0;
4742 if (!drag_info.first_move) {
4743 drag_range_markerbar_op (item, event);
4745 switch (range_marker_op) {
4746 case CreateRangeMarker:
4748 begin_reversible_command (_("new range marker"));
4749 XMLNode &before = session->locations()->get_state();
4750 session->locations()->next_available_name(rangename,"unnamed");
4751 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4752 session->locations()->add (newloc, true);
4753 XMLNode &after = session->locations()->get_state();
4754 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4755 commit_reversible_command ();
4757 range_bar_drag_rect->hide();
4758 range_marker_drag_rect->hide();
4762 case CreateTransportMarker:
4763 // popup menu to pick loop or punch
4764 new_transport_marker_context_menu (&event->button, item);
4769 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4771 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4776 start = session->locations()->first_mark_before (drag_info.grab_frame);
4777 end = session->locations()->first_mark_after (drag_info.grab_frame);
4779 if (end == max_frames) {
4780 end = session->current_end_frame ();
4784 start = session->current_start_frame ();
4787 switch (mouse_mode) {
4789 /* find the two markers on either side and then make the selection from it */
4790 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4794 /* find the two markers on either side of the click and make the range out of it */
4795 selection->set (0, start, end);
4804 stop_canvas_autoscroll ();
4810 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4812 drag_info.item = item;
4813 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4814 drag_info.finished_callback = &Editor::end_mouse_zoom;
4816 start_grab (event, zoom_cursor);
4818 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4822 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4827 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4828 snap_to (drag_info.current_pointer_frame);
4830 if (drag_info.first_move) {
4831 snap_to (drag_info.grab_frame);
4835 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4837 /* base start and end on initial click position */
4838 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4839 start = drag_info.current_pointer_frame;
4840 end = drag_info.grab_frame;
4842 end = drag_info.current_pointer_frame;
4843 start = drag_info.grab_frame;
4848 if (drag_info.first_move) {
4850 zoom_rect->raise_to_top();
4853 reposition_zoom_rect(start, end);
4855 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4856 drag_info.first_move = false;
4858 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4863 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4865 if (!drag_info.first_move) {
4866 drag_mouse_zoom (item, event);
4868 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4869 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4871 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4874 temporal_zoom_to_frame (false, drag_info.grab_frame);
4876 temporal_zoom_step (false);
4877 center_screen (drag_info.grab_frame);
4885 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4887 double x1 = frame_to_pixel (start);
4888 double x2 = frame_to_pixel (end);
4889 double y2 = full_canvas_height - 1.0;
4891 zoom_rect->property_x1() = x1;
4892 zoom_rect->property_y1() = 1.0;
4893 zoom_rect->property_x2() = x2;
4894 zoom_rect->property_y2() = y2;
4898 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4900 drag_info.item = item;
4901 drag_info.motion_callback = &Editor::drag_rubberband_select;
4902 drag_info.finished_callback = &Editor::end_rubberband_select;
4904 start_grab (event, cross_hair_cursor);
4906 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4910 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4917 /* use a bigger drag threshold than the default */
4919 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4923 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4924 if (drag_info.first_move) {
4925 snap_to (drag_info.grab_frame);
4927 snap_to (drag_info.current_pointer_frame);
4930 /* base start and end on initial click position */
4932 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4933 start = drag_info.current_pointer_frame;
4934 end = drag_info.grab_frame;
4936 end = drag_info.current_pointer_frame;
4937 start = drag_info.grab_frame;
4940 if (drag_info.current_pointer_y < drag_info.grab_y) {
4941 y1 = drag_info.current_pointer_y;
4942 y2 = drag_info.grab_y;
4944 y2 = drag_info.current_pointer_y;
4945 y1 = drag_info.grab_y;
4949 if (start != end || y1 != y2) {
4951 double x1 = frame_to_pixel (start);
4952 double x2 = frame_to_pixel (end);
4954 rubberband_rect->property_x1() = x1;
4955 rubberband_rect->property_y1() = y1;
4956 rubberband_rect->property_x2() = x2;
4957 rubberband_rect->property_y2() = y2;
4959 rubberband_rect->show();
4960 rubberband_rect->raise_to_top();
4962 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4963 drag_info.first_move = false;
4965 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4970 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4972 if (!drag_info.first_move) {
4974 drag_rubberband_select (item, event);
4977 if (drag_info.current_pointer_y < drag_info.grab_y) {
4978 y1 = drag_info.current_pointer_y;
4979 y2 = drag_info.grab_y;
4982 y2 = drag_info.current_pointer_y;
4983 y1 = drag_info.grab_y;
4987 Selection::Operation op = Keyboard::selection_type (event->button.state);
4990 begin_reversible_command (_("rubberband selection"));
4992 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4993 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4995 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4999 commit_reversible_command ();
5003 selection->clear_tracks();
5004 selection->clear_regions();
5005 selection->clear_points ();
5006 selection->clear_lines ();
5009 rubberband_rect->hide();
5014 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5016 using namespace Gtkmm2ext;
5018 ArdourPrompter prompter (false);
5020 prompter.set_prompt (_("Name for region:"));
5021 prompter.set_initial_text (clicked_regionview->region()->name());
5022 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5023 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5024 prompter.show_all ();
5025 switch (prompter.run ()) {
5026 case Gtk::RESPONSE_ACCEPT:
5028 prompter.get_result(str);
5030 clicked_regionview->region()->set_name (str);
5038 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5040 drag_info.item = item;
5041 drag_info.motion_callback = &Editor::time_fx_motion;
5042 drag_info.finished_callback = &Editor::end_time_fx;
5046 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5050 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5052 RegionView* rv = clicked_regionview;
5054 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5055 snap_to (drag_info.current_pointer_frame);
5058 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5062 if (drag_info.current_pointer_frame > rv->region()->position()) {
5063 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5066 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5067 drag_info.first_move = false;
5069 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5073 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5075 clicked_regionview->get_time_axis_view().hide_timestretch ();
5077 if (drag_info.first_move) {
5081 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5082 /* backwards drag of the left edge - not usable */
5086 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5087 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5089 begin_reversible_command (_("timestretch"));
5091 if (run_timestretch (selection->regions, percentage) == 0) {
5092 session->commit_reversible_command ();
5097 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5099 /* no brushing without a useful snap setting */
5102 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5105 switch (snap_mode) {
5107 return; /* can't work because it allows region to be placed anywhere */
5112 switch (snap_type) {
5115 case SnapToEditCursor:
5122 /* don't brush a copy over the original */
5124 if (pos == rv->region()->position()) {
5128 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5130 if (rtv == 0 || !rtv->is_track()) {
5134 boost::shared_ptr<Playlist> playlist = rtv->playlist();
5135 double speed = rtv->get_diskstream()->speed();
5137 XMLNode &before = playlist->get_state();
5138 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5139 XMLNode &after = playlist->get_state();
5140 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5142 // playlist is frozen, so we have to update manually
5144 playlist->Modified(); /* EMIT SIGNAL */
5148 Editor::track_height_step_timeout ()
5151 struct timeval delta;
5153 gettimeofday (&now, 0);
5154 timersub (&now, &last_track_height_step_timestamp, &delta);
5156 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5157 current_stepping_trackview = 0;
5165 Editor::add_mouse_speed (double speed, double dir)
5169 mouse_direction = dir;
5171 index = mouse_speed_entries;
5173 if (++index >= mouse_speed_size) {
5175 have_full_mouse_speed = true;
5178 mouse_speed[index] = speed;
5179 mouse_speed_entries = index;
5183 Editor::compute_mouse_speed ()
5187 if (!have_full_mouse_speed) {
5189 /* partial speed buffer, just use whatever we have so far */
5191 if (mouse_speed_entries == 0 ) {
5195 for (size_t n = 0; n < mouse_speed_entries; ++n) {
5196 total += mouse_speed[n];
5199 return mouse_direction * total/mouse_speed_entries;
5202 /* compute the average (effectively low-pass filtering) mouse speed
5203 across the entire buffer.
5206 for (size_t n = 0; n < mouse_speed_size; ++n) {
5207 total += mouse_speed[n];
5211 return mouse_direction * total/mouse_speed_size;
5215 Editor::update_mouse_speed ()
5220 session->request_transport_speed (0.0);
5221 mouse_speed_update = -1;
5225 speed = compute_mouse_speed ();
5227 struct timeval tmnow;
5229 gettimeofday (&tmnow, 0);
5230 double now = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec;
5232 if (now - last_scrub_time > 250000) {
5234 // 0.25 seconds since last mouse motion, start to brake
5236 if (fabs (speed) < 0.1) {
5237 /* don't asymptotically approach zero */
5238 memset (mouse_speed, 0, sizeof (double) * mouse_speed_size);
5240 } else if (fabs (speed) < 0.25) {
5241 add_mouse_speed (fabs (speed * 0.2), mouse_direction);
5243 add_mouse_speed (fabs (speed * 0.6), mouse_direction);
5247 session->request_transport_speed (speed);