2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <pbd/error.h>
29 #include <gtkmm2ext/utils.h>
30 #include <pbd/memento_command.h>
31 #include <pbd/basename.h>
33 #include "ardour_ui.h"
35 #include "time_axis_view.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
38 #include "midi_region_view.h"
40 #include "streamview.h"
41 #include "region_gain_line.h"
42 #include "automation_time_axis.h"
43 #include "control_point.h"
46 #include "selection.h"
49 #include "rgb_macros.h"
51 #include <ardour/types.h>
52 #include <ardour/profile.h>
53 #include <ardour/route.h>
54 #include <ardour/audio_track.h>
55 #include <ardour/audio_diskstream.h>
56 #include <ardour/midi_diskstream.h>
57 #include <ardour/playlist.h>
58 #include <ardour/audioplaylist.h>
59 #include <ardour/audioregion.h>
60 #include <ardour/midi_region.h>
61 #include <ardour/dB.h>
62 #include <ardour/utils.h>
63 #include <ardour/region_factory.h>
64 #include <ardour/source_factory.h>
71 using namespace ARDOUR;
75 using namespace Editing;
78 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
92 switch (event->type) {
93 case GDK_BUTTON_RELEASE:
94 case GDK_BUTTON_PRESS:
95 case GDK_2BUTTON_PRESS:
96 case GDK_3BUTTON_PRESS:
97 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
99 case GDK_MOTION_NOTIFY:
100 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
102 case GDK_ENTER_NOTIFY:
103 case GDK_LEAVE_NOTIFY:
104 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
107 case GDK_KEY_RELEASE:
108 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
111 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
115 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
116 position is negative (as can be the case with motion events in particular),
117 the frame location is always positive.
120 return pixel_to_frame (*pcx);
124 Editor::mouse_mode_toggled (MouseMode m)
126 if (ignore_mouse_mode_toggle) {
132 if (mouse_select_button.get_active()) {
138 if (mouse_move_button.get_active()) {
144 if (mouse_gain_button.get_active()) {
150 if (mouse_zoom_button.get_active()) {
156 if (mouse_timefx_button.get_active()) {
162 if (mouse_audition_button.get_active()) {
168 if (mouse_note_button.get_active()) {
179 Editor::set_mouse_mode (MouseMode m, bool force)
181 if (drag_info.item) {
185 if (!force && m == mouse_mode) {
193 if (mouse_mode != MouseRange) {
195 /* in all modes except range, hide the range selection,
196 show the object (region) selection.
199 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
200 (*i)->set_should_show_selection (true);
202 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
203 (*i)->hide_selection ();
209 in range mode,show the range selection.
212 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
213 if ((*i)->get_selected()) {
214 (*i)->show_selection (selection->time);
219 /* XXX the hack of unsetting all other buttons should go
220 away once GTK2 allows us to use regular radio buttons drawn like
221 normal buttons, rather than my silly GroupedButton hack.
224 ignore_mouse_mode_toggle = true;
226 switch (mouse_mode) {
228 mouse_select_button.set_active (true);
229 current_canvas_cursor = selector_cursor;
233 mouse_move_button.set_active (true);
234 current_canvas_cursor = grabber_cursor;
238 mouse_gain_button.set_active (true);
239 current_canvas_cursor = cross_hair_cursor;
243 mouse_zoom_button.set_active (true);
244 current_canvas_cursor = zoom_cursor;
248 mouse_timefx_button.set_active (true);
249 current_canvas_cursor = time_fx_cursor; // just use playhead
253 mouse_audition_button.set_active (true);
254 current_canvas_cursor = speaker_cursor;
258 mouse_note_button.set_active (true);
259 set_midi_edit_cursor (current_midi_edit_mode());
263 if (mouse_mode == MouseNote)
264 midi_toolbar_frame.show();
266 midi_toolbar_frame.hide();
268 ignore_mouse_mode_toggle = false;
271 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
276 Editor::step_mouse_mode (bool next)
278 switch (current_mouse_mode()) {
280 if (next) set_mouse_mode (MouseRange);
281 else set_mouse_mode (MouseTimeFX);
285 if (next) set_mouse_mode (MouseZoom);
286 else set_mouse_mode (MouseObject);
290 if (next) set_mouse_mode (MouseGain);
291 else set_mouse_mode (MouseRange);
295 if (next) set_mouse_mode (MouseTimeFX);
296 else set_mouse_mode (MouseZoom);
300 if (next) set_mouse_mode (MouseAudition);
301 else set_mouse_mode (MouseGain);
305 if (next) set_mouse_mode (MouseObject);
306 else set_mouse_mode (MouseTimeFX);
310 if (next) set_mouse_mode (MouseObject);
311 else set_mouse_mode (MouseAudition);
317 Editor::midi_edit_mode_toggled (MidiEditMode m)
319 if (ignore_midi_edit_mode_toggle) {
325 if (midi_tool_pencil_button.get_active())
326 set_midi_edit_mode (m);
330 if (midi_tool_select_button.get_active())
331 set_midi_edit_mode (m);
335 if (midi_tool_erase_button.get_active())
336 set_midi_edit_mode (m);
343 set_midi_edit_cursor(m);
348 Editor::set_midi_edit_mode (MidiEditMode m, bool force)
350 if (drag_info.item) {
354 if (!force && m == midi_edit_mode) {
362 ignore_midi_edit_mode_toggle = true;
364 switch (midi_edit_mode) {
366 midi_tool_pencil_button.set_active (true);
370 midi_tool_select_button.set_active (true);
374 midi_tool_erase_button.set_active (true);
378 ignore_midi_edit_mode_toggle = false;
380 set_midi_edit_cursor (current_midi_edit_mode());
383 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
388 Editor::set_midi_edit_cursor (MidiEditMode m)
390 switch (midi_edit_mode) {
392 current_canvas_cursor = midi_pencil_cursor;
396 current_canvas_cursor = midi_select_cursor;
400 current_canvas_cursor = midi_erase_cursor;
406 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
410 /* in object/audition/timefx mode, any button press sets
411 the selection if the object can be selected. this is a
412 bit of hack, because we want to avoid this if the
413 mouse operation is a region alignment.
415 note: not dbl-click or triple-click
418 if (((mouse_mode != MouseObject) &&
419 (mouse_mode != MouseAudition || item_type != RegionItem) &&
420 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
421 (mouse_mode != MouseRange)) ||
423 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
428 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
430 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
432 /* almost no selection action on modified button-2 or button-3 events */
434 if (item_type != RegionItem && event->button.button != 2) {
440 Selection::Operation op = Keyboard::selection_type (event->button.state);
441 bool press = (event->type == GDK_BUTTON_PRESS);
443 // begin_reversible_command (_("select on click"));
447 if (mouse_mode != MouseRange) {
448 commit = set_selected_regionview_from_click (press, op, true);
449 } else if (event->type == GDK_BUTTON_PRESS) {
450 commit = set_selected_track_from_click (press, op, false);
454 case RegionViewNameHighlight:
456 if (mouse_mode != MouseRange) {
457 commit = set_selected_regionview_from_click (press, op, true);
458 } else if (event->type == GDK_BUTTON_PRESS) {
459 commit = set_selected_track_from_click (press, op, false);
464 case FadeInHandleItem:
466 case FadeOutHandleItem:
468 if (mouse_mode != MouseRange) {
469 commit = set_selected_regionview_from_click (press, op, true);
470 } else if (event->type == GDK_BUTTON_PRESS) {
471 commit = set_selected_track_from_click (press, op, false);
475 case CrossfadeViewItem:
476 commit = set_selected_track_from_click (press, op, false);
479 case ControlPointItem:
480 commit = set_selected_track_from_click (press, op, true);
481 if (mouse_mode != MouseRange) {
482 commit |= set_selected_control_point_from_click (op, false);
487 /* for context click or range selection, select track */
488 if (event->button.button == 3) {
489 commit = set_selected_track_from_click (press, op, true);
490 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
491 commit = set_selected_track_from_click (press, op, false);
495 case AutomationTrackItem:
496 commit = set_selected_track_from_click (press, op, true);
504 // commit_reversible_command ();
509 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
511 track_canvas.grab_focus();
513 if (session && session->actively_recording()) {
517 button_selection (item, event, item_type);
519 if (drag_info.item == 0 &&
520 (Keyboard::is_delete_event (&event->button) ||
521 Keyboard::is_context_menu_event (&event->button) ||
522 Keyboard::is_edit_event (&event->button))) {
524 /* handled by button release */
528 switch (event->button.button) {
531 if (event->type == GDK_BUTTON_PRESS) {
533 if (drag_info.item) {
534 drag_info.item->ungrab (event->button.time);
537 /* single mouse clicks on any of these item types operate
538 independent of mouse mode, mostly because they are
539 not on the main track canvas or because we want
545 case PlayheadCursorItem:
546 start_cursor_grab (item, event);
550 if (Keyboard::modifier_state_equals (event->button.state,
551 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
552 hide_marker (item, event);
554 start_marker_grab (item, event);
558 case TempoMarkerItem:
559 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
560 start_tempo_marker_copy_grab (item, event);
562 start_tempo_marker_grab (item, event);
566 case MeterMarkerItem:
567 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
568 start_meter_marker_copy_grab (item, event);
570 start_meter_marker_grab (item, event);
580 case RangeMarkerBarItem:
581 start_range_markerbar_op (item, event, CreateRangeMarker);
585 case TransportMarkerBarItem:
586 start_range_markerbar_op (item, event, CreateTransportMarker);
595 switch (mouse_mode) {
598 case StartSelectionTrimItem:
599 start_selection_op (item, event, SelectionStartTrim);
602 case EndSelectionTrimItem:
603 start_selection_op (item, event, SelectionEndTrim);
607 if (Keyboard::modifier_state_contains
608 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
609 // contains and not equals because I can't use alt as a modifier alone.
610 start_selection_grab (item, event);
611 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
612 /* grab selection for moving */
613 start_selection_op (item, event, SelectionMove);
616 /* this was debated, but decided the more common action was to
617 make a new selection */
618 start_selection_op (item, event, CreateSelection);
623 start_selection_op (item, event, CreateSelection);
629 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
630 event->type == GDK_BUTTON_PRESS) {
632 start_rubberband_select (item, event);
634 } else if (event->type == GDK_BUTTON_PRESS) {
637 case FadeInHandleItem:
638 start_fade_in_grab (item, event);
641 case FadeOutHandleItem:
642 start_fade_out_grab (item, event);
646 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
647 start_region_copy_grab (item, event);
648 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
649 start_region_brush_grab (item, event);
651 start_region_grab (item, event);
655 case RegionViewNameHighlight:
656 start_trim (item, event);
661 /* rename happens on edit clicks */
662 start_trim (clicked_regionview->get_name_highlight(), event);
666 case ControlPointItem:
667 start_control_point_grab (item, event);
671 case AutomationLineItem:
672 start_line_grab_from_line (item, event);
677 case AutomationTrackItem:
678 start_rubberband_select (item, event);
682 case ImageFrameHandleStartItem:
683 imageframe_start_handle_op(item, event) ;
686 case ImageFrameHandleEndItem:
687 imageframe_end_handle_op(item, event) ;
690 case MarkerViewHandleStartItem:
691 markerview_item_start_handle_op(item, event) ;
694 case MarkerViewHandleEndItem:
695 markerview_item_end_handle_op(item, event) ;
699 start_markerview_grab(item, event) ;
702 start_imageframe_grab(item, event) ;
720 // start_line_grab_from_regionview (item, event);
724 start_line_grab_from_line (item, event);
727 case ControlPointItem:
728 start_control_point_grab (item, event);
739 case ControlPointItem:
740 start_control_point_grab (item, event);
743 case AutomationLineItem:
744 start_line_grab_from_line (item, event);
748 // XXX need automation mode to identify which
750 // start_line_grab_from_regionview (item, event);
760 if (event->type == GDK_BUTTON_PRESS) {
761 start_mouse_zoom (item, event);
768 if (item_type == RegionItem) {
769 start_time_fx (item, event);
775 last_scrub_frame = 0;
777 have_full_mouse_speed = false;
778 memset (mouse_speed, 0, sizeof (double) * mouse_speed_size);
779 /* rest handled in motion & release */
783 start_create_region_grab (item, event);
792 switch (mouse_mode) {
794 if (event->type == GDK_BUTTON_PRESS) {
797 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
798 start_region_copy_grab (item, event);
800 start_region_grab (item, event);
804 case ControlPointItem:
805 start_control_point_grab (item, event);
816 case RegionViewNameHighlight:
817 start_trim (item, event);
822 start_trim (clicked_regionview->get_name_highlight(), event);
833 if (event->type == GDK_BUTTON_PRESS) {
834 /* relax till release */
841 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
842 temporal_zoom_session();
844 temporal_zoom_to_frame (true, event_frame(event));
867 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
869 nframes_t where = event_frame (event, 0, 0);
871 /* no action if we're recording */
873 if (session && session->actively_recording()) {
877 /* first, see if we're finishing a drag ... */
879 if (drag_info.item) {
880 if (end_grab (item, event)) {
881 /* grab dragged, so do nothing else */
886 button_selection (item, event, item_type);
888 /* edit events get handled here */
890 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
896 case TempoMarkerItem:
897 edit_tempo_marker (item);
900 case MeterMarkerItem:
901 edit_meter_marker (item);
905 if (clicked_regionview->name_active()) {
906 return mouse_rename_region (item, event);
916 /* context menu events get handled here */
918 if (Keyboard::is_context_menu_event (&event->button)) {
920 if (drag_info.item == 0) {
922 /* no matter which button pops up the context menu, tell the menu
923 widget to use button 1 to drive menu selection.
928 case FadeInHandleItem:
930 case FadeOutHandleItem:
931 popup_fade_context_menu (1, event->button.time, item, item_type);
935 popup_track_context_menu (1, event->button.time, where);
939 case RegionViewNameHighlight:
941 popup_track_context_menu (1, event->button.time, where);
945 popup_track_context_menu (1, event->button.time, where);
948 case AutomationTrackItem:
949 case CrossfadeViewItem:
950 popup_track_context_menu (1, event->button.time, where);
954 case RangeMarkerBarItem:
955 case TransportMarkerBarItem:
958 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
962 marker_context_menu (&event->button, item);
965 case TempoMarkerItem:
966 tm_marker_context_menu (&event->button, item);
969 case MeterMarkerItem:
970 tm_marker_context_menu (&event->button, item);
975 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
977 case ImageFrameTimeAxisItem:
978 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
981 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
983 case MarkerTimeAxisItem:
984 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
996 /* delete events get handled here */
998 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1000 switch (item_type) {
1001 case TempoMarkerItem:
1002 remove_tempo_marker (item);
1005 case MeterMarkerItem:
1006 remove_meter_marker (item);
1010 remove_marker (*item, event);
1014 if (mouse_mode == MouseObject) {
1015 remove_clicked_region ();
1019 case ControlPointItem:
1020 if (mouse_mode == MouseGain) {
1021 remove_gain_control_point (item, event);
1023 remove_control_point (item, event);
1033 switch (event->button.button) {
1036 switch (item_type) {
1037 /* see comments in button_press_handler */
1038 case EditCursorItem:
1039 case PlayheadCursorItem:
1042 case AutomationLineItem:
1043 case StartSelectionTrimItem:
1044 case EndSelectionTrimItem:
1048 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1049 snap_to (where, 0, true);
1051 mouse_add_new_marker (where);
1055 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1058 mouse_add_new_tempo_event (where);
1062 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1070 switch (mouse_mode) {
1072 switch (item_type) {
1073 case AutomationTrackItem:
1074 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
1088 // Gain only makes sense for audio regions
1090 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1094 switch (item_type) {
1096 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1100 case AutomationTrackItem:
1101 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1102 add_automation_event (item, event, where, event->button.y);
1112 if (last_scrub_frame == 0) {
1113 /* no drag, just a click */
1114 switch (item_type) {
1116 audition_selected_region ();
1122 /* make sure we stop */
1123 session->request_transport_speed (0.0);
1137 switch (mouse_mode) {
1141 // x_style_paste (where, 1.0);
1161 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1167 switch (item_type) {
1168 case ControlPointItem:
1169 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1170 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1171 cp->set_visible (true);
1175 at_y = cp->get_y ();
1176 cp->item()->i2w (at_x, at_y);
1180 fraction = 1.0 - (cp->get_y() / cp->line().height());
1182 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1183 show_verbose_canvas_cursor ();
1185 if (is_drawable()) {
1186 track_canvas.get_window()->set_cursor (*fader_cursor);
1192 if (mouse_mode == MouseGain) {
1193 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1195 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1196 if (is_drawable()) {
1197 track_canvas.get_window()->set_cursor (*fader_cursor);
1202 case AutomationLineItem:
1203 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1205 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1207 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1209 if (is_drawable()) {
1210 track_canvas.get_window()->set_cursor (*fader_cursor);
1215 case RegionViewNameHighlight:
1216 if (is_drawable() && mouse_mode == MouseObject) {
1217 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1221 case StartSelectionTrimItem:
1222 case EndSelectionTrimItem:
1225 case ImageFrameHandleStartItem:
1226 case ImageFrameHandleEndItem:
1227 case MarkerViewHandleStartItem:
1228 case MarkerViewHandleEndItem:
1231 if (is_drawable()) {
1232 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1236 case EditCursorItem:
1237 case PlayheadCursorItem:
1238 if (is_drawable()) {
1239 track_canvas.get_window()->set_cursor (*grabber_cursor);
1243 case RegionViewName:
1245 /* when the name is not an active item, the entire name highlight is for trimming */
1247 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1248 if (mouse_mode == MouseObject && is_drawable()) {
1249 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1255 case AutomationTrackItem:
1256 if (is_drawable()) {
1257 Gdk::Cursor *cursor;
1258 switch (mouse_mode) {
1260 cursor = selector_cursor;
1263 cursor = zoom_cursor;
1266 cursor = cross_hair_cursor;
1270 track_canvas.get_window()->set_cursor (*cursor);
1272 AutomationTimeAxisView* atv;
1273 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1274 clear_entered_track = false;
1275 set_entered_track (atv);
1281 case RangeMarkerBarItem:
1282 case TransportMarkerBarItem:
1285 if (is_drawable()) {
1286 time_canvas.get_window()->set_cursor (*timebar_cursor);
1291 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1294 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1296 case MeterMarkerItem:
1297 case TempoMarkerItem:
1298 if (is_drawable()) {
1299 time_canvas.get_window()->set_cursor (*timebar_cursor);
1302 case FadeInHandleItem:
1303 case FadeOutHandleItem:
1304 if (mouse_mode == MouseObject) {
1305 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1307 rect->property_fill_color_rgba() = 0;
1308 rect->property_outline_pixels() = 1;
1317 /* second pass to handle entered track status in a comprehensible way.
1320 switch (item_type) {
1322 case AutomationLineItem:
1323 case ControlPointItem:
1324 /* these do not affect the current entered track state */
1325 clear_entered_track = false;
1328 case AutomationTrackItem:
1329 /* handled above already */
1333 set_entered_track (0);
1341 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1350 switch (item_type) {
1351 case ControlPointItem:
1352 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1353 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1354 if (cp->line().npoints() > 1 && !cp->selected()) {
1355 cp->set_visible (false);
1359 if (is_drawable()) {
1360 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1363 hide_verbose_canvas_cursor ();
1366 case RegionViewNameHighlight:
1367 case StartSelectionTrimItem:
1368 case EndSelectionTrimItem:
1369 case EditCursorItem:
1370 case PlayheadCursorItem:
1373 case ImageFrameHandleStartItem:
1374 case ImageFrameHandleEndItem:
1375 case MarkerViewHandleStartItem:
1376 case MarkerViewHandleEndItem:
1379 if (is_drawable()) {
1380 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1385 case AutomationLineItem:
1386 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1388 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1390 line->property_fill_color_rgba() = al->get_line_color();
1392 if (is_drawable()) {
1393 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1397 case RegionViewName:
1398 /* see enter_handler() for notes */
1399 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1400 if (is_drawable() && mouse_mode == MouseObject) {
1401 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1406 case RangeMarkerBarItem:
1407 case TransportMarkerBarItem:
1411 if (is_drawable()) {
1412 time_canvas.get_window()->set_cursor (*timebar_cursor);
1417 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1420 loc = find_location_from_marker (marker, is_start);
1421 if (loc) location_flags_changed (loc, this);
1423 case MeterMarkerItem:
1424 case TempoMarkerItem:
1426 if (is_drawable()) {
1427 time_canvas.get_window()->set_cursor (*timebar_cursor);
1432 case FadeInHandleItem:
1433 case FadeOutHandleItem:
1434 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1436 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1438 rect->property_fill_color_rgba() = rv->get_fill_color();
1439 rect->property_outline_pixels() = 0;
1444 case AutomationTrackItem:
1445 if (is_drawable()) {
1446 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1447 clear_entered_track = true;
1448 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1460 Editor::left_automation_track ()
1462 if (clear_entered_track) {
1463 set_entered_track (0);
1464 clear_entered_track = false;
1470 _update_mouse_speed (void *arg)
1472 return static_cast<Editor*>(arg)->update_mouse_speed ();
1476 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1480 /* We call this so that MOTION_NOTIFY events continue to be
1481 delivered to the canvas. We need to do this because we set
1482 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1483 the density of the events, at the expense of a round-trip
1484 to the server. Given that this will mostly occur on cases
1485 where DISPLAY = :0.0, and given the cost of what the motion
1486 event might do, its a good tradeoff.
1489 track_canvas.get_pointer (x, y);
1491 if (current_stepping_trackview) {
1492 /* don't keep the persistent stepped trackview if the mouse moves */
1493 current_stepping_trackview = 0;
1494 step_timeout.disconnect ();
1497 if (session && session->actively_recording()) {
1498 /* Sorry. no dragging stuff around while we record */
1502 drag_info.item_type = item_type;
1503 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1504 &drag_info.current_pointer_y);
1506 switch (mouse_mode) {
1509 struct timeval tmnow;
1511 if (last_scrub_frame == 0) {
1513 /* first motion, just set up the variables */
1515 last_scrub_frame = (nframes64_t) drag_info.current_pointer_frame;
1516 gettimeofday (&tmnow, 0);
1517 last_scrub_time = tmnow.tv_sec * 1000000.0 + tmnow.tv_usec;
1518 session->request_locate (last_scrub_frame, true);
1521 /* how fast is the mouse moving ? */
1529 if (last_scrub_frame < (nframes64_t) drag_info.current_pointer_frame) {
1530 distance = (nframes64_t) drag_info.current_pointer_frame - last_scrub_frame;
1533 distance = last_scrub_frame - (nframes64_t) drag_info.current_pointer_frame;
1537 if (drag_info.grab_x < drag_info.current_pointer_x) {
1538 distance = drag_info.current_pointer_x - drag_info.grab_x;
1541 distance = drag_info.grab_x - drag_info.current_pointer_x;
1546 gettimeofday (&tmnow, 0);
1547 time = (tmnow.tv_sec * 1000000.0 + tmnow.tv_usec) - last_scrub_time;
1548 last_scrub_frame = drag_info.current_pointer_frame;
1549 last_scrub_time = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec;
1550 speed = ((double)distance/session->frame_rate()) / (time/1000000.0); // frames/sec
1552 add_mouse_speed (speed, dir);
1554 if (mouse_speed_update < 0) {
1555 mouse_speed_update = g_timeout_add (10, _update_mouse_speed, this);
1556 update_mouse_speed ();
1566 if (!from_autoscroll && drag_info.item) {
1567 /* item != 0 is the best test i can think of for dragging.
1569 if (!drag_info.move_threshold_passed) {
1571 bool x_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1572 bool y_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1574 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1576 // and change the initial grab loc/frame if this drag info wants us to
1578 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1579 drag_info.grab_frame = drag_info.current_pointer_frame;
1580 drag_info.grab_x = drag_info.current_pointer_x;
1581 drag_info.grab_y = drag_info.current_pointer_y;
1582 drag_info.last_pointer_frame = drag_info.grab_frame;
1583 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1588 switch (item_type) {
1589 case PlayheadCursorItem:
1590 case EditCursorItem:
1592 case ControlPointItem:
1593 case TempoMarkerItem:
1594 case MeterMarkerItem:
1595 case RegionViewNameHighlight:
1596 case StartSelectionTrimItem:
1597 case EndSelectionTrimItem:
1600 case AutomationLineItem:
1601 case FadeInHandleItem:
1602 case FadeOutHandleItem:
1605 case ImageFrameHandleStartItem:
1606 case ImageFrameHandleEndItem:
1607 case MarkerViewHandleStartItem:
1608 case MarkerViewHandleEndItem:
1611 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1612 (event->motion.state & Gdk::BUTTON2_MASK))) {
1613 if (!from_autoscroll) {
1614 maybe_autoscroll (event);
1616 (this->*(drag_info.motion_callback)) (item, event);
1625 switch (mouse_mode) {
1631 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1632 (event->motion.state & GDK_BUTTON2_MASK))) {
1633 if (!from_autoscroll) {
1634 maybe_autoscroll (event);
1636 (this->*(drag_info.motion_callback)) (item, event);
1647 track_canvas_motion (event);
1648 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1656 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1658 if (drag_info.item == 0) {
1659 fatal << _("programming error: start_grab called without drag item") << endmsg;
1665 cursor = grabber_cursor;
1668 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1670 if (event->button.button == 2) {
1671 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1672 drag_info.y_constrained = true;
1673 drag_info.x_constrained = false;
1675 drag_info.y_constrained = false;
1676 drag_info.x_constrained = true;
1679 drag_info.x_constrained = false;
1680 drag_info.y_constrained = false;
1683 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1684 drag_info.last_pointer_frame = drag_info.grab_frame;
1685 drag_info.current_pointer_frame = drag_info.grab_frame;
1686 drag_info.current_pointer_x = drag_info.grab_x;
1687 drag_info.current_pointer_y = drag_info.grab_y;
1688 drag_info.cumulative_x_drag = 0;
1689 drag_info.cumulative_y_drag = 0;
1690 drag_info.first_move = true;
1691 drag_info.move_threshold_passed = false;
1692 drag_info.want_move_threshold = false;
1693 drag_info.pointer_frame_offset = 0;
1694 drag_info.brushing = false;
1695 drag_info.copied_location = 0;
1697 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1699 event->button.time);
1701 if (session && session->transport_rolling()) {
1702 drag_info.was_rolling = true;
1704 drag_info.was_rolling = false;
1707 switch (snap_type) {
1708 case SnapToRegionStart:
1709 case SnapToRegionEnd:
1710 case SnapToRegionSync:
1711 case SnapToRegionBoundary:
1712 build_region_boundary_cache ();
1720 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1722 drag_info.item->ungrab (0);
1723 drag_info.item = new_item;
1726 cursor = grabber_cursor;
1729 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1733 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1735 bool did_drag = false;
1737 stop_canvas_autoscroll ();
1739 if (drag_info.item == 0) {
1743 drag_info.item->ungrab (event->button.time);
1745 if (drag_info.finished_callback) {
1746 (this->*(drag_info.finished_callback)) (item, event);
1749 did_drag = !drag_info.first_move;
1751 hide_verbose_canvas_cursor();
1754 drag_info.copy = false;
1755 drag_info.motion_callback = 0;
1756 drag_info.finished_callback = 0;
1757 drag_info.last_trackview = 0;
1758 drag_info.last_frame_position = 0;
1759 drag_info.grab_frame = 0;
1760 drag_info.last_pointer_frame = 0;
1761 drag_info.current_pointer_frame = 0;
1762 drag_info.brushing = false;
1764 if (drag_info.copied_location) {
1765 delete drag_info.copied_location;
1766 drag_info.copied_location = 0;
1773 Editor::set_edit_cursor (GdkEvent* event)
1775 nframes_t pointer_frame = event_frame (event);
1777 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1778 if (snap_type != SnapToEditCursor) {
1779 snap_to (pointer_frame);
1783 edit_cursor->set_position (pointer_frame);
1784 edit_cursor_clock.set (pointer_frame);
1788 Editor::set_playhead_cursor (GdkEvent* event)
1790 nframes_t pointer_frame = event_frame (event);
1792 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1793 snap_to (pointer_frame);
1797 session->request_locate (pointer_frame, session->transport_rolling());
1802 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1804 drag_info.item = item;
1805 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1806 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1810 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1811 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1815 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1817 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
1821 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1823 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1825 nframes_t fade_length;
1827 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1828 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1834 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1838 if (pos < (arv->region()->position() + 64)) {
1839 fade_length = 64; // this should be a minimum defined somewhere
1840 } else if (pos > arv->region()->last_frame()) {
1841 fade_length = arv->region()->length();
1843 fade_length = pos - arv->region()->position();
1845 /* mapover the region selection */
1847 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1849 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1855 tmp->reset_fade_in_shape_width (fade_length);
1858 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1860 drag_info.first_move = false;
1864 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1866 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1868 nframes_t fade_length;
1870 if (drag_info.first_move) return;
1872 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1873 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1878 if (pos < (arv->region()->position() + 64)) {
1879 fade_length = 64; // this should be a minimum defined somewhere
1880 } else if (pos > arv->region()->last_frame()) {
1881 fade_length = arv->region()->length();
1883 fade_length = pos - arv->region()->position();
1886 begin_reversible_command (_("change fade in length"));
1888 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1890 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1896 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
1897 XMLNode &before = alist->get_state();
1899 tmp->audio_region()->set_fade_in_length (fade_length);
1901 XMLNode &after = alist->get_state();
1902 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1905 commit_reversible_command ();
1909 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1911 drag_info.item = item;
1912 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1913 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1917 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1918 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1922 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1924 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
1928 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1930 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1932 nframes_t fade_length;
1934 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1935 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1940 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1944 if (pos > (arv->region()->last_frame() - 64)) {
1945 fade_length = 64; // this should really be a minimum fade defined somewhere
1947 else if (pos < arv->region()->position()) {
1948 fade_length = arv->region()->length();
1951 fade_length = arv->region()->last_frame() - pos;
1954 /* mapover the region selection */
1956 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1958 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1964 tmp->reset_fade_out_shape_width (fade_length);
1967 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1969 drag_info.first_move = false;
1973 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1975 if (drag_info.first_move) return;
1977 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1979 nframes_t fade_length;
1981 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1982 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1988 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1992 if (pos > (arv->region()->last_frame() - 64)) {
1993 fade_length = 64; // this should really be a minimum fade defined somewhere
1995 else if (pos < arv->region()->position()) {
1996 fade_length = arv->region()->length();
1999 fade_length = arv->region()->last_frame() - pos;
2002 begin_reversible_command (_("change fade out length"));
2004 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2006 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2012 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2013 XMLNode &before = alist->get_state();
2015 tmp->audio_region()->set_fade_out_length (fade_length);
2017 XMLNode &after = alist->get_state();
2018 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2021 commit_reversible_command ();
2025 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2027 drag_info.item = item;
2028 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2029 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2033 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2034 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2038 Cursor* cursor = (Cursor *) drag_info.data;
2040 if (cursor == playhead_cursor) {
2041 _dragging_playhead = true;
2043 if (session && drag_info.was_rolling) {
2044 session->request_stop ();
2047 if (session && session->is_auditioning()) {
2048 session->cancel_audition ();
2052 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2054 show_verbose_time_cursor (cursor->current_frame, 10);
2058 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2060 Cursor* cursor = (Cursor *) drag_info.data;
2061 nframes_t adjusted_frame;
2063 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2064 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2070 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2071 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
2072 snap_to (adjusted_frame);
2076 if (adjusted_frame == drag_info.last_pointer_frame) return;
2078 cursor->set_position (adjusted_frame);
2080 if (cursor == edit_cursor) {
2081 edit_cursor_clock.set (cursor->current_frame);
2083 UpdateAllTransportClocks (cursor->current_frame);
2086 show_verbose_time_cursor (cursor->current_frame, 10);
2088 drag_info.last_pointer_frame = adjusted_frame;
2089 drag_info.first_move = false;
2093 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2095 if (drag_info.first_move) return;
2097 cursor_drag_motion_callback (item, event);
2099 _dragging_playhead = false;
2101 if (item == &playhead_cursor->canvas_item) {
2103 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2105 } else if (item == &edit_cursor->canvas_item) {
2106 edit_cursor->set_position (edit_cursor->current_frame);
2107 edit_cursor_clock.set (edit_cursor->current_frame);
2112 Editor::update_marker_drag_item (Location *location)
2114 double x1 = frame_to_pixel (location->start());
2115 double x2 = frame_to_pixel (location->end());
2117 if (location->is_mark()) {
2118 marker_drag_line_points.front().set_x(x1);
2119 marker_drag_line_points.back().set_x(x1);
2120 marker_drag_line->property_points() = marker_drag_line_points;
2123 range_marker_drag_rect->property_x1() = x1;
2124 range_marker_drag_rect->property_x2() = x2;
2129 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2133 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2134 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2140 Location *location = find_location_from_marker (marker, is_start);
2142 drag_info.item = item;
2143 drag_info.data = marker;
2144 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2145 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2149 drag_info.copied_location = new Location (*location);
2150 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2152 update_marker_drag_item (location);
2154 if (location->is_mark()) {
2155 marker_drag_line->show();
2156 marker_drag_line->raise_to_top();
2159 range_marker_drag_rect->show();
2160 range_marker_drag_rect->raise_to_top();
2163 if (is_start) show_verbose_time_cursor (location->start(), 10);
2164 else show_verbose_time_cursor (location->end(), 10);
2168 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2171 Marker* marker = (Marker *) drag_info.data;
2172 Location *real_location;
2173 Location *copy_location;
2175 bool move_both = false;
2179 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2180 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2185 nframes_t next = newframe;
2187 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2188 snap_to (newframe, 0, true);
2191 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2195 /* call this to find out if its the start or end */
2197 real_location = find_location_from_marker (marker, is_start);
2199 /* use the copy that we're "dragging" around */
2201 copy_location = drag_info.copied_location;
2203 f_delta = copy_location->end() - copy_location->start();
2205 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2209 if (copy_location->is_mark()) {
2212 copy_location->set_start (newframe);
2216 if (is_start) { // start-of-range marker
2219 copy_location->set_start (newframe);
2220 copy_location->set_end (newframe + f_delta);
2221 } else if (newframe < copy_location->end()) {
2222 copy_location->set_start (newframe);
2224 snap_to (next, 1, true);
2225 copy_location->set_end (next);
2226 copy_location->set_start (newframe);
2229 } else { // end marker
2232 copy_location->set_end (newframe);
2233 copy_location->set_start (newframe - f_delta);
2234 } else if (newframe > copy_location->start()) {
2235 copy_location->set_end (newframe);
2237 } else if (newframe > 0) {
2238 snap_to (next, -1, true);
2239 copy_location->set_start (next);
2240 copy_location->set_end (newframe);
2245 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2246 drag_info.first_move = false;
2248 update_marker_drag_item (copy_location);
2250 LocationMarkers* lm = find_location_markers (real_location);
2251 lm->set_position (copy_location->start(), copy_location->end());
2253 show_verbose_time_cursor (newframe, 10);
2257 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2259 if (drag_info.first_move) {
2260 marker_drag_motion_callback (item, event);
2264 Marker* marker = (Marker *) drag_info.data;
2268 begin_reversible_command ( _("move marker") );
2269 XMLNode &before = session->locations()->get_state();
2271 Location * location = find_location_from_marker (marker, is_start);
2274 if (location->is_mark()) {
2275 location->set_start (drag_info.copied_location->start());
2277 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2281 XMLNode &after = session->locations()->get_state();
2282 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2283 commit_reversible_command ();
2285 marker_drag_line->hide();
2286 range_marker_drag_rect->hide();
2290 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2293 MeterMarker* meter_marker;
2295 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2296 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2300 meter_marker = dynamic_cast<MeterMarker*> (marker);
2302 MetricSection& section (meter_marker->meter());
2304 if (!section.movable()) {
2308 drag_info.item = item;
2309 drag_info.data = marker;
2310 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2311 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2315 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2317 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2321 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2324 MeterMarker* meter_marker;
2326 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2327 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2331 meter_marker = dynamic_cast<MeterMarker*> (marker);
2333 // create a dummy marker for visual representation of moving the copy.
2334 // The actual copying is not done before we reach the finish callback.
2336 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2337 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2338 *new MeterSection(meter_marker->meter()));
2340 drag_info.item = &new_marker->the_item();
2341 drag_info.copy = true;
2342 drag_info.data = new_marker;
2343 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2344 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2348 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2350 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2354 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2356 MeterMarker* marker = (MeterMarker *) drag_info.data;
2357 nframes_t adjusted_frame;
2359 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2360 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2366 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2367 snap_to (adjusted_frame);
2370 if (adjusted_frame == drag_info.last_pointer_frame) return;
2372 marker->set_position (adjusted_frame);
2375 drag_info.last_pointer_frame = adjusted_frame;
2376 drag_info.first_move = false;
2378 show_verbose_time_cursor (adjusted_frame, 10);
2382 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2384 if (drag_info.first_move) return;
2386 meter_marker_drag_motion_callback (drag_info.item, event);
2388 MeterMarker* marker = (MeterMarker *) drag_info.data;
2391 TempoMap& map (session->tempo_map());
2392 map.bbt_time (drag_info.last_pointer_frame, when);
2394 if (drag_info.copy == true) {
2395 begin_reversible_command (_("copy meter mark"));
2396 XMLNode &before = map.get_state();
2397 map.add_meter (marker->meter(), when);
2398 XMLNode &after = map.get_state();
2399 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2400 commit_reversible_command ();
2402 // delete the dummy marker we used for visual representation of copying.
2403 // a new visual marker will show up automatically.
2406 begin_reversible_command (_("move meter mark"));
2407 XMLNode &before = map.get_state();
2408 map.move_meter (marker->meter(), when);
2409 XMLNode &after = map.get_state();
2410 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2411 commit_reversible_command ();
2416 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2419 TempoMarker* tempo_marker;
2421 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2422 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2426 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2427 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2431 MetricSection& section (tempo_marker->tempo());
2433 if (!section.movable()) {
2437 drag_info.item = item;
2438 drag_info.data = marker;
2439 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2440 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2444 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2445 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2449 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2452 TempoMarker* tempo_marker;
2454 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2455 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2459 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2460 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2464 // create a dummy marker for visual representation of moving the copy.
2465 // The actual copying is not done before we reach the finish callback.
2467 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2468 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2469 *new TempoSection(tempo_marker->tempo()));
2471 drag_info.item = &new_marker->the_item();
2472 drag_info.copy = true;
2473 drag_info.data = new_marker;
2474 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2475 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2479 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2481 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2485 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2487 TempoMarker* marker = (TempoMarker *) drag_info.data;
2488 nframes_t adjusted_frame;
2490 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2491 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2497 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2498 snap_to (adjusted_frame);
2501 if (adjusted_frame == drag_info.last_pointer_frame) return;
2503 /* OK, we've moved far enough to make it worth actually move the thing. */
2505 marker->set_position (adjusted_frame);
2507 show_verbose_time_cursor (adjusted_frame, 10);
2509 drag_info.last_pointer_frame = adjusted_frame;
2510 drag_info.first_move = false;
2514 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2516 if (drag_info.first_move) return;
2518 tempo_marker_drag_motion_callback (drag_info.item, event);
2520 TempoMarker* marker = (TempoMarker *) drag_info.data;
2523 TempoMap& map (session->tempo_map());
2524 map.bbt_time (drag_info.last_pointer_frame, when);
2526 if (drag_info.copy == true) {
2527 begin_reversible_command (_("copy tempo mark"));
2528 XMLNode &before = map.get_state();
2529 map.add_tempo (marker->tempo(), when);
2530 XMLNode &after = map.get_state();
2531 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2532 commit_reversible_command ();
2534 // delete the dummy marker we used for visual representation of copying.
2535 // a new visual marker will show up automatically.
2538 begin_reversible_command (_("move tempo mark"));
2539 XMLNode &before = map.get_state();
2540 map.move_tempo (marker->tempo(), when);
2541 XMLNode &after = map.get_state();
2542 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2543 commit_reversible_command ();
2548 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2550 ControlPoint* control_point;
2552 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2553 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2557 // We shouldn't remove the first or last gain point
2558 if (control_point->line().is_last_point(*control_point) ||
2559 control_point->line().is_first_point(*control_point)) {
2563 control_point->line().remove_point (*control_point);
2567 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2569 ControlPoint* control_point;
2571 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2572 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2576 control_point->line().remove_point (*control_point);
2580 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2582 ControlPoint* control_point;
2584 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2585 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2589 drag_info.item = item;
2590 drag_info.data = control_point;
2591 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2592 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2594 start_grab (event, fader_cursor);
2596 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2598 double fraction = 1.0 - ((control_point->get_y() - control_point->line().y_position()) / (double)control_point->line().height());
2599 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
2600 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2602 show_verbose_canvas_cursor ();
2606 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2608 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2610 double cx = drag_info.current_pointer_x;
2611 double cy = drag_info.current_pointer_y;
2613 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2614 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2616 if (drag_info.x_constrained) {
2617 cx = drag_info.grab_x;
2619 if (drag_info.y_constrained) {
2620 cy = drag_info.grab_y;
2623 cp->line().parent_group().w2i (cx, cy);
2627 cy = min ((double) (cp->line().y_position() + cp->line().height()), cy);
2629 //translate cx to frames
2630 nframes_t cx_frames = unit_to_frame (cx);
2632 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2633 snap_to (cx_frames);
2636 const double fraction = 1.0 - ((cy - cp->line().y_position()) / (double)cp->line().height());
2640 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2646 cp->line().point_drag (*cp, cx_frames , fraction, push);
2648 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
2650 drag_info.first_move = false;
2654 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2656 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2658 if (drag_info.first_move) {
2662 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2663 reset_point_selection ();
2667 control_point_drag_motion_callback (item, event);
2669 cp->line().end_drag (cp);
2673 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2675 switch (mouse_mode) {
2677 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2678 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2686 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2690 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2691 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2695 start_line_grab (al, event);
2699 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2703 nframes_t frame_within_region;
2705 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2709 cx = event->button.x;
2710 cy = event->button.y;
2711 line->parent_group().w2i (cx, cy);
2712 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2714 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2715 current_line_drag_info.after)) {
2716 /* no adjacent points */
2720 drag_info.item = &line->grab_item();
2721 drag_info.data = line;
2722 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2723 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2725 start_grab (event, fader_cursor);
2727 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2729 line->start_drag (0, drag_info.grab_frame, fraction);
2731 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2732 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2733 show_verbose_canvas_cursor ();
2737 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2739 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2740 double cx = drag_info.current_pointer_x;
2741 double cy = drag_info.current_pointer_y;
2743 line->parent_group().w2i (cx, cy);
2745 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2749 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2755 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2757 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2761 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2763 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2764 line_drag_motion_callback (item, event);
2769 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2771 if (selection->regions.empty() || clicked_regionview == 0) {
2775 drag_info.copy = false;
2776 drag_info.item = item;
2777 drag_info.data = clicked_regionview;
2778 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2779 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2784 TimeAxisView* tvp = clicked_axisview;
2785 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2787 if (tv && tv->is_track()) {
2788 speed = tv->get_diskstream()->speed();
2791 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2792 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2793 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2794 // we want a move threshold
2795 drag_info.want_move_threshold = true;
2797 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2799 begin_reversible_command (_("move region(s)"));
2803 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2805 drag_info.copy = false;
2806 drag_info.item = item;
2807 drag_info.data = clicked_axisview;
2808 drag_info.last_trackview = clicked_axisview;
2809 drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
2810 drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
2816 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2818 if (selection->regions.empty() || clicked_regionview == 0) {
2822 drag_info.copy = true;
2823 drag_info.item = item;
2824 drag_info.data = clicked_regionview;
2828 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2829 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2832 if (rtv && rtv->is_track()) {
2833 speed = rtv->get_diskstream()->speed();
2836 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2837 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2838 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2839 // we want a move threshold
2840 drag_info.want_move_threshold = true;
2841 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2842 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2843 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2847 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2849 if (selection->regions.empty() || clicked_regionview == 0) {
2853 drag_info.copy = false;
2854 drag_info.item = item;
2855 drag_info.data = clicked_regionview;
2856 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2857 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2862 TimeAxisView* tvp = clicked_axisview;
2863 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2865 if (tv && tv->is_track()) {
2866 speed = tv->get_diskstream()->speed();
2869 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2870 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2871 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2872 // we want a move threshold
2873 drag_info.want_move_threshold = true;
2874 drag_info.brushing = true;
2876 begin_reversible_command (_("Drag region brush"));
2880 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2884 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2885 nframes_t pending_region_position = 0;
2886 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2887 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2888 bool clamp_y_axis = false;
2889 vector<int32_t> height_list(512) ;
2890 vector<int32_t>::iterator j;
2892 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2894 drag_info.want_move_threshold = false; // don't copy again
2896 /* duplicate the region(s) */
2898 vector<RegionView*> new_regionviews;
2900 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2906 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2907 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2910 nrv = new AudioRegionView (*arv);
2912 nrv = new MidiRegionView (*mrv);
2917 nrv->get_canvas_group()->show ();
2919 new_regionviews.push_back (nrv);
2922 if (new_regionviews.empty()) {
2926 /* reset selection to new regionviews */
2928 selection->set (new_regionviews);
2930 /* reset drag_info data to reflect the fact that we are dragging the copies */
2932 drag_info.data = new_regionviews.front();
2934 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2937 /* Which trackview is this ? */
2939 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2940 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2942 /* The region motion is only processed if the pointer is over
2946 if (!tv || !tv->is_track()) {
2947 /* To make sure we hide the verbose canvas cursor when the mouse is
2948 not held over a track.
2950 hide_verbose_canvas_cursor ();
2954 original_pointer_order = drag_info.last_trackview->order;
2956 /************************************************************
2958 ************************************************************/
2960 if (drag_info.brushing) {
2961 clamp_y_axis = true;
2966 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2968 int32_t children = 0, numtracks = 0;
2969 // XXX hard coding track limit, oh my, so very very bad
2970 bitset <1024> tracks (0x00);
2971 /* get a bitmask representing the visible tracks */
2973 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2974 TimeAxisView *tracklist_timeview;
2975 tracklist_timeview = (*i);
2976 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2977 TimeAxisView::Children children_list;
2979 /* zeroes are audio tracks. ones are other types. */
2981 if (!rtv2->hidden()) {
2983 if (visible_y_high < rtv2->order) {
2984 visible_y_high = rtv2->order;
2986 if (visible_y_low > rtv2->order) {
2987 visible_y_low = rtv2->order;
2990 if (!rtv2->is_track()) {
2991 tracks = tracks |= (0x01 << rtv2->order);
2994 height_list[rtv2->order] = (*i)->height;
2996 if ((children_list = rtv2->get_child_list()).size() > 0) {
2997 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2998 tracks = tracks |= (0x01 << (rtv2->order + children));
2999 height_list[rtv2->order + children] = (*j)->height;
3007 /* find the actual span according to the canvas */
3009 canvas_pointer_y_span = pointer_y_span;
3010 if (drag_info.last_trackview->order >= tv->order) {
3012 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3013 if (height_list[y] == 0 ) {
3014 canvas_pointer_y_span--;
3019 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3020 if ( height_list[y] == 0 ) {
3021 canvas_pointer_y_span++;
3026 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3027 RegionView* rv2 = (*i);
3028 double ix1, ix2, iy1, iy2;
3031 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3032 rv2->get_canvas_group()->i2w (ix1, iy1);
3033 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3034 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3036 if (rtv2->order != original_pointer_order) {
3037 /* this isn't the pointer track */
3039 if (canvas_pointer_y_span > 0) {
3041 /* moving up the canvas */
3042 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3044 int32_t visible_tracks = 0;
3045 while (visible_tracks < canvas_pointer_y_span ) {
3048 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3049 /* we're passing through a hidden track */
3054 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3055 clamp_y_axis = true;
3059 clamp_y_axis = true;
3062 } else if (canvas_pointer_y_span < 0) {
3064 /*moving down the canvas*/
3066 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3069 int32_t visible_tracks = 0;
3071 while (visible_tracks > canvas_pointer_y_span ) {
3074 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3078 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3079 clamp_y_axis = true;
3084 clamp_y_axis = true;
3090 /* this is the pointer's track */
3091 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3092 clamp_y_axis = true;
3093 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3094 clamp_y_axis = true;
3102 } else if (drag_info.last_trackview == tv) {
3103 clamp_y_axis = true;
3107 if (!clamp_y_axis) {
3108 drag_info.last_trackview = tv;
3111 /************************************************************
3113 ************************************************************/
3115 /* compute the amount of pointer motion in frames, and where
3116 the region would be if we moved it by that much.
3119 if (drag_info.move_threshold_passed) {
3121 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3123 nframes_t sync_frame;
3124 nframes_t sync_offset;
3127 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3129 sync_offset = rv->region()->sync_offset (sync_dir);
3130 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3132 /* we snap if the snap modifier is not enabled.
3135 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3136 snap_to (sync_frame);
3139 if (sync_frame - sync_offset <= sync_frame) {
3140 pending_region_position = sync_frame - (sync_dir*sync_offset);
3142 pending_region_position = 0;
3146 pending_region_position = 0;
3149 if (pending_region_position > max_frames - rv->region()->length()) {
3150 pending_region_position = drag_info.last_frame_position;
3153 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3155 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3157 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3158 to make it appear at the new location.
3161 if (pending_region_position > drag_info.last_frame_position) {
3162 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3164 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3167 drag_info.last_frame_position = pending_region_position;
3174 /* threshold not passed */
3179 /*************************************************************
3181 ************************************************************/
3183 if (x_delta == 0 && (pointer_y_span == 0)) {
3184 /* haven't reached next snap point, and we're not switching
3185 trackviews. nothing to do.
3192 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3194 RegionView* rv2 = (*i);
3196 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3198 double ix1, ix2, iy1, iy2;
3199 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3200 rv2->get_canvas_group()->i2w (ix1, iy1);
3209 /*************************************************************
3211 ************************************************************/
3215 if (drag_info.first_move) {
3216 if (drag_info.move_threshold_passed) {
3227 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3228 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3230 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3232 RegionView* rv = (*i);
3233 double ix1, ix2, iy1, iy2;
3234 int32_t temp_pointer_y_span = pointer_y_span;
3236 /* get item BBox, which will be relative to parent. so we have
3237 to query on a child, then convert to world coordinates using
3241 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3242 rv->get_canvas_group()->i2w (ix1, iy1);
3243 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3244 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3245 RouteTimeAxisView* temp_rtv;
3247 if ((pointer_y_span != 0) && !clamp_y_axis) {
3250 for (j = height_list.begin(); j!= height_list.end(); j++) {
3251 if (x == canvas_rtv->order) {
3252 /* we found the track the region is on */
3253 if (x != original_pointer_order) {
3254 /*this isn't from the same track we're dragging from */
3255 temp_pointer_y_span = canvas_pointer_y_span;
3257 while (temp_pointer_y_span > 0) {
3258 /* we're moving up canvas-wise,
3259 so we need to find the next track height
3261 if (j != height_list.begin()) {
3264 if (x != original_pointer_order) {
3265 /* we're not from the dragged track, so ignore hidden tracks. */
3267 temp_pointer_y_span++;
3271 temp_pointer_y_span--;
3273 while (temp_pointer_y_span < 0) {
3275 if (x != original_pointer_order) {
3277 temp_pointer_y_span--;
3281 if (j != height_list.end()) {
3284 temp_pointer_y_span++;
3286 /* find out where we'll be when we move and set height accordingly */
3288 tvp2 = trackview_by_y_position (iy1 + y_delta);
3289 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3290 rv->set_y_position_and_height (0, temp_rtv->height);
3292 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3293 personally, i think this can confuse things, but never mind.
3296 //const GdkColor& col (temp_rtv->view->get_region_color());
3297 //rv->set_color (const_cast<GdkColor&>(col));
3304 /* prevent the regionview from being moved to before
3305 the zero position on the canvas.
3310 if (-x_delta > ix1) {
3313 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3314 x_delta = max_frames - rv->region()->last_frame();
3318 if (drag_info.first_move) {
3320 /* hide any dependent views */
3322 rv->get_time_axis_view().hide_dependent_views (*rv);
3324 /* this is subtle. raising the regionview itself won't help,
3325 because raise_to_top() just puts the item on the top of
3326 its parent's stack. so, we need to put the trackview canvas_display group
3327 on the top, since its parent is the whole canvas.
3330 rv->get_canvas_group()->raise_to_top();
3331 rv->get_time_axis_view().canvas_display->raise_to_top();
3332 cursor_group->raise_to_top();
3334 rv->fake_set_opaque (true);
3337 if (drag_info.brushing) {
3338 mouse_brush_insert_region (rv, pending_region_position);
3340 rv->move (x_delta, y_delta);
3343 } /* foreach region */
3347 if (drag_info.first_move && drag_info.move_threshold_passed) {
3348 cursor_group->raise_to_top();
3349 drag_info.first_move = false;
3352 if (x_delta != 0 && !drag_info.brushing) {
3353 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3358 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3361 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3362 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3363 bool nocommit = true;
3365 RouteTimeAxisView* rtv;
3366 bool regionview_y_movement;
3367 bool regionview_x_movement;
3368 vector<RegionView*> copies;
3370 /* first_move is set to false if the regionview has been moved in the
3374 if (drag_info.first_move) {
3381 /* The regionview has been moved at some stage during the grab so we need
3382 to account for any mouse movement between this event and the last one.
3385 region_drag_motion_callback (item, event);
3387 if (drag_info.brushing) {
3388 /* all changes were made during motion event handlers */
3390 if (drag_info.copy) {
3391 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3392 copies.push_back (*i);
3399 /* adjust for track speed */
3402 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3403 if (rtv && rtv->get_diskstream()) {
3404 speed = rtv->get_diskstream()->speed();
3407 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3408 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3410 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3411 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3415 if (drag_info.copy) {
3416 if (drag_info.x_constrained) {
3417 op_string = _("fixed time region copy");
3419 op_string = _("region copy");
3422 if (drag_info.x_constrained) {
3423 op_string = _("fixed time region drag");
3425 op_string = _("region drag");
3429 begin_reversible_command (op_string);
3431 if (regionview_y_movement) {
3433 /* moved to a different audio track. */
3435 vector<RegionView*> new_selection;
3437 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3439 RegionView* rv = (*i);
3441 double ix1, ix2, iy1, iy2;
3443 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3444 rv->get_canvas_group()->i2w (ix1, iy1);
3445 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3446 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3448 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3449 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3451 where = (nframes_t) (unit_to_frame (ix1) * speed);
3452 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3454 /* undo the previous hide_dependent_views so that xfades don't
3455 disappear on copying regions
3458 rv->get_time_axis_view().reveal_dependent_views (*rv);
3460 if (!drag_info.copy) {
3462 /* the region that used to be in the old playlist is not
3463 moved to the new one - we make a copy of it. as a result,
3464 any existing editor for the region should no longer be
3468 rv->hide_region_editor();
3469 rv->fake_set_opaque (false);
3471 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3472 from_playlist->remove_region ((rv->region()));
3473 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3477 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3479 copies.push_back (rv);
3482 latest_regionview = 0;
3484 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3485 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3486 to_playlist->add_region (new_region, where);
3487 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3490 if (latest_regionview) {
3491 new_selection.push_back (latest_regionview);
3494 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3495 was selected in all of them, then removing it from the playlist will have removed all
3496 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3497 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3498 corresponding regionview, and the selection is now empty).
3500 this could have invalidated any and all iterators into the region selection.
3502 the heuristic we use here is: if the region selection is empty, break out of the loop
3503 here. if the region selection is not empty, then restart the loop because we know that
3504 we must have removed at least the region(view) we've just been working on as well as any
3505 that we processed on previous iterations.
3507 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3508 we can just iterate.
3511 if (drag_info.copy) {
3514 if (selection->regions.empty()) {
3517 i = selection->regions.by_layer().begin();
3522 selection->set (new_selection);
3526 /* motion within a single track */
3528 list<RegionView*> regions = selection->regions.by_layer();
3530 if (drag_info.copy) {
3531 selection->clear_regions();
3534 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3538 if (!rv->region()->can_move()) {
3542 if (regionview_x_movement) {
3543 double ownspeed = 1.0;
3544 rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3546 if (rtv && rtv->get_diskstream()) {
3547 ownspeed = rtv->get_diskstream()->speed();
3550 /* base the new region position on the current position of the regionview.*/
3552 double ix1, ix2, iy1, iy2;
3554 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3555 rv->get_canvas_group()->i2w (ix1, iy1);
3556 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3560 where = rv->region()->position();
3563 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3565 assert (to_playlist);
3569 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3571 if (drag_info.copy) {
3573 boost::shared_ptr<Region> newregion;
3574 boost::shared_ptr<Region> ar;
3575 boost::shared_ptr<Region> mr;
3577 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3578 newregion = RegionFactory::create (ar);
3579 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3580 newregion = RegionFactory::create (mr);
3585 latest_regionview = 0;
3586 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3587 to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
3590 if (latest_regionview) {
3591 rtv->reveal_dependent_views (*latest_regionview);
3592 selection->add (latest_regionview);
3595 /* if the original region was locked, we don't care for the new one */
3597 newregion->set_locked (false);
3601 /* just change the model */
3603 rv->region()->set_position (where, (void*) this);
3609 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3611 if (drag_info.copy) {
3612 copies.push_back (rv);
3620 commit_reversible_command ();
3623 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3630 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3632 if (drag_info.move_threshold_passed) {
3633 if (drag_info.first_move) {
3634 // TODO: create region-create-drag region view here
3635 drag_info.first_move = false;
3638 // TODO: resize region-create-drag region view here
3643 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3645 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.last_trackview);
3649 const boost::shared_ptr<MidiDiskstream> diskstream =
3650 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
3653 warning << "Cannot create non-MIDI region" << endl;
3657 if (drag_info.first_move) {
3658 begin_reversible_command (_("create region"));
3659 XMLNode &before = mtv->playlist()->get_state();
3661 nframes_t start = drag_info.grab_frame;
3662 snap_to (start, -1);
3663 const Meter& m = session->tempo_map().meter_at(start);
3664 const Tempo& t = session->tempo_map().tempo_at(start);
3665 double length = m.frames_per_bar(t, session->frame_rate());
3667 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
3669 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>(RegionFactory::create(
3670 src, 0, length, PBD::basename_nosuffix(src->name()))), start);
3671 XMLNode &after = mtv->playlist()->get_state();
3672 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
3673 commit_reversible_command();
3676 create_region_drag_motion_callback (item, event);
3677 // TODO: create region-create-drag region here
3682 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3684 /* Either add to or set the set the region selection, unless
3685 this is an alignment click (control used)
3688 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3689 TimeAxisView* tv = &rv.get_time_axis_view();
3690 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3692 if (rtv && rtv->is_track()) {
3693 speed = rtv->get_diskstream()->speed();
3696 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3698 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3700 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3702 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3706 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3712 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3718 nframes_t frame_rate;
3725 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3726 case AudioClock::BBT:
3727 session->bbt_time (frame, bbt);
3728 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3731 case AudioClock::SMPTE:
3732 session->smpte_time (frame, smpte);
3733 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3736 case AudioClock::MinSec:
3737 /* XXX this is copied from show_verbose_duration_cursor() */
3738 frame_rate = session->frame_rate();
3739 hours = frame / (frame_rate * 3600);
3740 frame = frame % (frame_rate * 3600);
3741 mins = frame / (frame_rate * 60);
3742 frame = frame % (frame_rate * 60);
3743 secs = (float) frame / (float) frame_rate;
3744 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3748 snprintf (buf, sizeof(buf), "%u", frame);
3752 if (xpos >= 0 && ypos >=0) {
3753 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3756 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3758 show_verbose_canvas_cursor ();
3762 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3769 nframes_t distance, frame_rate;
3771 Meter meter_at_start(session->tempo_map().meter_at(start));
3777 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3778 case AudioClock::BBT:
3779 session->bbt_time (start, sbbt);
3780 session->bbt_time (end, ebbt);
3783 /* XXX this computation won't work well if the
3784 user makes a selection that spans any meter changes.
3787 ebbt.bars -= sbbt.bars;
3788 if (ebbt.beats >= sbbt.beats) {
3789 ebbt.beats -= sbbt.beats;
3792 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3794 if (ebbt.ticks >= sbbt.ticks) {
3795 ebbt.ticks -= sbbt.ticks;
3798 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3801 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3804 case AudioClock::SMPTE:
3805 session->smpte_duration (end - start, smpte);
3806 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3809 case AudioClock::MinSec:
3810 /* XXX this stuff should be elsewhere.. */
3811 distance = end - start;
3812 frame_rate = session->frame_rate();
3813 hours = distance / (frame_rate * 3600);
3814 distance = distance % (frame_rate * 3600);
3815 mins = distance / (frame_rate * 60);
3816 distance = distance % (frame_rate * 60);
3817 secs = (float) distance / (float) frame_rate;
3818 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3822 snprintf (buf, sizeof(buf), "%u", end - start);
3826 if (xpos >= 0 && ypos >=0) {
3827 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3830 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3832 show_verbose_canvas_cursor ();
3836 Editor::collect_new_region_view (RegionView* rv)
3838 latest_regionview = rv;
3842 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3844 if (clicked_regionview == 0) {
3848 /* lets try to create new Region for the selection */
3850 vector<boost::shared_ptr<AudioRegion> > new_regions;
3851 create_region_from_selection (new_regions);
3853 if (new_regions.empty()) {
3857 /* XXX fix me one day to use all new regions */
3859 boost::shared_ptr<Region> region (new_regions.front());
3861 /* add it to the current stream/playlist.
3863 tricky: the streamview for the track will add a new regionview. we will
3864 catch the signal it sends when it creates the regionview to
3865 set the regionview we want to then drag.
3868 latest_regionview = 0;
3869 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3871 /* A selection grab currently creates two undo/redo operations, one for
3872 creating the new region and another for moving it.
3875 begin_reversible_command (_("selection grab"));
3877 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3879 XMLNode *before = &(playlist->get_state());
3880 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3881 XMLNode *after = &(playlist->get_state());
3882 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3884 commit_reversible_command ();
3888 if (latest_regionview == 0) {
3889 /* something went wrong */
3893 /* we need to deselect all other regionviews, and select this one
3894 i'm ignoring undo stuff, because the region creation will take care of it */
3895 selection->set (latest_regionview);
3897 drag_info.item = latest_regionview->get_canvas_group();
3898 drag_info.data = latest_regionview;
3899 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3900 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3904 drag_info.last_trackview = clicked_axisview;
3905 drag_info.last_frame_position = latest_regionview->region()->position();
3906 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3908 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3912 Editor::cancel_selection ()
3914 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3915 (*i)->hide_selection ();
3917 begin_reversible_command (_("cancel selection"));
3918 selection->clear ();
3919 clicked_selection = 0;
3920 commit_reversible_command ();
3924 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3926 nframes_t start = 0;
3933 drag_info.item = item;
3934 drag_info.motion_callback = &Editor::drag_selection;
3935 drag_info.finished_callback = &Editor::end_selection_op;
3940 case CreateSelection:
3941 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3942 drag_info.copy = true;
3944 drag_info.copy = false;
3946 start_grab (event, selector_cursor);
3949 case SelectionStartTrim:
3950 if (clicked_axisview) {
3951 clicked_axisview->order_selection_trims (item, true);
3953 start_grab (event, trimmer_cursor);
3954 start = selection->time[clicked_selection].start;
3955 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3958 case SelectionEndTrim:
3959 if (clicked_axisview) {
3960 clicked_axisview->order_selection_trims (item, false);
3962 start_grab (event, trimmer_cursor);
3963 end = selection->time[clicked_selection].end;
3964 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3968 start = selection->time[clicked_selection].start;
3970 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3974 if (selection_op == SelectionMove) {
3975 show_verbose_time_cursor(start, 10);
3977 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3982 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3984 nframes_t start = 0;
3987 nframes_t pending_position;
3989 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3990 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3992 pending_position = 0;
3995 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3996 snap_to (pending_position);
3999 /* only alter selection if the current frame is
4000 different from the last frame position (adjusted)
4003 if (pending_position == drag_info.last_pointer_frame) return;
4005 switch (selection_op) {
4006 case CreateSelection:
4008 if (drag_info.first_move) {
4009 snap_to (drag_info.grab_frame);
4012 if (pending_position < drag_info.grab_frame) {
4013 start = pending_position;
4014 end = drag_info.grab_frame;
4016 end = pending_position;
4017 start = drag_info.grab_frame;
4020 /* first drag: Either add to the selection
4021 or create a new selection->
4024 if (drag_info.first_move) {
4026 begin_reversible_command (_("range selection"));
4028 if (drag_info.copy) {
4029 /* adding to the selection */
4030 clicked_selection = selection->add (start, end);
4031 drag_info.copy = false;
4033 /* new selection-> */
4034 clicked_selection = selection->set (clicked_axisview, start, end);
4039 case SelectionStartTrim:
4041 if (drag_info.first_move) {
4042 begin_reversible_command (_("trim selection start"));
4045 start = selection->time[clicked_selection].start;
4046 end = selection->time[clicked_selection].end;
4048 if (pending_position > end) {
4051 start = pending_position;
4055 case SelectionEndTrim:
4057 if (drag_info.first_move) {
4058 begin_reversible_command (_("trim selection end"));
4061 start = selection->time[clicked_selection].start;
4062 end = selection->time[clicked_selection].end;
4064 if (pending_position < start) {
4067 end = pending_position;
4074 if (drag_info.first_move) {
4075 begin_reversible_command (_("move selection"));
4078 start = selection->time[clicked_selection].start;
4079 end = selection->time[clicked_selection].end;
4081 length = end - start;
4083 start = pending_position;
4086 end = start + length;
4091 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4092 start_canvas_autoscroll (1);
4096 selection->replace (clicked_selection, start, end);
4099 drag_info.last_pointer_frame = pending_position;
4100 drag_info.first_move = false;
4102 if (selection_op == SelectionMove) {
4103 show_verbose_time_cursor(start, 10);
4105 show_verbose_time_cursor(pending_position, 10);
4110 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4112 if (!drag_info.first_move) {
4113 drag_selection (item, event);
4114 /* XXX this is not object-oriented programming at all. ick */
4115 if (selection->time.consolidate()) {
4116 selection->TimeChanged ();
4118 commit_reversible_command ();
4120 /* just a click, no pointer movement.*/
4122 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4124 selection->clear_time();
4129 /* XXX what happens if its a music selection? */
4130 session->set_audio_range (selection->time);
4131 stop_canvas_autoscroll ();
4135 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4138 TimeAxisView* tvp = clicked_axisview;
4139 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4141 if (tv && tv->is_track()) {
4142 speed = tv->get_diskstream()->speed();
4145 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4146 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4147 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4149 //drag_info.item = clicked_regionview->get_name_highlight();
4150 drag_info.item = item;
4151 drag_info.motion_callback = &Editor::trim_motion_callback;
4152 drag_info.finished_callback = &Editor::trim_finished_callback;
4154 start_grab (event, trimmer_cursor);
4156 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4157 trim_op = ContentsTrim;
4159 /* These will get overridden for a point trim.*/
4160 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4161 /* closer to start */
4162 trim_op = StartTrim;
4163 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4171 show_verbose_time_cursor(region_start, 10);
4174 show_verbose_time_cursor(region_end, 10);
4177 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4183 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4185 RegionView* rv = clicked_regionview;
4186 nframes_t frame_delta = 0;
4187 bool left_direction;
4188 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4190 /* snap modifier works differently here..
4191 its' current state has to be passed to the
4192 various trim functions in order to work properly
4196 TimeAxisView* tvp = clicked_axisview;
4197 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4198 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4200 if (tv && tv->is_track()) {
4201 speed = tv->get_diskstream()->speed();
4204 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4205 left_direction = true;
4207 left_direction = false;
4211 snap_to (drag_info.current_pointer_frame);
4214 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4218 if (drag_info.first_move) {
4224 trim_type = "Region start trim";
4227 trim_type = "Region end trim";
4230 trim_type = "Region content trim";
4234 begin_reversible_command (trim_type);
4236 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4237 (*i)->fake_set_opaque(false);
4238 (*i)->region()->freeze ();
4240 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4242 arv->temporarily_hide_envelope ();
4244 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4245 insert_result = motion_frozen_playlists.insert (pl);
4246 if (insert_result.second) {
4247 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4252 if (left_direction) {
4253 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4255 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4260 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4263 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4264 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4270 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4273 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4274 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4281 bool swap_direction = false;
4283 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4284 swap_direction = true;
4287 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4288 i != selection->regions.by_layer().end(); ++i)
4290 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4298 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4301 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4304 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4308 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4309 drag_info.first_move = false;
4313 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4315 boost::shared_ptr<Region> region (rv.region());
4317 if (region->locked()) {
4321 nframes_t new_bound;
4324 TimeAxisView* tvp = clicked_axisview;
4325 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4327 if (tv && tv->is_track()) {
4328 speed = tv->get_diskstream()->speed();
4331 if (left_direction) {
4332 if (swap_direction) {
4333 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4335 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4338 if (swap_direction) {
4339 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4341 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4346 snap_to (new_bound);
4348 region->trim_start ((nframes_t) (new_bound * speed), this);
4349 rv.region_changed (StartChanged);
4353 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4355 boost::shared_ptr<Region> region (rv.region());
4357 if (region->locked()) {
4361 nframes_t new_bound;
4364 TimeAxisView* tvp = clicked_axisview;
4365 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4367 if (tv && tv->is_track()) {
4368 speed = tv->get_diskstream()->speed();
4371 if (left_direction) {
4372 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4374 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4378 snap_to (new_bound, (left_direction ? 0 : 1));
4381 region->trim_front ((nframes_t) (new_bound * speed), this);
4383 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4387 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4389 boost::shared_ptr<Region> region (rv.region());
4391 if (region->locked()) {
4395 nframes_t new_bound;
4398 TimeAxisView* tvp = clicked_axisview;
4399 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4401 if (tv && tv->is_track()) {
4402 speed = tv->get_diskstream()->speed();
4405 if (left_direction) {
4406 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4408 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4412 snap_to (new_bound);
4414 region->trim_end ((nframes_t) (new_bound * speed), this);
4415 rv.region_changed (LengthChanged);
4419 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4421 if (!drag_info.first_move) {
4422 trim_motion_callback (item, event);
4424 if (!clicked_regionview->get_selected()) {
4425 thaw_region_after_trim (*clicked_regionview);
4428 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4429 i != selection->regions.by_layer().end(); ++i)
4431 thaw_region_after_trim (**i);
4432 (*i)->fake_set_opaque (true);
4436 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4438 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4441 motion_frozen_playlists.clear ();
4443 commit_reversible_command();
4445 /* no mouse movement */
4451 Editor::point_trim (GdkEvent* event)
4453 RegionView* rv = clicked_regionview;
4454 nframes_t new_bound = drag_info.current_pointer_frame;
4456 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4457 snap_to (new_bound);
4460 /* Choose action dependant on which button was pressed */
4461 switch (event->button.button) {
4463 trim_op = StartTrim;
4464 begin_reversible_command (_("Start point trim"));
4466 if (rv->get_selected()) {
4468 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4469 i != selection->regions.by_layer().end(); ++i)
4471 if (!(*i)->region()->locked()) {
4472 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4473 XMLNode &before = pl->get_state();
4474 (*i)->region()->trim_front (new_bound, this);
4475 XMLNode &after = pl->get_state();
4476 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4482 if (!rv->region()->locked()) {
4483 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4484 XMLNode &before = pl->get_state();
4485 rv->region()->trim_front (new_bound, this);
4486 XMLNode &after = pl->get_state();
4487 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4491 commit_reversible_command();
4496 begin_reversible_command (_("End point trim"));
4498 if (rv->get_selected()) {
4500 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4502 if (!(*i)->region()->locked()) {
4503 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4504 XMLNode &before = pl->get_state();
4505 (*i)->region()->trim_end (new_bound, this);
4506 XMLNode &after = pl->get_state();
4507 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4513 if (!rv->region()->locked()) {
4514 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4515 XMLNode &before = pl->get_state();
4516 rv->region()->trim_end (new_bound, this);
4517 XMLNode &after = pl->get_state();
4518 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4522 commit_reversible_command();
4531 Editor::thaw_region_after_trim (RegionView& rv)
4533 boost::shared_ptr<Region> region (rv.region());
4535 if (region->locked()) {
4539 region->thaw (_("trimmed region"));
4540 XMLNode &after = region->playlist()->get_state();
4541 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4543 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4545 arv->unhide_envelope ();
4549 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4554 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4555 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4559 Location* location = find_location_from_marker (marker, is_start);
4560 location->set_hidden (true, this);
4565 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4571 drag_info.item = item;
4572 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4573 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4575 range_marker_op = op;
4577 if (!temp_location) {
4578 temp_location = new Location;
4582 case CreateRangeMarker:
4583 case CreateTransportMarker:
4585 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4586 drag_info.copy = true;
4588 drag_info.copy = false;
4590 start_grab (event, selector_cursor);
4594 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4599 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4601 nframes_t start = 0;
4603 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4605 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4606 snap_to (drag_info.current_pointer_frame);
4609 /* only alter selection if the current frame is
4610 different from the last frame position.
4613 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4615 switch (range_marker_op) {
4616 case CreateRangeMarker:
4617 case CreateTransportMarker:
4618 if (drag_info.first_move) {
4619 snap_to (drag_info.grab_frame);
4622 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4623 start = drag_info.current_pointer_frame;
4624 end = drag_info.grab_frame;
4626 end = drag_info.current_pointer_frame;
4627 start = drag_info.grab_frame;
4630 /* first drag: Either add to the selection
4631 or create a new selection.
4634 if (drag_info.first_move) {
4636 temp_location->set (start, end);
4640 update_marker_drag_item (temp_location);
4641 range_marker_drag_rect->show();
4642 range_marker_drag_rect->raise_to_top();
4648 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4649 start_canvas_autoscroll (1);
4653 temp_location->set (start, end);
4655 double x1 = frame_to_pixel (start);
4656 double x2 = frame_to_pixel (end);
4657 crect->property_x1() = x1;
4658 crect->property_x2() = x2;
4660 update_marker_drag_item (temp_location);
4663 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4664 drag_info.first_move = false;
4666 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4671 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4673 Location * newloc = 0;
4676 if (!drag_info.first_move) {
4677 drag_range_markerbar_op (item, event);
4679 switch (range_marker_op) {
4680 case CreateRangeMarker:
4682 begin_reversible_command (_("new range marker"));
4683 XMLNode &before = session->locations()->get_state();
4684 session->locations()->next_available_name(rangename,"unnamed");
4685 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4686 session->locations()->add (newloc, true);
4687 XMLNode &after = session->locations()->get_state();
4688 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4689 commit_reversible_command ();
4691 range_bar_drag_rect->hide();
4692 range_marker_drag_rect->hide();
4696 case CreateTransportMarker:
4697 // popup menu to pick loop or punch
4698 new_transport_marker_context_menu (&event->button, item);
4703 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4705 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4710 start = session->locations()->first_mark_before (drag_info.grab_frame);
4711 end = session->locations()->first_mark_after (drag_info.grab_frame);
4713 if (end == max_frames) {
4714 end = session->current_end_frame ();
4718 start = session->current_start_frame ();
4721 switch (mouse_mode) {
4723 /* find the two markers on either side and then make the selection from it */
4724 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4728 /* find the two markers on either side of the click and make the range out of it */
4729 selection->set (0, start, end);
4738 stop_canvas_autoscroll ();
4744 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4746 drag_info.item = item;
4747 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4748 drag_info.finished_callback = &Editor::end_mouse_zoom;
4750 start_grab (event, zoom_cursor);
4752 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4756 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4761 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4762 snap_to (drag_info.current_pointer_frame);
4764 if (drag_info.first_move) {
4765 snap_to (drag_info.grab_frame);
4769 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4771 /* base start and end on initial click position */
4772 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4773 start = drag_info.current_pointer_frame;
4774 end = drag_info.grab_frame;
4776 end = drag_info.current_pointer_frame;
4777 start = drag_info.grab_frame;
4782 if (drag_info.first_move) {
4784 zoom_rect->raise_to_top();
4787 reposition_zoom_rect(start, end);
4789 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4790 drag_info.first_move = false;
4792 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4797 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4799 if (!drag_info.first_move) {
4800 drag_mouse_zoom (item, event);
4802 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4803 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4805 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4808 temporal_zoom_to_frame (false, drag_info.grab_frame);
4810 temporal_zoom_step (false);
4811 center_screen (drag_info.grab_frame);
4819 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4821 double x1 = frame_to_pixel (start);
4822 double x2 = frame_to_pixel (end);
4823 double y2 = full_canvas_height - 1.0;
4825 zoom_rect->property_x1() = x1;
4826 zoom_rect->property_y1() = 1.0;
4827 zoom_rect->property_x2() = x2;
4828 zoom_rect->property_y2() = y2;
4832 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4834 drag_info.item = item;
4835 drag_info.motion_callback = &Editor::drag_rubberband_select;
4836 drag_info.finished_callback = &Editor::end_rubberband_select;
4838 start_grab (event, cross_hair_cursor);
4840 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4844 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4851 /* use a bigger drag threshold than the default */
4853 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4857 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4858 if (drag_info.first_move) {
4859 snap_to (drag_info.grab_frame);
4861 snap_to (drag_info.current_pointer_frame);
4864 /* base start and end on initial click position */
4866 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4867 start = drag_info.current_pointer_frame;
4868 end = drag_info.grab_frame;
4870 end = drag_info.current_pointer_frame;
4871 start = drag_info.grab_frame;
4874 if (drag_info.current_pointer_y < drag_info.grab_y) {
4875 y1 = drag_info.current_pointer_y;
4876 y2 = drag_info.grab_y;
4878 y2 = drag_info.current_pointer_y;
4879 y1 = drag_info.grab_y;
4883 if (start != end || y1 != y2) {
4885 double x1 = frame_to_pixel (start);
4886 double x2 = frame_to_pixel (end);
4888 rubberband_rect->property_x1() = x1;
4889 rubberband_rect->property_y1() = y1;
4890 rubberband_rect->property_x2() = x2;
4891 rubberband_rect->property_y2() = y2;
4893 rubberband_rect->show();
4894 rubberband_rect->raise_to_top();
4896 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4897 drag_info.first_move = false;
4899 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4904 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4906 if (!drag_info.first_move) {
4908 drag_rubberband_select (item, event);
4911 if (drag_info.current_pointer_y < drag_info.grab_y) {
4912 y1 = drag_info.current_pointer_y;
4913 y2 = drag_info.grab_y;
4916 y2 = drag_info.current_pointer_y;
4917 y1 = drag_info.grab_y;
4921 Selection::Operation op = Keyboard::selection_type (event->button.state);
4924 begin_reversible_command (_("rubberband selection"));
4926 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4927 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4929 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4933 commit_reversible_command ();
4937 selection->clear_tracks();
4938 selection->clear_regions();
4939 selection->clear_points ();
4940 selection->clear_lines ();
4943 rubberband_rect->hide();
4948 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4950 using namespace Gtkmm2ext;
4952 ArdourPrompter prompter (false);
4954 prompter.set_prompt (_("Name for region:"));
4955 prompter.set_initial_text (clicked_regionview->region()->name());
4956 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4957 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4958 prompter.show_all ();
4959 switch (prompter.run ()) {
4960 case Gtk::RESPONSE_ACCEPT:
4962 prompter.get_result(str);
4964 clicked_regionview->region()->set_name (str);
4972 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4974 drag_info.item = item;
4975 drag_info.motion_callback = &Editor::time_fx_motion;
4976 drag_info.finished_callback = &Editor::end_time_fx;
4980 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4984 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4986 RegionView* rv = clicked_regionview;
4988 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4989 snap_to (drag_info.current_pointer_frame);
4992 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4996 if (drag_info.current_pointer_frame > rv->region()->position()) {
4997 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5000 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5001 drag_info.first_move = false;
5003 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5007 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5009 clicked_regionview->get_time_axis_view().hide_timestretch ();
5011 if (drag_info.first_move) {
5015 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5016 /* backwards drag of the left edge - not usable */
5020 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5021 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5023 begin_reversible_command (_("timestretch"));
5025 if (run_timestretch (selection->regions, percentage) == 0) {
5026 session->commit_reversible_command ();
5031 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5033 /* no brushing without a useful snap setting */
5036 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5039 switch (snap_mode) {
5041 return; /* can't work because it allows region to be placed anywhere */
5046 switch (snap_type) {
5049 case SnapToEditCursor:
5056 /* don't brush a copy over the original */
5058 if (pos == rv->region()->position()) {
5062 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5064 if (rtv == 0 || !rtv->is_track()) {
5068 boost::shared_ptr<Playlist> playlist = rtv->playlist();
5069 double speed = rtv->get_diskstream()->speed();
5071 XMLNode &before = playlist->get_state();
5072 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5073 XMLNode &after = playlist->get_state();
5074 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5076 // playlist is frozen, so we have to update manually
5078 playlist->Modified(); /* EMIT SIGNAL */
5082 Editor::track_height_step_timeout ()
5085 struct timeval delta;
5087 gettimeofday (&now, 0);
5088 timersub (&now, &last_track_height_step_timestamp, &delta);
5090 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5091 current_stepping_trackview = 0;
5099 Editor::add_mouse_speed (double speed, double dir)
5103 mouse_direction = dir;
5105 index = mouse_speed_entries;
5107 if (++index >= mouse_speed_size) {
5109 have_full_mouse_speed = true;
5112 mouse_speed[index] = speed;
5113 mouse_speed_entries = index;
5117 Editor::compute_mouse_speed ()
5121 if (!have_full_mouse_speed) {
5123 /* partial speed buffer, just use whatever we have so far */
5125 if (mouse_speed_entries == 0 ) {
5129 for (size_t n = 0; n < mouse_speed_entries; ++n) {
5130 total += mouse_speed[n];
5133 return mouse_direction * total/mouse_speed_entries;
5136 /* compute the average (effectively low-pass filtering) mouse speed
5137 across the entire buffer.
5140 for (size_t n = 0; n < mouse_speed_size; ++n) {
5141 total += mouse_speed[n];
5145 return mouse_direction * total/mouse_speed_size;
5149 Editor::update_mouse_speed ()
5154 session->request_transport_speed (0.0);
5155 mouse_speed_update = -1;
5159 speed = compute_mouse_speed ();
5161 struct timeval tmnow;
5163 gettimeofday (&tmnow, 0);
5164 double now = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec;
5166 if (now - last_scrub_time > 250000) {
5168 // 0.25 seconds since last mouse motion, start to brake
5170 if (fabs (speed) < 0.1) {
5171 /* don't asymptotically approach zero */
5172 memset (mouse_speed, 0, sizeof (double) * mouse_speed_size);
5174 } else if (fabs (speed) < 0.25) {
5175 add_mouse_speed (fabs (speed * 0.2), mouse_direction);
5177 add_mouse_speed (fabs (speed * 0.6), mouse_direction);
5181 session->request_transport_speed (speed);