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;
77 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
80 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
94 switch (event->type) {
95 case GDK_BUTTON_RELEASE:
96 case GDK_BUTTON_PRESS:
97 case GDK_2BUTTON_PRESS:
98 case GDK_3BUTTON_PRESS:
99 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
101 case GDK_MOTION_NOTIFY:
102 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
104 case GDK_ENTER_NOTIFY:
105 case GDK_LEAVE_NOTIFY:
106 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
109 case GDK_KEY_RELEASE:
110 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
113 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
117 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
118 position is negative (as can be the case with motion events in particular),
119 the frame location is always positive.
122 return pixel_to_frame (*pcx);
126 Editor::mouse_mode_toggled (MouseMode m)
128 if (ignore_mouse_mode_toggle) {
134 if (mouse_select_button.get_active()) {
140 if (mouse_move_button.get_active()) {
146 if (mouse_gain_button.get_active()) {
152 if (mouse_zoom_button.get_active()) {
158 if (mouse_timefx_button.get_active()) {
164 if (mouse_audition_button.get_active()) {
170 if (mouse_note_button.get_active()) {
181 Editor::set_mouse_mode (MouseMode m, bool force)
183 if (drag_info.item) {
187 if (!force && m == mouse_mode) {
195 if (mouse_mode != MouseRange) {
197 /* in all modes except range, hide the range selection,
198 show the object (region) selection.
201 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
202 (*i)->set_should_show_selection (true);
204 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
205 (*i)->hide_selection ();
211 in range mode,show the range selection.
214 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
215 if ((*i)->get_selected()) {
216 (*i)->show_selection (selection->time);
221 /* XXX the hack of unsetting all other buttons should go
222 away once GTK2 allows us to use regular radio buttons drawn like
223 normal buttons, rather than my silly GroupedButton hack.
226 ignore_mouse_mode_toggle = true;
228 switch (mouse_mode) {
230 mouse_select_button.set_active (true);
231 current_canvas_cursor = selector_cursor;
235 mouse_move_button.set_active (true);
236 current_canvas_cursor = grabber_cursor;
240 mouse_gain_button.set_active (true);
241 current_canvas_cursor = cross_hair_cursor;
245 mouse_zoom_button.set_active (true);
246 current_canvas_cursor = zoom_cursor;
250 mouse_timefx_button.set_active (true);
251 current_canvas_cursor = time_fx_cursor; // just use playhead
255 mouse_audition_button.set_active (true);
256 current_canvas_cursor = speaker_cursor;
260 mouse_note_button.set_active (true);
261 set_midi_edit_cursor (current_midi_edit_mode());
265 if (mouse_mode == MouseNote)
266 midi_toolbar_frame.show();
268 midi_toolbar_frame.hide();
270 ignore_mouse_mode_toggle = false;
273 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
278 Editor::step_mouse_mode (bool next)
280 switch (current_mouse_mode()) {
282 if (next) set_mouse_mode (MouseRange);
283 else set_mouse_mode (MouseTimeFX);
287 if (next) set_mouse_mode (MouseZoom);
288 else set_mouse_mode (MouseObject);
292 if (next) set_mouse_mode (MouseGain);
293 else set_mouse_mode (MouseRange);
297 if (next) set_mouse_mode (MouseTimeFX);
298 else set_mouse_mode (MouseZoom);
302 if (next) set_mouse_mode (MouseAudition);
303 else set_mouse_mode (MouseGain);
307 if (next) set_mouse_mode (MouseObject);
308 else set_mouse_mode (MouseTimeFX);
312 if (next) set_mouse_mode (MouseObject);
313 else set_mouse_mode (MouseAudition);
319 Editor::midi_edit_mode_toggled (MidiEditMode m)
321 if (ignore_midi_edit_mode_toggle) {
327 if (midi_tool_pencil_button.get_active())
328 set_midi_edit_mode (m);
332 if (midi_tool_select_button.get_active())
333 set_midi_edit_mode (m);
337 if (midi_tool_erase_button.get_active())
338 set_midi_edit_mode (m);
345 set_midi_edit_cursor(m);
350 Editor::set_midi_edit_mode (MidiEditMode m, bool force)
352 if (drag_info.item) {
356 if (!force && m == midi_edit_mode) {
364 ignore_midi_edit_mode_toggle = true;
366 switch (midi_edit_mode) {
368 midi_tool_pencil_button.set_active (true);
372 midi_tool_select_button.set_active (true);
376 midi_tool_erase_button.set_active (true);
380 ignore_midi_edit_mode_toggle = false;
382 set_midi_edit_cursor (current_midi_edit_mode());
385 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
390 Editor::set_midi_edit_cursor (MidiEditMode m)
392 switch (midi_edit_mode) {
394 current_canvas_cursor = midi_pencil_cursor;
398 current_canvas_cursor = midi_select_cursor;
402 current_canvas_cursor = midi_erase_cursor;
408 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
412 /* in object/audition/timefx mode, any button press sets
413 the selection if the object can be selected. this is a
414 bit of hack, because we want to avoid this if the
415 mouse operation is a region alignment.
417 note: not dbl-click or triple-click
420 if (((mouse_mode != MouseObject) &&
421 (mouse_mode != MouseAudition || item_type != RegionItem) &&
422 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
423 (mouse_mode != MouseRange)) ||
425 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
430 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
432 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
434 /* almost no selection action on modified button-2 or button-3 events */
436 if (item_type != RegionItem && event->button.button != 2) {
442 Selection::Operation op = Keyboard::selection_type (event->button.state);
443 bool press = (event->type == GDK_BUTTON_PRESS);
445 // begin_reversible_command (_("select on click"));
449 if (mouse_mode != MouseRange) {
450 commit = set_selected_regionview_from_click (press, op, true);
451 } else if (event->type == GDK_BUTTON_PRESS) {
452 commit = set_selected_track_from_click (press, op, false);
456 case RegionViewNameHighlight:
458 if (mouse_mode != MouseRange) {
459 commit = set_selected_regionview_from_click (press, op, true);
460 } else if (event->type == GDK_BUTTON_PRESS) {
461 commit = set_selected_track_from_click (press, op, false);
466 case FadeInHandleItem:
468 case FadeOutHandleItem:
470 if (mouse_mode != MouseRange) {
471 commit = set_selected_regionview_from_click (press, op, true);
472 } else if (event->type == GDK_BUTTON_PRESS) {
473 commit = set_selected_track_from_click (press, op, false);
477 case CrossfadeViewItem:
478 commit = set_selected_track_from_click (press, op, false);
481 case ControlPointItem:
482 commit = set_selected_track_from_click (press, op, true);
483 if (mouse_mode != MouseRange) {
484 commit |= set_selected_control_point_from_click (op, false);
489 /* for context click or range selection, select track */
490 if (event->button.button == 3) {
491 commit = set_selected_track_from_click (press, op, true);
492 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
493 commit = set_selected_track_from_click (press, op, false);
497 case AutomationTrackItem:
498 commit = set_selected_track_from_click (press, op, true);
506 // commit_reversible_command ();
511 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
513 track_canvas.grab_focus();
515 if (session && session->actively_recording()) {
519 button_selection (item, event, item_type);
521 if (drag_info.item == 0 &&
522 (Keyboard::is_delete_event (&event->button) ||
523 Keyboard::is_context_menu_event (&event->button) ||
524 Keyboard::is_edit_event (&event->button))) {
526 /* handled by button release */
530 switch (event->button.button) {
533 if (event->type == GDK_BUTTON_PRESS) {
535 if (drag_info.item) {
536 drag_info.item->ungrab (event->button.time);
539 /* single mouse clicks on any of these item types operate
540 independent of mouse mode, mostly because they are
541 not on the main track canvas or because we want
547 case PlayheadCursorItem:
548 start_cursor_grab (item, event);
552 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
553 hide_marker (item, event);
555 start_marker_grab (item, event);
559 case TempoMarkerItem:
560 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
561 start_tempo_marker_copy_grab (item, event);
563 start_tempo_marker_grab (item, event);
567 case MeterMarkerItem:
568 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
569 start_meter_marker_copy_grab (item, event);
571 start_meter_marker_grab (item, event);
581 case RangeMarkerBarItem:
582 start_range_markerbar_op (item, event, CreateRangeMarker);
586 case TransportMarkerBarItem:
587 start_range_markerbar_op (item, event, CreateTransportMarker);
596 switch (mouse_mode) {
599 case StartSelectionTrimItem:
600 start_selection_op (item, event, SelectionStartTrim);
603 case EndSelectionTrimItem:
604 start_selection_op (item, event, SelectionEndTrim);
608 if (Keyboard::modifier_state_contains
609 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
610 // contains and not equals because I can't use alt as a modifier alone.
611 start_selection_grab (item, event);
612 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
613 /* grab selection for moving */
614 start_selection_op (item, event, SelectionMove);
617 /* this was debated, but decided the more common action was to
618 make a new selection */
619 start_selection_op (item, event, CreateSelection);
624 start_selection_op (item, event, CreateSelection);
630 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
631 event->type == GDK_BUTTON_PRESS) {
633 start_rubberband_select (item, event);
635 } else if (event->type == GDK_BUTTON_PRESS) {
638 case FadeInHandleItem:
639 start_fade_in_grab (item, event);
642 case FadeOutHandleItem:
643 start_fade_out_grab (item, event);
647 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
648 start_region_copy_grab (item, event);
649 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
650 start_region_brush_grab (item, event);
652 start_region_grab (item, event);
656 case RegionViewNameHighlight:
657 start_trim (item, event);
662 /* rename happens on edit clicks */
663 start_trim (clicked_regionview->get_name_highlight(), event);
667 case ControlPointItem:
668 start_control_point_grab (item, event);
672 case AutomationLineItem:
673 start_line_grab_from_line (item, event);
678 case AutomationTrackItem:
679 start_rubberband_select (item, event);
683 case ImageFrameHandleStartItem:
684 imageframe_start_handle_op(item, event) ;
687 case ImageFrameHandleEndItem:
688 imageframe_end_handle_op(item, event) ;
691 case MarkerViewHandleStartItem:
692 markerview_item_start_handle_op(item, event) ;
695 case MarkerViewHandleEndItem:
696 markerview_item_end_handle_op(item, event) ;
700 start_markerview_grab(item, event) ;
703 start_imageframe_grab(item, event) ;
721 // start_line_grab_from_regionview (item, event);
725 start_line_grab_from_line (item, event);
728 case ControlPointItem:
729 start_control_point_grab (item, event);
740 case ControlPointItem:
741 start_control_point_grab (item, event);
744 case AutomationLineItem:
745 start_line_grab_from_line (item, event);
749 // XXX need automation mode to identify which
751 // start_line_grab_from_regionview (item, event);
761 if (event->type == GDK_BUTTON_PRESS) {
762 start_mouse_zoom (item, event);
769 if (item_type == RegionItem) {
770 start_time_fx (item, event);
776 last_scrub_frame = 0;
778 have_full_mouse_speed = false;
779 memset (mouse_speed, 0, sizeof (double) * mouse_speed_size);
780 /* rest handled in motion & release */
784 start_create_region_grab (item, event);
793 switch (mouse_mode) {
795 if (event->type == GDK_BUTTON_PRESS) {
798 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
799 start_region_copy_grab (item, event);
801 start_region_grab (item, event);
805 case ControlPointItem:
806 start_control_point_grab (item, event);
817 case RegionViewNameHighlight:
818 start_trim (item, event);
823 start_trim (clicked_regionview->get_name_highlight(), event);
834 if (event->type == GDK_BUTTON_PRESS) {
835 /* relax till release */
842 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
843 temporal_zoom_session();
845 temporal_zoom_to_frame (true, event_frame(event));
868 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
870 nframes_t where = event_frame (event, 0, 0);
872 /* no action if we're recording */
874 if (session && session->actively_recording()) {
878 /* first, see if we're finishing a drag ... */
880 if (drag_info.item) {
881 if (end_grab (item, event)) {
882 /* grab dragged, so do nothing else */
887 button_selection (item, event, item_type);
889 /* edit events get handled here */
891 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
897 case TempoMarkerItem:
898 edit_tempo_marker (item);
901 case MeterMarkerItem:
902 edit_meter_marker (item);
906 if (clicked_regionview->name_active()) {
907 return mouse_rename_region (item, event);
917 /* context menu events get handled here */
919 if (Keyboard::is_context_menu_event (&event->button)) {
921 if (drag_info.item == 0) {
923 /* no matter which button pops up the context menu, tell the menu
924 widget to use button 1 to drive menu selection.
929 case FadeInHandleItem:
931 case FadeOutHandleItem:
932 popup_fade_context_menu (1, event->button.time, item, item_type);
936 popup_track_context_menu (1, event->button.time, where);
940 case RegionViewNameHighlight:
942 popup_track_context_menu (1, event->button.time, where);
946 popup_track_context_menu (1, event->button.time, where);
949 case AutomationTrackItem:
950 case CrossfadeViewItem:
951 popup_track_context_menu (1, event->button.time, where);
955 case RangeMarkerBarItem:
956 case TransportMarkerBarItem:
959 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
963 marker_context_menu (&event->button, item);
966 case TempoMarkerItem:
967 tm_marker_context_menu (&event->button, item);
970 case MeterMarkerItem:
971 tm_marker_context_menu (&event->button, item);
976 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
978 case ImageFrameTimeAxisItem:
979 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
982 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
984 case MarkerTimeAxisItem:
985 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
997 /* delete events get handled here */
999 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1001 switch (item_type) {
1002 case TempoMarkerItem:
1003 remove_tempo_marker (item);
1006 case MeterMarkerItem:
1007 remove_meter_marker (item);
1011 remove_marker (*item, event);
1015 if (mouse_mode == MouseObject) {
1016 remove_clicked_region ();
1020 case ControlPointItem:
1021 if (mouse_mode == MouseGain) {
1022 remove_gain_control_point (item, event);
1024 remove_control_point (item, event);
1034 switch (event->button.button) {
1037 switch (item_type) {
1038 /* see comments in button_press_handler */
1039 case EditCursorItem:
1040 case PlayheadCursorItem:
1043 case AutomationLineItem:
1044 case StartSelectionTrimItem:
1045 case EndSelectionTrimItem:
1049 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1050 snap_to (where, 0, true);
1052 mouse_add_new_marker (where);
1056 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1059 mouse_add_new_tempo_event (where);
1063 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1071 switch (mouse_mode) {
1073 switch (item_type) {
1074 case AutomationTrackItem:
1075 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->add_automation_event
1089 // Gain only makes sense for audio regions
1091 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1095 switch (item_type) {
1097 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1101 case AutomationTrackItem:
1102 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1103 add_automation_event (item, event, where, event->button.y);
1113 if (last_scrub_frame == 0) {
1114 /* no drag, just a click */
1115 switch (item_type) {
1117 audition_selected_region ();
1123 /* make sure we stop */
1124 session->request_transport_speed (0.0);
1138 switch (mouse_mode) {
1142 // x_style_paste (where, 1.0);
1162 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1168 switch (item_type) {
1169 case ControlPointItem:
1170 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1171 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1172 cp->set_visible (true);
1176 at_y = cp->get_y ();
1177 cp->item()->i2w (at_x, at_y);
1181 fraction = 1.0 - (cp->get_y() / cp->line().height());
1183 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1184 show_verbose_canvas_cursor ();
1186 if (is_drawable()) {
1187 track_canvas.get_window()->set_cursor (*fader_cursor);
1193 if (mouse_mode == MouseGain) {
1194 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1196 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1197 if (is_drawable()) {
1198 track_canvas.get_window()->set_cursor (*fader_cursor);
1203 case AutomationLineItem:
1204 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1206 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1208 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1210 if (is_drawable()) {
1211 track_canvas.get_window()->set_cursor (*fader_cursor);
1216 case RegionViewNameHighlight:
1217 if (is_drawable() && mouse_mode == MouseObject) {
1218 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1222 case StartSelectionTrimItem:
1223 case EndSelectionTrimItem:
1226 case ImageFrameHandleStartItem:
1227 case ImageFrameHandleEndItem:
1228 case MarkerViewHandleStartItem:
1229 case MarkerViewHandleEndItem:
1232 if (is_drawable()) {
1233 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1237 case EditCursorItem:
1238 case PlayheadCursorItem:
1239 if (is_drawable()) {
1240 track_canvas.get_window()->set_cursor (*grabber_cursor);
1244 case RegionViewName:
1246 /* when the name is not an active item, the entire name highlight is for trimming */
1248 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1249 if (mouse_mode == MouseObject && is_drawable()) {
1250 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1256 case AutomationTrackItem:
1257 if (is_drawable()) {
1258 Gdk::Cursor *cursor;
1259 switch (mouse_mode) {
1261 cursor = selector_cursor;
1264 cursor = zoom_cursor;
1267 cursor = cross_hair_cursor;
1271 track_canvas.get_window()->set_cursor (*cursor);
1273 AutomationTimeAxisView* atv;
1274 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1275 clear_entered_track = false;
1276 set_entered_track (atv);
1282 case RangeMarkerBarItem:
1283 case TransportMarkerBarItem:
1286 if (is_drawable()) {
1287 time_canvas.get_window()->set_cursor (*timebar_cursor);
1292 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1295 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1297 case MeterMarkerItem:
1298 case TempoMarkerItem:
1299 if (is_drawable()) {
1300 time_canvas.get_window()->set_cursor (*timebar_cursor);
1303 case FadeInHandleItem:
1304 case FadeOutHandleItem:
1305 if (mouse_mode == MouseObject) {
1306 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1308 rect->property_fill_color_rgba() = 0;
1309 rect->property_outline_pixels() = 1;
1318 /* second pass to handle entered track status in a comprehensible way.
1321 switch (item_type) {
1323 case AutomationLineItem:
1324 case ControlPointItem:
1325 /* these do not affect the current entered track state */
1326 clear_entered_track = false;
1329 case AutomationTrackItem:
1330 /* handled above already */
1334 set_entered_track (0);
1342 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1351 switch (item_type) {
1352 case ControlPointItem:
1353 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1354 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1355 if (cp->line().npoints() > 1 && !cp->selected()) {
1356 cp->set_visible (false);
1360 if (is_drawable()) {
1361 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1364 hide_verbose_canvas_cursor ();
1367 case RegionViewNameHighlight:
1368 case StartSelectionTrimItem:
1369 case EndSelectionTrimItem:
1370 case EditCursorItem:
1371 case PlayheadCursorItem:
1374 case ImageFrameHandleStartItem:
1375 case ImageFrameHandleEndItem:
1376 case MarkerViewHandleStartItem:
1377 case MarkerViewHandleEndItem:
1380 if (is_drawable()) {
1381 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1386 case AutomationLineItem:
1387 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1389 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1391 line->property_fill_color_rgba() = al->get_line_color();
1393 if (is_drawable()) {
1394 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1398 case RegionViewName:
1399 /* see enter_handler() for notes */
1400 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1401 if (is_drawable() && mouse_mode == MouseObject) {
1402 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1407 case RangeMarkerBarItem:
1408 case TransportMarkerBarItem:
1412 if (is_drawable()) {
1413 time_canvas.get_window()->set_cursor (*timebar_cursor);
1418 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1421 loc = find_location_from_marker (marker, is_start);
1422 if (loc) location_flags_changed (loc, this);
1424 case MeterMarkerItem:
1425 case TempoMarkerItem:
1427 if (is_drawable()) {
1428 time_canvas.get_window()->set_cursor (*timebar_cursor);
1433 case FadeInHandleItem:
1434 case FadeOutHandleItem:
1435 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1437 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1439 rect->property_fill_color_rgba() = rv->get_fill_color();
1440 rect->property_outline_pixels() = 0;
1445 case AutomationTrackItem:
1446 if (is_drawable()) {
1447 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1448 clear_entered_track = true;
1449 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1461 Editor::left_automation_track ()
1463 if (clear_entered_track) {
1464 set_entered_track (0);
1465 clear_entered_track = false;
1471 _update_mouse_speed (void *arg)
1473 return static_cast<Editor*>(arg)->update_mouse_speed ();
1477 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1481 /* We call this so that MOTION_NOTIFY events continue to be
1482 delivered to the canvas. We need to do this because we set
1483 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1484 the density of the events, at the expense of a round-trip
1485 to the server. Given that this will mostly occur on cases
1486 where DISPLAY = :0.0, and given the cost of what the motion
1487 event might do, its a good tradeoff.
1490 track_canvas.get_pointer (x, y);
1492 if (current_stepping_trackview) {
1493 /* don't keep the persistent stepped trackview if the mouse moves */
1494 current_stepping_trackview = 0;
1495 step_timeout.disconnect ();
1498 if (session && session->actively_recording()) {
1499 /* Sorry. no dragging stuff around while we record */
1503 drag_info.item_type = item_type;
1504 drag_info.last_pointer_x = drag_info.current_pointer_x;
1505 drag_info.last_pointer_y = drag_info.current_pointer_y;
1506 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1507 &drag_info.current_pointer_y);
1509 switch (mouse_mode) {
1512 struct timeval tmnow;
1514 if (last_scrub_frame == 0) {
1516 /* first motion, just set up the variables */
1518 last_scrub_frame = (nframes64_t) drag_info.current_pointer_frame;
1519 gettimeofday (&tmnow, 0);
1520 last_scrub_time = tmnow.tv_sec * 1000000.0 + tmnow.tv_usec;
1521 session->request_locate (last_scrub_frame, true);
1524 /* how fast is the mouse moving ? */
1532 if (last_scrub_frame < (nframes64_t) drag_info.current_pointer_frame) {
1533 distance = (nframes64_t) drag_info.current_pointer_frame - last_scrub_frame;
1536 distance = last_scrub_frame - (nframes64_t) drag_info.current_pointer_frame;
1540 if (drag_info.grab_x < drag_info.current_pointer_x) {
1541 distance = drag_info.current_pointer_x - drag_info.grab_x;
1544 distance = drag_info.grab_x - drag_info.current_pointer_x;
1549 gettimeofday (&tmnow, 0);
1550 time = (tmnow.tv_sec * 1000000.0 + tmnow.tv_usec) - last_scrub_time;
1551 last_scrub_frame = drag_info.current_pointer_frame;
1552 last_scrub_time = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec;
1553 speed = ((double)distance/session->frame_rate()) / (time/1000000.0); // frames/sec
1555 add_mouse_speed (speed, dir);
1557 if (mouse_speed_update < 0) {
1558 mouse_speed_update = g_timeout_add (10, _update_mouse_speed, this);
1559 update_mouse_speed ();
1569 if (!from_autoscroll && drag_info.item) {
1570 /* item != 0 is the best test i can think of for dragging.
1572 if (!drag_info.move_threshold_passed) {
1574 bool x_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1575 bool y_threshold_passed = (abs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1577 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1579 // and change the initial grab loc/frame if this drag info wants us to
1581 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1582 drag_info.grab_frame = drag_info.current_pointer_frame;
1583 drag_info.grab_x = drag_info.current_pointer_x;
1584 drag_info.grab_y = drag_info.current_pointer_y;
1585 drag_info.last_pointer_frame = drag_info.grab_frame;
1586 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1591 switch (item_type) {
1592 case PlayheadCursorItem:
1593 case EditCursorItem:
1595 case ControlPointItem:
1596 case TempoMarkerItem:
1597 case MeterMarkerItem:
1598 case RegionViewNameHighlight:
1599 case StartSelectionTrimItem:
1600 case EndSelectionTrimItem:
1603 case AutomationLineItem:
1604 case FadeInHandleItem:
1605 case FadeOutHandleItem:
1608 case ImageFrameHandleStartItem:
1609 case ImageFrameHandleEndItem:
1610 case MarkerViewHandleStartItem:
1611 case MarkerViewHandleEndItem:
1614 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1615 (event->motion.state & Gdk::BUTTON2_MASK))) {
1616 if (!from_autoscroll) {
1617 maybe_autoscroll (event);
1619 (this->*(drag_info.motion_callback)) (item, event);
1628 switch (mouse_mode) {
1634 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1635 (event->motion.state & GDK_BUTTON2_MASK))) {
1636 if (!from_autoscroll) {
1637 maybe_autoscroll (event);
1639 (this->*(drag_info.motion_callback)) (item, event);
1650 track_canvas_motion (event);
1651 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1659 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1661 if (drag_info.item == 0) {
1662 fatal << _("programming error: start_grab called without drag item") << endmsg;
1668 cursor = grabber_cursor;
1671 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1673 if (event->button.button == 2) {
1674 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
1675 drag_info.y_constrained = true;
1676 drag_info.x_constrained = false;
1678 drag_info.y_constrained = false;
1679 drag_info.x_constrained = true;
1682 drag_info.x_constrained = false;
1683 drag_info.y_constrained = false;
1686 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1687 drag_info.last_pointer_frame = drag_info.grab_frame;
1688 drag_info.current_pointer_frame = drag_info.grab_frame;
1689 drag_info.current_pointer_x = drag_info.grab_x;
1690 drag_info.current_pointer_y = drag_info.grab_y;
1691 drag_info.last_pointer_x = drag_info.current_pointer_x;
1692 drag_info.last_pointer_y = drag_info.current_pointer_y;
1693 drag_info.cumulative_x_drag = 0;
1694 drag_info.cumulative_y_drag = 0;
1695 drag_info.first_move = true;
1696 drag_info.move_threshold_passed = false;
1697 drag_info.want_move_threshold = false;
1698 drag_info.pointer_frame_offset = 0;
1699 drag_info.brushing = false;
1700 drag_info.copied_location = 0;
1702 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1704 event->button.time);
1706 if (session && session->transport_rolling()) {
1707 drag_info.was_rolling = true;
1709 drag_info.was_rolling = false;
1712 switch (snap_type) {
1713 case SnapToRegionStart:
1714 case SnapToRegionEnd:
1715 case SnapToRegionSync:
1716 case SnapToRegionBoundary:
1717 build_region_boundary_cache ();
1725 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1727 drag_info.item->ungrab (0);
1728 drag_info.item = new_item;
1731 cursor = grabber_cursor;
1734 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1738 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1740 bool did_drag = false;
1742 stop_canvas_autoscroll ();
1744 if (drag_info.item == 0) {
1748 drag_info.item->ungrab (event->button.time);
1750 if (drag_info.finished_callback) {
1751 drag_info.last_pointer_x = drag_info.current_pointer_x;
1752 drag_info.last_pointer_y = drag_info.current_pointer_y;
1753 (this->*(drag_info.finished_callback)) (item, event);
1756 did_drag = !drag_info.first_move;
1758 hide_verbose_canvas_cursor();
1761 drag_info.copy = false;
1762 drag_info.motion_callback = 0;
1763 drag_info.finished_callback = 0;
1764 drag_info.last_trackview = 0;
1765 drag_info.last_frame_position = 0;
1766 drag_info.grab_frame = 0;
1767 drag_info.last_pointer_frame = 0;
1768 drag_info.current_pointer_frame = 0;
1769 drag_info.brushing = false;
1771 if (drag_info.copied_location) {
1772 delete drag_info.copied_location;
1773 drag_info.copied_location = 0;
1780 Editor::set_edit_cursor (GdkEvent* event)
1782 nframes_t pointer_frame = event_frame (event);
1784 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1785 if (snap_type != SnapToEditCursor) {
1786 snap_to (pointer_frame);
1790 edit_cursor->set_position (pointer_frame);
1791 edit_cursor_clock.set (pointer_frame);
1795 Editor::set_playhead_cursor (GdkEvent* event)
1797 nframes_t pointer_frame = event_frame (event);
1799 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1800 snap_to (pointer_frame);
1804 session->request_locate (pointer_frame, session->transport_rolling());
1809 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1811 drag_info.item = item;
1812 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1813 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1817 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1818 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1822 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1824 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());
1828 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1830 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1832 nframes_t fade_length;
1834 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1835 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1841 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1845 if (pos < (arv->region()->position() + 64)) {
1846 fade_length = 64; // this should be a minimum defined somewhere
1847 } else if (pos > arv->region()->last_frame()) {
1848 fade_length = arv->region()->length();
1850 fade_length = pos - arv->region()->position();
1852 /* mapover the region selection */
1854 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1856 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1862 tmp->reset_fade_in_shape_width (fade_length);
1865 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1867 drag_info.first_move = false;
1871 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1873 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1875 nframes_t fade_length;
1877 if (drag_info.first_move) return;
1879 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1880 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1885 if (pos < (arv->region()->position() + 64)) {
1886 fade_length = 64; // this should be a minimum defined somewhere
1887 } else if (pos > arv->region()->last_frame()) {
1888 fade_length = arv->region()->length();
1890 fade_length = pos - arv->region()->position();
1893 begin_reversible_command (_("change fade in length"));
1895 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1897 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1903 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
1904 XMLNode &before = alist->get_state();
1906 tmp->audio_region()->set_fade_in_length (fade_length);
1908 XMLNode &after = alist->get_state();
1909 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1912 commit_reversible_command ();
1916 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1918 drag_info.item = item;
1919 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1920 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1924 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1925 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1929 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1931 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());
1935 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1937 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1939 nframes_t fade_length;
1941 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1942 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1947 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1951 if (pos > (arv->region()->last_frame() - 64)) {
1952 fade_length = 64; // this should really be a minimum fade defined somewhere
1954 else if (pos < arv->region()->position()) {
1955 fade_length = arv->region()->length();
1958 fade_length = arv->region()->last_frame() - pos;
1961 /* mapover the region selection */
1963 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1965 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1971 tmp->reset_fade_out_shape_width (fade_length);
1974 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1976 drag_info.first_move = false;
1980 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1982 if (drag_info.first_move) return;
1984 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1986 nframes_t fade_length;
1988 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1989 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1995 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1999 if (pos > (arv->region()->last_frame() - 64)) {
2000 fade_length = 64; // this should really be a minimum fade defined somewhere
2002 else if (pos < arv->region()->position()) {
2003 fade_length = arv->region()->length();
2006 fade_length = arv->region()->last_frame() - pos;
2009 begin_reversible_command (_("change fade out length"));
2011 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2013 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2019 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2020 XMLNode &before = alist->get_state();
2022 tmp->audio_region()->set_fade_out_length (fade_length);
2024 XMLNode &after = alist->get_state();
2025 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2028 commit_reversible_command ();
2032 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2034 drag_info.item = item;
2035 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2036 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2040 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2041 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2045 Cursor* cursor = (Cursor *) drag_info.data;
2047 if (cursor == playhead_cursor) {
2048 _dragging_playhead = true;
2050 if (session && drag_info.was_rolling) {
2051 session->request_stop ();
2054 if (session && session->is_auditioning()) {
2055 session->cancel_audition ();
2059 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2061 show_verbose_time_cursor (cursor->current_frame, 10);
2065 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2067 Cursor* cursor = (Cursor *) drag_info.data;
2068 nframes_t adjusted_frame;
2070 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2071 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2077 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2078 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
2079 snap_to (adjusted_frame);
2083 if (adjusted_frame == drag_info.last_pointer_frame) return;
2085 cursor->set_position (adjusted_frame);
2087 if (cursor == edit_cursor) {
2088 edit_cursor_clock.set (cursor->current_frame);
2090 UpdateAllTransportClocks (cursor->current_frame);
2093 show_verbose_time_cursor (cursor->current_frame, 10);
2095 drag_info.last_pointer_frame = adjusted_frame;
2096 drag_info.first_move = false;
2100 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2102 if (drag_info.first_move) return;
2104 cursor_drag_motion_callback (item, event);
2106 _dragging_playhead = false;
2108 if (item == &playhead_cursor->canvas_item) {
2110 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2112 } else if (item == &edit_cursor->canvas_item) {
2113 edit_cursor->set_position (edit_cursor->current_frame);
2114 edit_cursor_clock.set (edit_cursor->current_frame);
2119 Editor::update_marker_drag_item (Location *location)
2121 double x1 = frame_to_pixel (location->start());
2122 double x2 = frame_to_pixel (location->end());
2124 if (location->is_mark()) {
2125 marker_drag_line_points.front().set_x(x1);
2126 marker_drag_line_points.back().set_x(x1);
2127 marker_drag_line->property_points() = marker_drag_line_points;
2130 range_marker_drag_rect->property_x1() = x1;
2131 range_marker_drag_rect->property_x2() = x2;
2136 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2140 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2141 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2147 Location *location = find_location_from_marker (marker, is_start);
2149 drag_info.item = item;
2150 drag_info.data = marker;
2151 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2152 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2156 drag_info.copied_location = new Location (*location);
2157 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2159 update_marker_drag_item (location);
2161 if (location->is_mark()) {
2162 marker_drag_line->show();
2163 marker_drag_line->raise_to_top();
2165 range_marker_drag_rect->show();
2166 range_marker_drag_rect->raise_to_top();
2170 show_verbose_time_cursor (location->start(), 10);
2172 show_verbose_time_cursor (location->end(), 10);
2177 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2180 Marker* marker = (Marker *) drag_info.data;
2181 Location *real_location;
2182 Location *copy_location;
2184 bool move_both = false;
2188 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2189 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2194 nframes_t next = newframe;
2196 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2197 snap_to (newframe, 0, true);
2200 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2204 /* call this to find out if its the start or end */
2206 real_location = find_location_from_marker (marker, is_start);
2208 /* use the copy that we're "dragging" around */
2210 copy_location = drag_info.copied_location;
2212 f_delta = copy_location->end() - copy_location->start();
2214 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2218 if (copy_location->is_mark()) {
2221 copy_location->set_start (newframe);
2225 if (is_start) { // start-of-range marker
2228 copy_location->set_start (newframe);
2229 copy_location->set_end (newframe + f_delta);
2230 } else if (newframe < copy_location->end()) {
2231 copy_location->set_start (newframe);
2233 snap_to (next, 1, true);
2234 copy_location->set_end (next);
2235 copy_location->set_start (newframe);
2238 } else { // end marker
2241 copy_location->set_end (newframe);
2242 copy_location->set_start (newframe - f_delta);
2243 } else if (newframe > copy_location->start()) {
2244 copy_location->set_end (newframe);
2246 } else if (newframe > 0) {
2247 snap_to (next, -1, true);
2248 copy_location->set_start (next);
2249 copy_location->set_end (newframe);
2254 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2255 drag_info.first_move = false;
2257 update_marker_drag_item (copy_location);
2259 LocationMarkers* lm = find_location_markers (real_location);
2260 lm->set_position (copy_location->start(), copy_location->end());
2262 show_verbose_time_cursor (newframe, 10);
2266 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2268 if (drag_info.first_move) {
2269 marker_drag_motion_callback (item, event);
2273 Marker* marker = (Marker *) drag_info.data;
2277 begin_reversible_command ( _("move marker") );
2278 XMLNode &before = session->locations()->get_state();
2280 Location * location = find_location_from_marker (marker, is_start);
2283 if (location->is_mark()) {
2284 location->set_start (drag_info.copied_location->start());
2286 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2290 XMLNode &after = session->locations()->get_state();
2291 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2292 commit_reversible_command ();
2294 marker_drag_line->hide();
2295 range_marker_drag_rect->hide();
2299 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2302 MeterMarker* meter_marker;
2304 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2305 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2309 meter_marker = dynamic_cast<MeterMarker*> (marker);
2311 MetricSection& section (meter_marker->meter());
2313 if (!section.movable()) {
2317 drag_info.item = item;
2318 drag_info.data = marker;
2319 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2320 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2324 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2326 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2330 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2333 MeterMarker* meter_marker;
2335 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2336 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2340 meter_marker = dynamic_cast<MeterMarker*> (marker);
2342 // create a dummy marker for visual representation of moving the copy.
2343 // The actual copying is not done before we reach the finish callback.
2345 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2346 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2347 *new MeterSection(meter_marker->meter()));
2349 drag_info.item = &new_marker->the_item();
2350 drag_info.copy = true;
2351 drag_info.data = new_marker;
2352 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2353 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2357 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2359 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2363 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2365 MeterMarker* marker = (MeterMarker *) drag_info.data;
2366 nframes_t adjusted_frame;
2368 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2369 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2375 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2376 snap_to (adjusted_frame);
2379 if (adjusted_frame == drag_info.last_pointer_frame) return;
2381 marker->set_position (adjusted_frame);
2384 drag_info.last_pointer_frame = adjusted_frame;
2385 drag_info.first_move = false;
2387 show_verbose_time_cursor (adjusted_frame, 10);
2391 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2393 if (drag_info.first_move) return;
2395 meter_marker_drag_motion_callback (drag_info.item, event);
2397 MeterMarker* marker = (MeterMarker *) drag_info.data;
2400 TempoMap& map (session->tempo_map());
2401 map.bbt_time (drag_info.last_pointer_frame, when);
2403 if (drag_info.copy == true) {
2404 begin_reversible_command (_("copy meter mark"));
2405 XMLNode &before = map.get_state();
2406 map.add_meter (marker->meter(), when);
2407 XMLNode &after = map.get_state();
2408 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2409 commit_reversible_command ();
2411 // delete the dummy marker we used for visual representation of copying.
2412 // a new visual marker will show up automatically.
2415 begin_reversible_command (_("move meter mark"));
2416 XMLNode &before = map.get_state();
2417 map.move_meter (marker->meter(), when);
2418 XMLNode &after = map.get_state();
2419 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2420 commit_reversible_command ();
2425 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2428 TempoMarker* tempo_marker;
2430 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2431 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2435 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2436 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2440 MetricSection& section (tempo_marker->tempo());
2442 if (!section.movable()) {
2446 drag_info.item = item;
2447 drag_info.data = marker;
2448 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2449 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2453 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2454 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2458 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2461 TempoMarker* tempo_marker;
2463 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2464 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2468 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2469 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2473 // create a dummy marker for visual representation of moving the copy.
2474 // The actual copying is not done before we reach the finish callback.
2476 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2477 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2478 *new TempoSection(tempo_marker->tempo()));
2480 drag_info.item = &new_marker->the_item();
2481 drag_info.copy = true;
2482 drag_info.data = new_marker;
2483 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2484 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2488 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2490 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2494 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2496 TempoMarker* marker = (TempoMarker *) drag_info.data;
2497 nframes_t adjusted_frame;
2499 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2500 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2506 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2507 snap_to (adjusted_frame);
2510 if (adjusted_frame == drag_info.last_pointer_frame) return;
2512 /* OK, we've moved far enough to make it worth actually move the thing. */
2514 marker->set_position (adjusted_frame);
2516 show_verbose_time_cursor (adjusted_frame, 10);
2518 drag_info.last_pointer_frame = adjusted_frame;
2519 drag_info.first_move = false;
2523 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2525 if (drag_info.first_move) return;
2527 tempo_marker_drag_motion_callback (drag_info.item, event);
2529 TempoMarker* marker = (TempoMarker *) drag_info.data;
2532 TempoMap& map (session->tempo_map());
2533 map.bbt_time (drag_info.last_pointer_frame, when);
2535 if (drag_info.copy == true) {
2536 begin_reversible_command (_("copy tempo mark"));
2537 XMLNode &before = map.get_state();
2538 map.add_tempo (marker->tempo(), when);
2539 XMLNode &after = map.get_state();
2540 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2541 commit_reversible_command ();
2543 // delete the dummy marker we used for visual representation of copying.
2544 // a new visual marker will show up automatically.
2547 begin_reversible_command (_("move tempo mark"));
2548 XMLNode &before = map.get_state();
2549 map.move_tempo (marker->tempo(), when);
2550 XMLNode &after = map.get_state();
2551 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2552 commit_reversible_command ();
2557 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2559 ControlPoint* control_point;
2561 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2562 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2566 // We shouldn't remove the first or last gain point
2567 if (control_point->line().is_last_point(*control_point) ||
2568 control_point->line().is_first_point(*control_point)) {
2572 control_point->line().remove_point (*control_point);
2576 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2578 ControlPoint* control_point;
2580 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2581 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2585 control_point->line().remove_point (*control_point);
2589 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2591 ControlPoint* control_point;
2593 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2594 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2598 drag_info.item = item;
2599 drag_info.data = control_point;
2600 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2601 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2603 start_grab (event, fader_cursor);
2605 // start the grab at the center of the control point so
2606 // the point doesn't 'jump' to the mouse after the first drag
2607 drag_info.grab_x = control_point->get_x();
2608 drag_info.grab_y = control_point->get_y();
2609 control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2610 track_canvas.w2c(drag_info.grab_x, drag_info.grab_y,
2611 drag_info.grab_x, drag_info.grab_y);
2613 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2615 control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2617 double fraction = 1.0 - ((control_point->get_y() - control_point->line().y_position()) / (double)control_point->line().height());
2618 set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction),
2619 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2621 show_verbose_canvas_cursor ();
2625 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2627 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2629 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2630 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2632 if (event->button.state & Keyboard::Alt) {
2637 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2638 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2640 // calculate zero crossing point. back off by .01 to stay on the
2641 // positive side of zero
2643 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line().height() - .01;
2644 cp->line().parent_group().i2w(_unused, zero_gain_y);
2646 // make sure we hit zero when passing through
2647 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2648 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2652 if (drag_info.x_constrained) {
2653 cx = drag_info.grab_x;
2655 if (drag_info.y_constrained) {
2656 cy = drag_info.grab_y;
2659 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2660 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2662 cp->line().parent_group().w2i (cx, cy);
2666 cy = min ((double) (cp->line().y_position() + cp->line().height()), cy);
2668 //translate cx to frames
2669 nframes_t cx_frames = unit_to_frame (cx);
2671 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2672 snap_to (cx_frames);
2675 const double fraction = 1.0 - ((cy - cp->line().y_position()) / (double)cp->line().height());
2679 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2685 cp->line().point_drag (*cp, cx_frames , fraction, push);
2687 set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
2689 drag_info.first_move = false;
2693 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2695 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2697 if (drag_info.first_move) {
2701 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2702 reset_point_selection ();
2706 control_point_drag_motion_callback (item, event);
2708 cp->line().end_drag (cp);
2712 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2714 switch (mouse_mode) {
2716 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2717 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2725 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2729 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2730 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2734 start_line_grab (al, event);
2738 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2742 nframes_t frame_within_region;
2744 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2748 cx = event->button.x;
2749 cy = event->button.y;
2750 line->parent_group().w2i (cx, cy);
2751 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2753 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2754 current_line_drag_info.after)) {
2755 /* no adjacent points */
2759 drag_info.item = &line->grab_item();
2760 drag_info.data = line;
2761 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2762 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2764 start_grab (event, fader_cursor);
2766 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2768 line->start_drag (0, drag_info.grab_frame, fraction);
2770 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2771 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2772 show_verbose_canvas_cursor ();
2776 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2778 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2780 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2782 if (event->button.state & Keyboard::Alt) {
2786 double cx = drag_info.current_pointer_x;
2787 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2789 // calculate zero crossing point. back off by .01 to stay on the
2790 // positive side of zero
2792 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
2793 line->parent_group().i2w(_unused, zero_gain_y);
2795 // make sure we hit zero when passing through
2796 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2797 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2801 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2803 line->parent_group().w2i (cx, cy);
2806 cy = min ((double) line->height(), cy);
2808 const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
2812 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2818 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2820 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2824 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2826 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2827 line_drag_motion_callback (item, event);
2832 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2834 if (selection->regions.empty() || clicked_regionview == 0) {
2838 drag_info.copy = false;
2839 drag_info.item = item;
2840 drag_info.data = clicked_regionview;
2841 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2842 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2847 TimeAxisView* tvp = clicked_axisview;
2848 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2850 if (tv && tv->is_track()) {
2851 speed = tv->get_diskstream()->speed();
2854 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2855 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2856 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2857 // we want a move threshold
2858 drag_info.want_move_threshold = true;
2860 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2862 begin_reversible_command (_("move region(s)"));
2866 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2868 drag_info.copy = false;
2869 drag_info.item = item;
2870 drag_info.data = clicked_axisview;
2871 drag_info.last_trackview = clicked_axisview;
2872 drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
2873 drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
2879 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2881 if (selection->regions.empty() || clicked_regionview == 0) {
2885 drag_info.copy = true;
2886 drag_info.item = item;
2887 drag_info.data = clicked_regionview;
2891 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2892 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2895 if (rtv && rtv->is_track()) {
2896 speed = rtv->get_diskstream()->speed();
2899 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2900 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2901 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2902 // we want a move threshold
2903 drag_info.want_move_threshold = true;
2904 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2905 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2906 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2910 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2912 if (selection->regions.empty() || clicked_regionview == 0) {
2916 drag_info.copy = false;
2917 drag_info.item = item;
2918 drag_info.data = clicked_regionview;
2919 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2920 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2925 TimeAxisView* tvp = clicked_axisview;
2926 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2928 if (tv && tv->is_track()) {
2929 speed = tv->get_diskstream()->speed();
2932 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2933 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2934 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2935 // we want a move threshold
2936 drag_info.want_move_threshold = true;
2937 drag_info.brushing = true;
2939 begin_reversible_command (_("Drag region brush"));
2943 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2947 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
2948 nframes_t pending_region_position = 0;
2949 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2950 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2951 bool clamp_y_axis = false;
2952 vector<int32_t> height_list(512) ;
2953 vector<int32_t>::iterator j;
2955 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2957 drag_info.want_move_threshold = false; // don't copy again
2959 /* duplicate the region(s) */
2961 vector<RegionView*> new_regionviews;
2963 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2970 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2971 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2974 nrv = new AudioRegionView (*arv);
2976 nrv = new MidiRegionView (*mrv);
2981 nrv->get_canvas_group()->show ();
2983 new_regionviews.push_back (nrv);
2986 if (new_regionviews.empty()) {
2990 /* reset selection to new regionviews */
2992 selection->set (new_regionviews);
2994 /* reset drag_info data to reflect the fact that we are dragging the copies */
2996 drag_info.data = new_regionviews.front();
2998 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3001 /* Which trackview is this ? */
3003 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3004 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3006 /* The region motion is only processed if the pointer is over
3010 if (!tv || !tv->is_track()) {
3011 /* To make sure we hide the verbose canvas cursor when the mouse is
3012 not held over a track.
3014 hide_verbose_canvas_cursor ();
3018 original_pointer_order = drag_info.last_trackview->order;
3020 /************************************************************
3022 ************************************************************/
3024 if (drag_info.brushing) {
3025 clamp_y_axis = true;
3030 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
3032 int32_t children = 0, numtracks = 0;
3033 // XXX hard coding track limit, oh my, so very very bad
3034 bitset <1024> tracks (0x00);
3035 /* get a bitmask representing the visible tracks */
3037 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3038 TimeAxisView *tracklist_timeview;
3039 tracklist_timeview = (*i);
3040 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
3041 TimeAxisView::Children children_list;
3043 /* zeroes are audio tracks. ones are other types. */
3045 if (!rtv2->hidden()) {
3047 if (visible_y_high < rtv2->order) {
3048 visible_y_high = rtv2->order;
3050 if (visible_y_low > rtv2->order) {
3051 visible_y_low = rtv2->order;
3054 if (!rtv2->is_track()) {
3055 tracks = tracks |= (0x01 << rtv2->order);
3058 height_list[rtv2->order] = (*i)->height;
3060 if ((children_list = rtv2->get_child_list()).size() > 0) {
3061 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3062 tracks = tracks |= (0x01 << (rtv2->order + children));
3063 height_list[rtv2->order + children] = (*j)->height;
3071 /* find the actual span according to the canvas */
3073 canvas_pointer_y_span = pointer_y_span;
3074 if (drag_info.last_trackview->order >= tv->order) {
3076 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3077 if (height_list[y] == 0 ) {
3078 canvas_pointer_y_span--;
3083 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3084 if ( height_list[y] == 0 ) {
3085 canvas_pointer_y_span++;
3090 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3091 RegionView* rv2 = (*i);
3092 double ix1, ix2, iy1, iy2;
3095 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3096 rv2->get_canvas_group()->i2w (ix1, iy1);
3097 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3098 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3100 if (rtv2->order != original_pointer_order) {
3101 /* this isn't the pointer track */
3103 if (canvas_pointer_y_span > 0) {
3105 /* moving up the canvas */
3106 if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3108 int32_t visible_tracks = 0;
3109 while (visible_tracks < canvas_pointer_y_span ) {
3112 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3113 /* we're passing through a hidden track */
3118 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3119 clamp_y_axis = true;
3123 clamp_y_axis = true;
3126 } else if (canvas_pointer_y_span < 0) {
3128 /*moving down the canvas*/
3130 if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3133 int32_t visible_tracks = 0;
3135 while (visible_tracks > canvas_pointer_y_span ) {
3138 while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3142 if ( tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3143 clamp_y_axis = true;
3148 clamp_y_axis = true;
3154 /* this is the pointer's track */
3155 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3156 clamp_y_axis = true;
3157 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3158 clamp_y_axis = true;
3166 } else if (drag_info.last_trackview == tv) {
3167 clamp_y_axis = true;
3171 if (!clamp_y_axis) {
3172 drag_info.last_trackview = tv;
3175 /************************************************************
3177 ************************************************************/
3179 /* compute the amount of pointer motion in frames, and where
3180 the region would be if we moved it by that much.
3183 if (drag_info.move_threshold_passed) {
3185 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3187 nframes_t sync_frame;
3188 nframes_t sync_offset;
3191 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3193 sync_offset = rv->region()->sync_offset (sync_dir);
3194 sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3196 /* we snap if the snap modifier is not enabled.
3199 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3200 snap_to (sync_frame);
3203 if (sync_frame - sync_offset <= sync_frame) {
3204 pending_region_position = sync_frame - (sync_dir*sync_offset);
3206 pending_region_position = 0;
3210 pending_region_position = 0;
3213 if (pending_region_position > max_frames - rv->region()->length()) {
3214 pending_region_position = drag_info.last_frame_position;
3217 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3219 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3221 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3222 to make it appear at the new location.
3225 if (pending_region_position > drag_info.last_frame_position) {
3226 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3228 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3231 drag_info.last_frame_position = pending_region_position;
3238 /* threshold not passed */
3243 /*************************************************************
3245 ************************************************************/
3247 if (x_delta == 0 && (pointer_y_span == 0)) {
3248 /* haven't reached next snap point, and we're not switching
3249 trackviews. nothing to do.
3256 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3258 RegionView* rv2 = (*i);
3260 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3262 double ix1, ix2, iy1, iy2;
3263 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3264 rv2->get_canvas_group()->i2w (ix1, iy1);
3273 /*************************************************************
3275 ************************************************************/
3279 if (drag_info.first_move) {
3280 if (drag_info.move_threshold_passed) {
3291 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3292 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3294 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3296 RegionView* rv = (*i);
3297 double ix1, ix2, iy1, iy2;
3298 int32_t temp_pointer_y_span = pointer_y_span;
3300 /* get item BBox, which will be relative to parent. so we have
3301 to query on a child, then convert to world coordinates using
3305 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3306 rv->get_canvas_group()->i2w (ix1, iy1);
3307 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3308 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3309 RouteTimeAxisView* temp_rtv;
3311 if ((pointer_y_span != 0) && !clamp_y_axis) {
3314 for (j = height_list.begin(); j!= height_list.end(); j++) {
3315 if (x == canvas_rtv->order) {
3316 /* we found the track the region is on */
3317 if (x != original_pointer_order) {
3318 /*this isn't from the same track we're dragging from */
3319 temp_pointer_y_span = canvas_pointer_y_span;
3321 while (temp_pointer_y_span > 0) {
3322 /* we're moving up canvas-wise,
3323 so we need to find the next track height
3325 if (j != height_list.begin()) {
3328 if (x != original_pointer_order) {
3329 /* we're not from the dragged track, so ignore hidden tracks. */
3331 temp_pointer_y_span++;
3335 temp_pointer_y_span--;
3337 while (temp_pointer_y_span < 0) {
3339 if (x != original_pointer_order) {
3341 temp_pointer_y_span--;
3345 if (j != height_list.end()) {
3348 temp_pointer_y_span++;
3350 /* find out where we'll be when we move and set height accordingly */
3352 tvp2 = trackview_by_y_position (iy1 + y_delta);
3353 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3354 rv->set_y_position_and_height (0, temp_rtv->height);
3356 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3357 personally, i think this can confuse things, but never mind.
3360 //const GdkColor& col (temp_rtv->view->get_region_color());
3361 //rv->set_color (const_cast<GdkColor&>(col));
3368 /* prevent the regionview from being moved to before
3369 the zero position on the canvas.
3374 if (-x_delta > ix1) {
3377 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3378 x_delta = max_frames - rv->region()->last_frame();
3382 if (drag_info.first_move) {
3384 /* hide any dependent views */
3386 rv->get_time_axis_view().hide_dependent_views (*rv);
3388 /* this is subtle. raising the regionview itself won't help,
3389 because raise_to_top() just puts the item on the top of
3390 its parent's stack. so, we need to put the trackview canvas_display group
3391 on the top, since its parent is the whole canvas.
3394 rv->get_canvas_group()->raise_to_top();
3395 rv->get_time_axis_view().canvas_display->raise_to_top();
3396 cursor_group->raise_to_top();
3398 rv->fake_set_opaque (true);
3401 if (drag_info.brushing) {
3402 mouse_brush_insert_region (rv, pending_region_position);
3404 rv->move (x_delta, y_delta);
3407 } /* foreach region */
3411 if (drag_info.first_move && drag_info.move_threshold_passed) {
3412 cursor_group->raise_to_top();
3413 drag_info.first_move = false;
3416 if (x_delta != 0 && !drag_info.brushing) {
3417 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3422 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3425 RegionView* rvdi = reinterpret_cast<RegionView *> (drag_info.data);
3426 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3427 bool nocommit = true;
3429 RouteTimeAxisView* rtv;
3430 bool regionview_y_movement;
3431 bool regionview_x_movement;
3432 vector<RegionView*> copies;
3433 list <boost::shared_ptr<Playlist > > used_playlists;
3434 list <sigc::connection > used_connections;
3435 bool preserve_selection = false;
3437 /* first_move is set to false if the regionview has been moved in the
3441 if (drag_info.first_move) {
3448 /* The regionview has been moved at some stage during the grab so we need
3449 to account for any mouse movement between this event and the last one.
3452 region_drag_motion_callback (item, event);
3454 if (drag_info.brushing) {
3455 /* all changes were made during motion event handlers */
3457 if (drag_info.copy) {
3458 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3459 copies.push_back (*i);
3466 /* adjust for track speed */
3469 rtv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3470 if (rtv && rtv->get_diskstream()) {
3471 speed = rtv->get_diskstream()->speed();
3474 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rvdi->region()->position()/speed));
3475 regionview_y_movement = (drag_info.last_trackview != &rvdi->get_time_axis_view());
3477 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3478 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3482 if (drag_info.copy) {
3483 if (drag_info.x_constrained) {
3484 op_string = _("fixed time region copy");
3486 op_string = _("region copy");
3489 if (drag_info.x_constrained) {
3490 op_string = _("fixed time region drag");
3492 op_string = _("region drag");
3496 begin_reversible_command (op_string);
3498 if (regionview_y_movement) {
3500 /* moved to a different audio track. */
3502 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3504 RegionView* rv = (*i);
3506 double ix1, ix2, iy1, iy2;
3508 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3509 rv->get_canvas_group()->i2w (ix1, iy1);
3511 RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(trackview_by_y_position (iy1));
3513 boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
3515 if (! to_playlist->frozen()) {
3517 we haven't seen this playlist before.
3518 we want to freeze it because we don't want to relayer per-region.
3519 its much better to do that just once if the playlist is large.
3523 connect so the selection is changed when the new regionview finally appears (after thaw).
3524 keep track of it so we can disconnect later.
3527 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3528 used_connections.push_back (c);
3531 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3533 /* remember used playlists so we can thaw them later */
3534 used_playlists.push_back(to_playlist);
3535 to_playlist->freeze();
3538 where = (nframes_t) (unit_to_frame (ix1) * speed);
3539 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3541 if (!drag_info.copy) {
3544 /* the region that used to be in the old playlist is not
3545 moved to the new one - we make a copy of it. as a result,
3546 any existing editor for the region should no longer be
3550 RouteTimeAxisView* from_playlist_rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_trackview());
3551 boost::shared_ptr<Playlist> from_playlist = from_playlist_rtv->playlist();
3553 if (! from_playlist->frozen()) {
3554 from_playlist->freeze();
3555 used_playlists.push_back(from_playlist);
3557 sigc::connection c = rtv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3558 used_connections.push_back (c);
3560 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3563 rv->hide_region_editor();
3564 rv->fake_set_opaque (false);
3566 from_playlist->remove_region ((rv->region()));
3570 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3572 copies.push_back (rv);
3575 latest_regionview = 0;
3577 to_playlist->add_region (new_region, where);
3579 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3580 was selected in all of them, then removing it from the playlist will have removed all
3581 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3582 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3583 corresponding regionview, and the selection is now empty).
3585 this could have invalidated any and all iterators into the region selection.
3587 the heuristic we use here is: if the region selection is empty, break out of the loop
3588 here. if the region selection is not empty, then restart the loop because we know that
3589 we must have removed at least the region(view) we've just been working on as well as any
3590 that we processed on previous iterations.
3592 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3593 we can just iterate.
3597 if (drag_info.copy) {
3600 if (selection->regions.empty()) {
3604 XXX see above .. but we just froze the playlists.. we have to keep iterating, right?
3607 //i = selection->regions.by_layer().begin();
3615 /* motion within a single track */
3617 list<RegionView*> regions = selection->regions.by_layer();
3619 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3621 RegionView* rv = (*i);
3622 boost::shared_ptr<Playlist> to_playlist = (*i)->region()->playlist();
3623 RouteTimeAxisView* from_rtv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3625 if (!rv->region()->can_move()) {
3629 if (regionview_x_movement) {
3630 double ownspeed = 1.0;
3632 if (from_rtv && from_rtv->get_diskstream()) {
3633 ownspeed = from_rtv->get_diskstream()->speed();
3636 /* base the new region position on the current position of the regionview.*/
3638 double ix1, ix2, iy1, iy2;
3640 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3641 rv->get_canvas_group()->i2w (ix1, iy1);
3642 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3646 where = rv->region()->position();
3649 if (! to_playlist->frozen()) {
3650 sigc::connection c = from_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_and_select_new_region_view));
3651 used_connections.push_back (c);
3654 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3656 used_playlists.push_back(to_playlist);
3657 to_playlist->freeze();
3660 if (drag_info.copy) {
3662 boost::shared_ptr<Region> newregion;
3663 boost::shared_ptr<Region> ar;
3664 boost::shared_ptr<Region> mr;
3666 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3667 newregion = RegionFactory::create (ar);
3668 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3669 newregion = RegionFactory::create (mr);
3674 to_playlist->add_region (newregion, (nframes_t) (where * from_rtv->get_diskstream()->speed()));
3676 /* if the original region was locked, we don't care for the new one */
3678 newregion->set_locked (false);
3679 copies.push_back (rv);
3683 /* just change the model */
3685 rv->region()->set_position (where, (void*) this);
3686 preserve_selection = true;
3693 if (! preserve_selection) {
3694 //selection->clear_regions();
3696 while (used_playlists.size() > 0) {
3698 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
3701 if (used_connections.size()) {
3702 sigc::connection c = used_connections.front();
3704 used_connections.pop_front();
3708 session->add_command (new MementoCommand<Playlist>(*(*i), 0, &(*i)->get_state()));
3709 used_playlists.pop_front();
3715 commit_reversible_command ();
3718 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3725 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3727 if (drag_info.move_threshold_passed) {
3728 if (drag_info.first_move) {
3729 // TODO: create region-create-drag region view here
3730 drag_info.first_move = false;
3733 // TODO: resize region-create-drag region view here
3738 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3740 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.last_trackview);
3744 const boost::shared_ptr<MidiDiskstream> diskstream =
3745 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
3748 warning << "Cannot create non-MIDI region" << endl;
3752 if (drag_info.first_move) {
3753 begin_reversible_command (_("create region"));
3754 XMLNode &before = mtv->playlist()->get_state();
3756 nframes_t start = drag_info.grab_frame;
3757 snap_to (start, -1);
3758 const Meter& m = session->tempo_map().meter_at(start);
3759 const Tempo& t = session->tempo_map().tempo_at(start);
3760 double length = m.frames_per_bar(t, session->frame_rate());
3762 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
3764 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>(RegionFactory::create(
3765 src, 0, length, PBD::basename_nosuffix(src->name()))), start);
3766 XMLNode &after = mtv->playlist()->get_state();
3767 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
3768 commit_reversible_command();
3771 create_region_drag_motion_callback (item, event);
3772 // TODO: create region-create-drag region here
3777 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3779 /* Either add to or set the set the region selection, unless
3780 this is an alignment click (control used)
3783 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3784 TimeAxisView* tv = &rv.get_time_axis_view();
3785 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3787 if (rtv && rtv->is_track()) {
3788 speed = rtv->get_diskstream()->speed();
3791 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3793 align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3795 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3797 align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3801 align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3807 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3813 nframes_t frame_rate;
3820 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3821 case AudioClock::BBT:
3822 session->bbt_time (frame, bbt);
3823 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3826 case AudioClock::SMPTE:
3827 session->smpte_time (frame, smpte);
3828 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3831 case AudioClock::MinSec:
3832 /* XXX this is copied from show_verbose_duration_cursor() */
3833 frame_rate = session->frame_rate();
3834 hours = frame / (frame_rate * 3600);
3835 frame = frame % (frame_rate * 3600);
3836 mins = frame / (frame_rate * 60);
3837 frame = frame % (frame_rate * 60);
3838 secs = (float) frame / (float) frame_rate;
3839 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3843 snprintf (buf, sizeof(buf), "%u", frame);
3847 if (xpos >= 0 && ypos >=0) {
3848 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3851 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3853 show_verbose_canvas_cursor ();
3857 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3864 nframes_t distance, frame_rate;
3866 Meter meter_at_start(session->tempo_map().meter_at(start));
3872 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3873 case AudioClock::BBT:
3874 session->bbt_time (start, sbbt);
3875 session->bbt_time (end, ebbt);
3878 /* XXX this computation won't work well if the
3879 user makes a selection that spans any meter changes.
3882 ebbt.bars -= sbbt.bars;
3883 if (ebbt.beats >= sbbt.beats) {
3884 ebbt.beats -= sbbt.beats;
3887 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3889 if (ebbt.ticks >= sbbt.ticks) {
3890 ebbt.ticks -= sbbt.ticks;
3893 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3896 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3899 case AudioClock::SMPTE:
3900 session->smpte_duration (end - start, smpte);
3901 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3904 case AudioClock::MinSec:
3905 /* XXX this stuff should be elsewhere.. */
3906 distance = end - start;
3907 frame_rate = session->frame_rate();
3908 hours = distance / (frame_rate * 3600);
3909 distance = distance % (frame_rate * 3600);
3910 mins = distance / (frame_rate * 60);
3911 distance = distance % (frame_rate * 60);
3912 secs = (float) distance / (float) frame_rate;
3913 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3917 snprintf (buf, sizeof(buf), "%u", end - start);
3921 if (xpos >= 0 && ypos >=0) {
3922 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3925 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3927 show_verbose_canvas_cursor ();
3931 Editor::collect_new_region_view (RegionView* rv)
3933 latest_regionview = rv;
3937 Editor::collect_and_select_new_region_view (RegionView* rv)
3940 latest_regionview = rv;
3944 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3946 if (clicked_regionview == 0) {
3950 /* lets try to create new Region for the selection */
3952 vector<boost::shared_ptr<AudioRegion> > new_regions;
3953 create_region_from_selection (new_regions);
3955 if (new_regions.empty()) {
3959 /* XXX fix me one day to use all new regions */
3961 boost::shared_ptr<Region> region (new_regions.front());
3963 /* add it to the current stream/playlist.
3965 tricky: the streamview for the track will add a new regionview. we will
3966 catch the signal it sends when it creates the regionview to
3967 set the regionview we want to then drag.
3970 latest_regionview = 0;
3971 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3973 /* A selection grab currently creates two undo/redo operations, one for
3974 creating the new region and another for moving it.
3977 begin_reversible_command (_("selection grab"));
3979 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3981 XMLNode *before = &(playlist->get_state());
3982 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3983 XMLNode *after = &(playlist->get_state());
3984 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3986 commit_reversible_command ();
3990 if (latest_regionview == 0) {
3991 /* something went wrong */
3995 /* we need to deselect all other regionviews, and select this one
3996 i'm ignoring undo stuff, because the region creation will take care of it */
3997 //selection->set (latest_regionview);
3999 drag_info.item = latest_regionview->get_canvas_group();
4000 drag_info.data = latest_regionview;
4001 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4002 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4006 drag_info.last_trackview = clicked_axisview;
4007 drag_info.last_frame_position = latest_regionview->region()->position();
4008 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4010 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4014 Editor::cancel_selection ()
4016 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4017 (*i)->hide_selection ();
4019 begin_reversible_command (_("cancel selection"));
4020 selection->clear ();
4021 clicked_selection = 0;
4022 commit_reversible_command ();
4026 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4028 nframes_t start = 0;
4035 drag_info.item = item;
4036 drag_info.motion_callback = &Editor::drag_selection;
4037 drag_info.finished_callback = &Editor::end_selection_op;
4042 case CreateSelection:
4043 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4044 drag_info.copy = true;
4046 drag_info.copy = false;
4048 start_grab (event, selector_cursor);
4051 case SelectionStartTrim:
4052 if (clicked_axisview) {
4053 clicked_axisview->order_selection_trims (item, true);
4055 start_grab (event, trimmer_cursor);
4056 start = selection->time[clicked_selection].start;
4057 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4060 case SelectionEndTrim:
4061 if (clicked_axisview) {
4062 clicked_axisview->order_selection_trims (item, false);
4064 start_grab (event, trimmer_cursor);
4065 end = selection->time[clicked_selection].end;
4066 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4070 start = selection->time[clicked_selection].start;
4072 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4076 if (selection_op == SelectionMove) {
4077 show_verbose_time_cursor(start, 10);
4079 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4084 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4086 nframes_t start = 0;
4089 nframes_t pending_position;
4091 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4092 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4094 pending_position = 0;
4097 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4098 snap_to (pending_position);
4101 /* only alter selection if the current frame is
4102 different from the last frame position (adjusted)
4105 if (pending_position == drag_info.last_pointer_frame) return;
4107 switch (selection_op) {
4108 case CreateSelection:
4110 if (drag_info.first_move) {
4111 snap_to (drag_info.grab_frame);
4114 if (pending_position < drag_info.grab_frame) {
4115 start = pending_position;
4116 end = drag_info.grab_frame;
4118 end = pending_position;
4119 start = drag_info.grab_frame;
4122 /* first drag: Either add to the selection
4123 or create a new selection->
4126 if (drag_info.first_move) {
4128 begin_reversible_command (_("range selection"));
4130 if (drag_info.copy) {
4131 /* adding to the selection */
4132 clicked_selection = selection->add (start, end);
4133 drag_info.copy = false;
4135 /* new selection-> */
4136 clicked_selection = selection->set (clicked_axisview, start, end);
4141 case SelectionStartTrim:
4143 if (drag_info.first_move) {
4144 begin_reversible_command (_("trim selection start"));
4147 start = selection->time[clicked_selection].start;
4148 end = selection->time[clicked_selection].end;
4150 if (pending_position > end) {
4153 start = pending_position;
4157 case SelectionEndTrim:
4159 if (drag_info.first_move) {
4160 begin_reversible_command (_("trim selection end"));
4163 start = selection->time[clicked_selection].start;
4164 end = selection->time[clicked_selection].end;
4166 if (pending_position < start) {
4169 end = pending_position;
4176 if (drag_info.first_move) {
4177 begin_reversible_command (_("move selection"));
4180 start = selection->time[clicked_selection].start;
4181 end = selection->time[clicked_selection].end;
4183 length = end - start;
4185 start = pending_position;
4188 end = start + length;
4193 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4194 start_canvas_autoscroll (1);
4198 selection->replace (clicked_selection, start, end);
4201 drag_info.last_pointer_frame = pending_position;
4202 drag_info.first_move = false;
4204 if (selection_op == SelectionMove) {
4205 show_verbose_time_cursor(start, 10);
4207 show_verbose_time_cursor(pending_position, 10);
4212 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4214 if (!drag_info.first_move) {
4215 drag_selection (item, event);
4216 /* XXX this is not object-oriented programming at all. ick */
4217 if (selection->time.consolidate()) {
4218 selection->TimeChanged ();
4220 commit_reversible_command ();
4222 /* just a click, no pointer movement.*/
4224 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4226 selection->clear_time();
4231 /* XXX what happens if its a music selection? */
4232 session->set_audio_range (selection->time);
4233 stop_canvas_autoscroll ();
4237 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4240 TimeAxisView* tvp = clicked_axisview;
4241 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4243 if (tv && tv->is_track()) {
4244 speed = tv->get_diskstream()->speed();
4247 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4248 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4249 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4251 //drag_info.item = clicked_regionview->get_name_highlight();
4252 drag_info.item = item;
4253 drag_info.motion_callback = &Editor::trim_motion_callback;
4254 drag_info.finished_callback = &Editor::trim_finished_callback;
4256 start_grab (event, trimmer_cursor);
4258 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4259 trim_op = ContentsTrim;
4261 /* These will get overridden for a point trim.*/
4262 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4263 /* closer to start */
4264 trim_op = StartTrim;
4265 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4273 show_verbose_time_cursor(region_start, 10);
4276 show_verbose_time_cursor(region_end, 10);
4279 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4285 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4287 RegionView* rv = clicked_regionview;
4288 nframes_t frame_delta = 0;
4289 bool left_direction;
4290 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4292 /* snap modifier works differently here..
4293 its' current state has to be passed to the
4294 various trim functions in order to work properly
4298 TimeAxisView* tvp = clicked_axisview;
4299 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4300 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4302 if (tv && tv->is_track()) {
4303 speed = tv->get_diskstream()->speed();
4306 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4307 left_direction = true;
4309 left_direction = false;
4313 snap_to (drag_info.current_pointer_frame);
4316 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4320 if (drag_info.first_move) {
4326 trim_type = "Region start trim";
4329 trim_type = "Region end trim";
4332 trim_type = "Region content trim";
4336 begin_reversible_command (trim_type);
4338 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4339 (*i)->fake_set_opaque(false);
4340 (*i)->region()->freeze ();
4342 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4344 arv->temporarily_hide_envelope ();
4346 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4347 insert_result = motion_frozen_playlists.insert (pl);
4348 if (insert_result.second) {
4349 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4355 if (left_direction) {
4356 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4358 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4363 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4366 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4367 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4373 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4376 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4377 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4384 bool swap_direction = false;
4386 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4387 swap_direction = true;
4390 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4391 i != selection->regions.by_layer().end(); ++i)
4393 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4401 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4404 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4407 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4411 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4412 drag_info.first_move = false;
4416 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4418 boost::shared_ptr<Region> region (rv.region());
4420 if (region->locked()) {
4424 nframes_t new_bound;
4427 TimeAxisView* tvp = clicked_axisview;
4428 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4430 if (tv && tv->is_track()) {
4431 speed = tv->get_diskstream()->speed();
4434 if (left_direction) {
4435 if (swap_direction) {
4436 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4438 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4441 if (swap_direction) {
4442 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4444 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4449 snap_to (new_bound);
4451 region->trim_start ((nframes_t) (new_bound * speed), this);
4452 rv.region_changed (StartChanged);
4456 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4458 boost::shared_ptr<Region> region (rv.region());
4460 if (region->locked()) {
4464 nframes_t new_bound;
4467 TimeAxisView* tvp = clicked_axisview;
4468 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4470 if (tv && tv->is_track()) {
4471 speed = tv->get_diskstream()->speed();
4474 if (left_direction) {
4475 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4477 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4481 snap_to (new_bound, (left_direction ? 0 : 1));
4484 region->trim_front ((nframes_t) (new_bound * speed), this);
4486 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4490 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4492 boost::shared_ptr<Region> region (rv.region());
4494 if (region->locked()) {
4498 nframes_t new_bound;
4501 TimeAxisView* tvp = clicked_axisview;
4502 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4504 if (tv && tv->is_track()) {
4505 speed = tv->get_diskstream()->speed();
4508 if (left_direction) {
4509 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4511 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4515 snap_to (new_bound);
4517 region->trim_end ((nframes_t) (new_bound * speed), this);
4518 rv.region_changed (LengthChanged);
4522 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4524 if (!drag_info.first_move) {
4525 trim_motion_callback (item, event);
4527 if (!clicked_regionview->get_selected()) {
4528 thaw_region_after_trim (*clicked_regionview);
4531 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4532 i != selection->regions.by_layer().end(); ++i)
4534 thaw_region_after_trim (**i);
4535 (*i)->fake_set_opaque (true);
4539 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4541 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4544 motion_frozen_playlists.clear ();
4546 commit_reversible_command();
4548 /* no mouse movement */
4554 Editor::point_trim (GdkEvent* event)
4556 RegionView* rv = clicked_regionview;
4557 nframes_t new_bound = drag_info.current_pointer_frame;
4559 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4560 snap_to (new_bound);
4563 /* Choose action dependant on which button was pressed */
4564 switch (event->button.button) {
4566 trim_op = StartTrim;
4567 begin_reversible_command (_("Start point trim"));
4569 if (rv->get_selected()) {
4571 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4572 i != selection->regions.by_layer().end(); ++i)
4574 if (!(*i)->region()->locked()) {
4575 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4576 XMLNode &before = pl->get_state();
4577 (*i)->region()->trim_front (new_bound, this);
4578 XMLNode &after = pl->get_state();
4579 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4585 if (!rv->region()->locked()) {
4586 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4587 XMLNode &before = pl->get_state();
4588 rv->region()->trim_front (new_bound, this);
4589 XMLNode &after = pl->get_state();
4590 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4594 commit_reversible_command();
4599 begin_reversible_command (_("End point trim"));
4601 if (rv->get_selected()) {
4603 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4605 if (!(*i)->region()->locked()) {
4606 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4607 XMLNode &before = pl->get_state();
4608 (*i)->region()->trim_end (new_bound, this);
4609 XMLNode &after = pl->get_state();
4610 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4616 if (!rv->region()->locked()) {
4617 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4618 XMLNode &before = pl->get_state();
4619 rv->region()->trim_end (new_bound, this);
4620 XMLNode &after = pl->get_state();
4621 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4625 commit_reversible_command();
4634 Editor::thaw_region_after_trim (RegionView& rv)
4636 boost::shared_ptr<Region> region (rv.region());
4638 if (region->locked()) {
4642 region->thaw (_("trimmed region"));
4643 XMLNode &after = region->playlist()->get_state();
4644 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4646 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4648 arv->unhide_envelope ();
4652 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4657 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4658 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4662 Location* location = find_location_from_marker (marker, is_start);
4663 location->set_hidden (true, this);
4668 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4674 drag_info.item = item;
4675 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4676 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4678 range_marker_op = op;
4680 if (!temp_location) {
4681 temp_location = new Location;
4685 case CreateRangeMarker:
4686 case CreateTransportMarker:
4688 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4689 drag_info.copy = true;
4691 drag_info.copy = false;
4693 start_grab (event, selector_cursor);
4697 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4702 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4704 nframes_t start = 0;
4706 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4708 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4709 snap_to (drag_info.current_pointer_frame);
4712 /* only alter selection if the current frame is
4713 different from the last frame position.
4716 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4718 switch (range_marker_op) {
4719 case CreateRangeMarker:
4720 case CreateTransportMarker:
4721 if (drag_info.first_move) {
4722 snap_to (drag_info.grab_frame);
4725 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4726 start = drag_info.current_pointer_frame;
4727 end = drag_info.grab_frame;
4729 end = drag_info.current_pointer_frame;
4730 start = drag_info.grab_frame;
4733 /* first drag: Either add to the selection
4734 or create a new selection.
4737 if (drag_info.first_move) {
4739 temp_location->set (start, end);
4743 update_marker_drag_item (temp_location);
4744 range_marker_drag_rect->show();
4745 range_marker_drag_rect->raise_to_top();
4751 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4752 start_canvas_autoscroll (1);
4756 temp_location->set (start, end);
4758 double x1 = frame_to_pixel (start);
4759 double x2 = frame_to_pixel (end);
4760 crect->property_x1() = x1;
4761 crect->property_x2() = x2;
4763 update_marker_drag_item (temp_location);
4766 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4767 drag_info.first_move = false;
4769 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4774 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4776 Location * newloc = 0;
4779 if (!drag_info.first_move) {
4780 drag_range_markerbar_op (item, event);
4782 switch (range_marker_op) {
4783 case CreateRangeMarker:
4785 begin_reversible_command (_("new range marker"));
4786 XMLNode &before = session->locations()->get_state();
4787 session->locations()->next_available_name(rangename,"unnamed");
4788 newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4789 session->locations()->add (newloc, true);
4790 XMLNode &after = session->locations()->get_state();
4791 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4792 commit_reversible_command ();
4794 range_bar_drag_rect->hide();
4795 range_marker_drag_rect->hide();
4799 case CreateTransportMarker:
4800 // popup menu to pick loop or punch
4801 new_transport_marker_context_menu (&event->button, item);
4806 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4808 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4813 start = session->locations()->first_mark_before (drag_info.grab_frame);
4814 end = session->locations()->first_mark_after (drag_info.grab_frame);
4816 if (end == max_frames) {
4817 end = session->current_end_frame ();
4821 start = session->current_start_frame ();
4824 switch (mouse_mode) {
4826 /* find the two markers on either side and then make the selection from it */
4827 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4831 /* find the two markers on either side of the click and make the range out of it */
4832 selection->set (0, start, end);
4841 stop_canvas_autoscroll ();
4847 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4849 drag_info.item = item;
4850 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4851 drag_info.finished_callback = &Editor::end_mouse_zoom;
4853 start_grab (event, zoom_cursor);
4855 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4859 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4864 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4865 snap_to (drag_info.current_pointer_frame);
4867 if (drag_info.first_move) {
4868 snap_to (drag_info.grab_frame);
4872 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4874 /* base start and end on initial click position */
4875 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4876 start = drag_info.current_pointer_frame;
4877 end = drag_info.grab_frame;
4879 end = drag_info.current_pointer_frame;
4880 start = drag_info.grab_frame;
4885 if (drag_info.first_move) {
4887 zoom_rect->raise_to_top();
4890 reposition_zoom_rect(start, end);
4892 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4893 drag_info.first_move = false;
4895 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4900 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4902 if (!drag_info.first_move) {
4903 drag_mouse_zoom (item, event);
4905 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4906 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4908 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4911 temporal_zoom_to_frame (false, drag_info.grab_frame);
4913 temporal_zoom_step (false);
4914 center_screen (drag_info.grab_frame);
4922 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4924 double x1 = frame_to_pixel (start);
4925 double x2 = frame_to_pixel (end);
4926 double y2 = full_canvas_height - 1.0;
4928 zoom_rect->property_x1() = x1;
4929 zoom_rect->property_y1() = 1.0;
4930 zoom_rect->property_x2() = x2;
4931 zoom_rect->property_y2() = y2;
4935 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4937 drag_info.item = item;
4938 drag_info.motion_callback = &Editor::drag_rubberband_select;
4939 drag_info.finished_callback = &Editor::end_rubberband_select;
4941 start_grab (event, cross_hair_cursor);
4943 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4947 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4954 /* use a bigger drag threshold than the default */
4956 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4960 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4961 if (drag_info.first_move) {
4962 snap_to (drag_info.grab_frame);
4964 snap_to (drag_info.current_pointer_frame);
4967 /* base start and end on initial click position */
4969 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4970 start = drag_info.current_pointer_frame;
4971 end = drag_info.grab_frame;
4973 end = drag_info.current_pointer_frame;
4974 start = drag_info.grab_frame;
4977 if (drag_info.current_pointer_y < drag_info.grab_y) {
4978 y1 = drag_info.current_pointer_y;
4979 y2 = drag_info.grab_y;
4981 y2 = drag_info.current_pointer_y;
4982 y1 = drag_info.grab_y;
4986 if (start != end || y1 != y2) {
4988 double x1 = frame_to_pixel (start);
4989 double x2 = frame_to_pixel (end);
4991 rubberband_rect->property_x1() = x1;
4992 rubberband_rect->property_y1() = y1;
4993 rubberband_rect->property_x2() = x2;
4994 rubberband_rect->property_y2() = y2;
4996 rubberband_rect->show();
4997 rubberband_rect->raise_to_top();
4999 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5000 drag_info.first_move = false;
5002 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5007 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5009 if (!drag_info.first_move) {
5011 drag_rubberband_select (item, event);
5014 if (drag_info.current_pointer_y < drag_info.grab_y) {
5015 y1 = drag_info.current_pointer_y;
5016 y2 = drag_info.grab_y;
5019 y2 = drag_info.current_pointer_y;
5020 y1 = drag_info.grab_y;
5024 Selection::Operation op = Keyboard::selection_type (event->button.state);
5027 begin_reversible_command (_("rubberband selection"));
5029 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5030 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5032 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5036 commit_reversible_command ();
5040 selection->clear_tracks();
5041 selection->clear_regions();
5042 selection->clear_points ();
5043 selection->clear_lines ();
5046 rubberband_rect->hide();
5051 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5053 using namespace Gtkmm2ext;
5055 ArdourPrompter prompter (false);
5057 prompter.set_prompt (_("Name for region:"));
5058 prompter.set_initial_text (clicked_regionview->region()->name());
5059 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5060 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5061 prompter.show_all ();
5062 switch (prompter.run ()) {
5063 case Gtk::RESPONSE_ACCEPT:
5065 prompter.get_result(str);
5067 clicked_regionview->region()->set_name (str);
5075 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5077 drag_info.item = item;
5078 drag_info.motion_callback = &Editor::time_fx_motion;
5079 drag_info.finished_callback = &Editor::end_time_fx;
5083 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5087 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5089 RegionView* rv = clicked_regionview;
5091 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5092 snap_to (drag_info.current_pointer_frame);
5095 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5099 if (drag_info.current_pointer_frame > rv->region()->position()) {
5100 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5103 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5104 drag_info.first_move = false;
5106 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5110 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5112 clicked_regionview->get_time_axis_view().hide_timestretch ();
5114 if (drag_info.first_move) {
5118 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5119 /* backwards drag of the left edge - not usable */
5123 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5124 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5126 begin_reversible_command (_("timestretch"));
5128 if (run_timestretch (selection->regions, percentage) == 0) {
5129 session->commit_reversible_command ();
5134 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5136 /* no brushing without a useful snap setting */
5139 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5142 switch (snap_mode) {
5144 return; /* can't work because it allows region to be placed anywhere */
5149 switch (snap_type) {
5152 case SnapToEditCursor:
5159 /* don't brush a copy over the original */
5161 if (pos == rv->region()->position()) {
5165 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5167 if (rtv == 0 || !rtv->is_track()) {
5171 boost::shared_ptr<Playlist> playlist = rtv->playlist();
5172 double speed = rtv->get_diskstream()->speed();
5174 XMLNode &before = playlist->get_state();
5175 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5176 XMLNode &after = playlist->get_state();
5177 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5179 // playlist is frozen, so we have to update manually
5181 playlist->Modified(); /* EMIT SIGNAL */
5185 Editor::track_height_step_timeout ()
5188 struct timeval delta;
5190 gettimeofday (&now, 0);
5191 timersub (&now, &last_track_height_step_timestamp, &delta);
5193 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5194 current_stepping_trackview = 0;
5202 Editor::add_mouse_speed (double speed, double dir)
5206 mouse_direction = dir;
5208 index = mouse_speed_entries;
5210 if (++index >= mouse_speed_size) {
5212 have_full_mouse_speed = true;
5215 mouse_speed[index] = speed;
5216 mouse_speed_entries = index;
5220 Editor::compute_mouse_speed ()
5224 if (!have_full_mouse_speed) {
5226 /* partial speed buffer, just use whatever we have so far */
5228 if (mouse_speed_entries == 0 ) {
5232 for (size_t n = 0; n < mouse_speed_entries; ++n) {
5233 total += mouse_speed[n];
5236 return mouse_direction * total/mouse_speed_entries;
5239 /* compute the average (effectively low-pass filtering) mouse speed
5240 across the entire buffer.
5243 for (size_t n = 0; n < mouse_speed_size; ++n) {
5244 total += mouse_speed[n];
5248 return mouse_direction * total/mouse_speed_size;
5252 Editor::update_mouse_speed ()
5257 session->request_transport_speed (0.0);
5258 mouse_speed_update = -1;
5262 speed = compute_mouse_speed ();
5264 struct timeval tmnow;
5266 gettimeofday (&tmnow, 0);
5267 double now = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec;
5269 if (now - last_scrub_time > 250000) {
5271 // 0.25 seconds since last mouse motion, start to brake
5273 if (fabs (speed) < 0.1) {
5274 /* don't asymptotically approach zero */
5275 memset (mouse_speed, 0, sizeof (double) * mouse_speed_size);
5277 } else if (fabs (speed) < 0.25) {
5278 add_mouse_speed (fabs (speed * 0.2), mouse_direction);
5280 add_mouse_speed (fabs (speed * 0.6), mouse_direction);
5284 session->request_transport_speed (speed);