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.
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
31 #include <pbd/memento_command.h>
33 #include "ardour_ui.h"
35 #include "time_axis_view.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
39 #include "streamview.h"
40 #include "region_gain_line.h"
41 #include "automation_time_axis.h"
44 #include "selection.h"
47 #include "rgb_macros.h"
49 #include <ardour/types.h>
50 #include <ardour/profile.h>
51 #include <ardour/route.h>
52 #include <ardour/audio_track.h>
53 #include <ardour/audio_diskstream.h>
54 #include <ardour/playlist.h>
55 #include <ardour/audioplaylist.h>
56 #include <ardour/audioregion.h>
57 #include <ardour/dB.h>
58 #include <ardour/utils.h>
59 #include <ardour/region_factory.h>
66 using namespace ARDOUR;
70 using namespace Editing;
73 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
77 Gdk::ModifierType mask;
78 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas.get_window();
79 Glib::RefPtr<const Gdk::Window> pointer_window;
81 pointer_window = canvas_window->get_pointer (x, y, mask);
83 if (pointer_window == track_canvas.get_bin_window()) {
85 track_canvas.window_to_world (x, y, wx, wy);
86 in_track_canvas = true;
89 in_track_canvas = false;
91 if (pointer_window == time_canvas.get_bin_window()) {
92 time_canvas.window_to_world (x, y, wx, wy);
98 wx += horizontal_adjustment.get_value();
99 wy += vertical_adjustment.get_value();
102 event.type = GDK_BUTTON_RELEASE;
106 where = event_frame (&event, 0, 0);
111 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
125 switch (event->type) {
126 case GDK_BUTTON_RELEASE:
127 case GDK_BUTTON_PRESS:
128 case GDK_2BUTTON_PRESS:
129 case GDK_3BUTTON_PRESS:
130 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
132 case GDK_MOTION_NOTIFY:
133 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
135 case GDK_ENTER_NOTIFY:
136 case GDK_LEAVE_NOTIFY:
137 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
140 case GDK_KEY_RELEASE:
141 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
144 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
148 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
149 position is negative (as can be the case with motion events in particular),
150 the frame location is always positive.
153 return pixel_to_frame (*pcx);
157 Editor::mouse_mode_toggled (MouseMode m)
159 if (ignore_mouse_mode_toggle) {
165 if (mouse_select_button.get_active()) {
171 if (mouse_move_button.get_active()) {
177 if (mouse_gain_button.get_active()) {
183 if (mouse_zoom_button.get_active()) {
189 if (mouse_timefx_button.get_active()) {
195 if (mouse_audition_button.get_active()) {
206 Editor::which_grabber_cursor ()
208 switch (_edit_point) {
210 return grabber_edit_point_cursor;
213 return grabber_cursor;
219 Editor::set_canvas_cursor ()
221 switch (mouse_mode) {
223 current_canvas_cursor = selector_cursor;
227 current_canvas_cursor = which_grabber_cursor();
231 current_canvas_cursor = cross_hair_cursor;
235 current_canvas_cursor = zoom_cursor;
239 current_canvas_cursor = time_fx_cursor; // just use playhead
243 current_canvas_cursor = speaker_cursor;
248 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
253 Editor::set_mouse_mode (MouseMode m, bool force)
255 if (drag_info.item) {
259 if (!force && m == mouse_mode) {
267 if (mouse_mode != MouseRange) {
269 /* in all modes except range, hide the range selection,
270 show the object (region) selection.
273 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
274 (*i)->set_should_show_selection (true);
276 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
277 (*i)->hide_selection ();
283 in range mode,show the range selection.
286 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
287 if ((*i)->get_selected()) {
288 (*i)->show_selection (selection->time);
293 /* XXX the hack of unsetting all other buttons should go
294 away once GTK2 allows us to use regular radio buttons drawn like
295 normal buttons, rather than my silly GroupedButton hack.
298 ignore_mouse_mode_toggle = true;
300 switch (mouse_mode) {
302 mouse_select_button.set_active (true);
306 mouse_move_button.set_active (true);
310 mouse_gain_button.set_active (true);
314 mouse_zoom_button.set_active (true);
318 mouse_timefx_button.set_active (true);
322 mouse_audition_button.set_active (true);
326 ignore_mouse_mode_toggle = false;
328 set_canvas_cursor ();
332 Editor::step_mouse_mode (bool next)
334 switch (current_mouse_mode()) {
336 if (next) set_mouse_mode (MouseRange);
337 else set_mouse_mode (MouseTimeFX);
341 if (next) set_mouse_mode (MouseZoom);
342 else set_mouse_mode (MouseObject);
346 if (next) set_mouse_mode (MouseGain);
347 else set_mouse_mode (MouseRange);
351 if (next) set_mouse_mode (MouseTimeFX);
352 else set_mouse_mode (MouseZoom);
356 if (next) set_mouse_mode (MouseAudition);
357 else set_mouse_mode (MouseGain);
361 if (next) set_mouse_mode (MouseObject);
362 else set_mouse_mode (MouseTimeFX);
368 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
370 /* in object/audition/timefx mode, any button press sets
371 the selection if the object can be selected. this is a
372 bit of hack, because we want to avoid this if the
373 mouse operation is a region alignment.
375 note: not dbl-click or triple-click
378 if (((mouse_mode != MouseObject) &&
379 (mouse_mode != MouseAudition || item_type != RegionItem) &&
380 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
381 (mouse_mode != MouseRange)) ||
383 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
388 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
390 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
392 /* almost no selection action on modified button-2 or button-3 events */
394 if (item_type != RegionItem && event->button.button != 2) {
400 Selection::Operation op = Keyboard::selection_type (event->button.state);
401 bool press = (event->type == GDK_BUTTON_PRESS);
403 // begin_reversible_command (_("select on click"));
407 if (mouse_mode != MouseRange) {
408 set_selected_regionview_from_click (press, op, true);
409 } else if (event->type == GDK_BUTTON_PRESS) {
410 set_selected_track_as_side_effect ();
414 case RegionViewNameHighlight:
416 if (mouse_mode != MouseRange) {
417 set_selected_regionview_from_click (press, op, true);
418 } else if (event->type == GDK_BUTTON_PRESS) {
419 set_selected_track_as_side_effect ();
423 case FadeInHandleItem:
425 case FadeOutHandleItem:
427 if (mouse_mode != MouseRange) {
428 set_selected_regionview_from_click (press, op, true);
429 } else if (event->type == GDK_BUTTON_PRESS) {
430 set_selected_track_as_side_effect ();
434 case GainAutomationControlPointItem:
435 case PanAutomationControlPointItem:
436 case RedirectAutomationControlPointItem:
437 set_selected_track_as_side_effect ();
438 if (mouse_mode != MouseRange) {
439 set_selected_control_point_from_click (op, false);
444 /* for context click or range selection, select track */
445 if (event->button.button == 3) {
446 set_selected_track_as_side_effect ();
447 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
448 set_selected_track_as_side_effect ();
452 case AutomationTrackItem:
453 set_selected_track_as_side_effect (true);
461 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
464 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
466 track_canvas.grab_focus();
468 if (session && session->actively_recording()) {
472 button_selection (item, event, item_type);
474 if (drag_info.item == 0 &&
475 (Keyboard::is_delete_event (&event->button) ||
476 Keyboard::is_context_menu_event (&event->button) ||
477 Keyboard::is_edit_event (&event->button))) {
479 /* handled by button release */
483 switch (event->button.button) {
486 if (event->type == GDK_BUTTON_PRESS) {
488 if (drag_info.item) {
489 drag_info.item->ungrab (event->button.time);
492 /* single mouse clicks on any of these item types operate
493 independent of mouse mode, mostly because they are
494 not on the main track canvas or because we want
499 case PlayheadCursorItem:
500 start_cursor_grab (item, event);
504 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
505 hide_marker (item, event);
507 start_marker_grab (item, event);
511 case TempoMarkerItem:
512 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
513 start_tempo_marker_copy_grab (item, event);
515 start_tempo_marker_grab (item, event);
519 case MeterMarkerItem:
520 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
521 start_meter_marker_copy_grab (item, event);
523 start_meter_marker_grab (item, event);
533 case RangeMarkerBarItem:
534 start_range_markerbar_op (item, event, CreateRangeMarker);
538 case CdMarkerBarItem:
539 start_range_markerbar_op (item, event, CreateCDMarker);
543 case TransportMarkerBarItem:
544 start_range_markerbar_op (item, event, CreateTransportMarker);
553 switch (mouse_mode) {
556 case StartSelectionTrimItem:
557 start_selection_op (item, event, SelectionStartTrim);
560 case EndSelectionTrimItem:
561 start_selection_op (item, event, SelectionEndTrim);
565 if (Keyboard::modifier_state_contains
566 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
567 // contains and not equals because I can't use alt as a modifier alone.
568 start_selection_grab (item, event);
569 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
570 /* grab selection for moving */
571 start_selection_op (item, event, SelectionMove);
573 /* this was debated, but decided the more common action was to
574 make a new selection */
575 start_selection_op (item, event, CreateSelection);
580 start_selection_op (item, event, CreateSelection);
586 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
587 event->type == GDK_BUTTON_PRESS) {
589 start_rubberband_select (item, event);
591 } else if (event->type == GDK_BUTTON_PRESS) {
594 case FadeInHandleItem:
595 start_fade_in_grab (item, event);
598 case FadeOutHandleItem:
599 start_fade_out_grab (item, event);
603 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
604 start_region_copy_grab (item, event);
605 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
606 start_region_brush_grab (item, event);
608 start_region_grab (item, event);
612 case RegionViewNameHighlight:
613 start_trim (item, event);
618 /* rename happens on edit clicks */
619 start_trim (clicked_regionview->get_name_highlight(), event);
623 case GainAutomationControlPointItem:
624 case PanAutomationControlPointItem:
625 case RedirectAutomationControlPointItem:
626 start_control_point_grab (item, event);
630 case GainAutomationLineItem:
631 case PanAutomationLineItem:
632 case RedirectAutomationLineItem:
633 start_line_grab_from_line (item, event);
638 case AutomationTrackItem:
639 start_rubberband_select (item, event);
642 /* <CMT Additions> */
643 case ImageFrameHandleStartItem:
644 imageframe_start_handle_op(item, event) ;
647 case ImageFrameHandleEndItem:
648 imageframe_end_handle_op(item, event) ;
651 case MarkerViewHandleStartItem:
652 markerview_item_start_handle_op(item, event) ;
655 case MarkerViewHandleEndItem:
656 markerview_item_end_handle_op(item, event) ;
659 /* </CMT Additions> */
661 /* <CMT Additions> */
663 start_markerview_grab(item, event) ;
666 start_imageframe_grab(item, event) ;
668 /* </CMT Additions> */
684 // start_line_grab_from_regionview (item, event);
687 case GainControlPointItem:
688 start_control_point_grab (item, event);
692 start_line_grab_from_line (item, event);
695 case GainAutomationControlPointItem:
696 case PanAutomationControlPointItem:
697 case RedirectAutomationControlPointItem:
698 start_control_point_grab (item, event);
709 case GainAutomationControlPointItem:
710 case PanAutomationControlPointItem:
711 case RedirectAutomationControlPointItem:
712 start_control_point_grab (item, event);
715 case GainAutomationLineItem:
716 case PanAutomationLineItem:
717 case RedirectAutomationLineItem:
718 start_line_grab_from_line (item, event);
722 // XXX need automation mode to identify which
724 // start_line_grab_from_regionview (item, event);
734 if (event->type == GDK_BUTTON_PRESS) {
735 start_mouse_zoom (item, event);
742 if (item_type == RegionItem) {
743 start_time_fx (item, event);
750 scrub_reverse_distance = 0;
751 last_scrub_x = event->button.x;
752 scrubbing_direction = 0;
753 track_canvas.get_window()->set_cursor (*transparent_cursor);
754 /* rest handled in motion & release */
763 switch (mouse_mode) {
765 if (event->type == GDK_BUTTON_PRESS) {
768 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
769 start_region_copy_grab (item, event);
771 start_region_grab (item, event);
776 case GainAutomationControlPointItem:
777 case PanAutomationControlPointItem:
778 case RedirectAutomationControlPointItem:
779 start_control_point_grab (item, event);
790 case RegionViewNameHighlight:
791 start_trim (item, event);
796 start_trim (clicked_regionview->get_name_highlight(), event);
807 if (event->type == GDK_BUTTON_PRESS) {
808 /* relax till release */
815 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
816 temporal_zoom_session();
818 temporal_zoom_to_frame (true, event_frame(event));
841 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
843 nframes_t where = event_frame (event, 0, 0);
844 AutomationTimeAxisView* atv = 0;
846 /* no action if we're recording */
848 if (session && session->actively_recording()) {
852 /* first, see if we're finishing a drag ... */
854 if (drag_info.item) {
855 if (end_grab (item, event)) {
856 /* grab dragged, so do nothing else */
861 button_selection (item, event, item_type);
863 /* edit events get handled here */
865 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
871 case TempoMarkerItem:
872 edit_tempo_marker (item);
875 case MeterMarkerItem:
876 edit_meter_marker (item);
880 if (clicked_regionview->name_active()) {
881 return mouse_rename_region (item, event);
891 /* context menu events get handled here */
893 if (Keyboard::is_context_menu_event (&event->button)) {
895 if (drag_info.item == 0) {
897 /* no matter which button pops up the context menu, tell the menu
898 widget to use button 1 to drive menu selection.
903 case FadeInHandleItem:
905 case FadeOutHandleItem:
906 popup_fade_context_menu (1, event->button.time, item, item_type);
910 popup_track_context_menu (1, event->button.time, item_type, false, where);
914 case RegionViewNameHighlight:
916 popup_track_context_menu (1, event->button.time, item_type, false, where);
920 popup_track_context_menu (1, event->button.time, item_type, true, where);
923 case AutomationTrackItem:
924 popup_track_context_menu (1, event->button.time, item_type, false, where);
928 case RangeMarkerBarItem:
929 case TransportMarkerBarItem:
930 case CdMarkerBarItem:
933 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
937 marker_context_menu (&event->button, item);
940 case TempoMarkerItem:
941 tm_marker_context_menu (&event->button, item);
944 case MeterMarkerItem:
945 tm_marker_context_menu (&event->button, item);
948 case CrossfadeViewItem:
949 popup_track_context_menu (1, event->button.time, item_type, false, where);
952 /* <CMT Additions> */
954 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
956 case ImageFrameTimeAxisItem:
957 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
960 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
962 case MarkerTimeAxisItem:
963 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
965 /* <CMT Additions> */
976 /* delete events get handled here */
978 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
981 case TempoMarkerItem:
982 remove_tempo_marker (item);
985 case MeterMarkerItem:
986 remove_meter_marker (item);
990 remove_marker (*item, event);
994 if (mouse_mode == MouseObject) {
995 remove_clicked_region ();
999 case GainControlPointItem:
1000 if (mouse_mode == MouseGain) {
1001 remove_gain_control_point (item, event);
1005 case GainAutomationControlPointItem:
1006 case PanAutomationControlPointItem:
1007 case RedirectAutomationControlPointItem:
1008 remove_control_point (item, event);
1017 switch (event->button.button) {
1020 switch (item_type) {
1021 /* see comments in button_press_handler */
1022 case PlayheadCursorItem:
1025 case GainAutomationLineItem:
1026 case PanAutomationLineItem:
1027 case RedirectAutomationLineItem:
1028 case StartSelectionTrimItem:
1029 case EndSelectionTrimItem:
1033 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1034 snap_to (where, 0, true);
1036 mouse_add_new_marker (where);
1039 case CdMarkerBarItem:
1040 // if we get here then a dragged range wasn't done
1041 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1042 snap_to (where, 0, true);
1044 mouse_add_new_marker (where, true);
1048 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1051 mouse_add_new_tempo_event (where);
1055 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1063 switch (mouse_mode) {
1065 switch (item_type) {
1066 case AutomationTrackItem:
1067 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_trackview);
1069 atv->add_automation_event (item, event, where, event->button.y);
1081 // Gain only makes sense for audio regions
1083 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1087 switch (item_type) {
1089 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1093 case AutomationTrackItem:
1094 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1095 add_automation_event (item, event, where, event->button.y);
1105 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1106 if (scrubbing_direction == 0) {
1107 /* no drag, just a click */
1108 switch (item_type) {
1110 play_selected_region ();
1116 /* make sure we stop */
1117 session->request_transport_speed (0.0);
1131 switch (mouse_mode) {
1134 switch (item_type) {
1136 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1138 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1141 // Button2 click is unused
1154 // x_style_paste (where, 1.0);
1174 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1180 if (last_item_entered != item) {
1181 last_item_entered = item;
1182 last_item_entered_n = 0;
1185 switch (item_type) {
1186 case GainControlPointItem:
1187 if (mouse_mode == MouseGain) {
1188 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1189 cp->set_visible (true);
1193 at_y = cp->get_y ();
1194 cp->item->i2w (at_x, at_y);
1198 fraction = 1.0 - (cp->get_y() / cp->line.height());
1200 if (is_drawable() && !_scrubbing) {
1201 track_canvas.get_window()->set_cursor (*fader_cursor);
1204 last_item_entered_n++;
1205 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1206 if (last_item_entered_n < 10) {
1207 show_verbose_canvas_cursor ();
1212 case GainAutomationControlPointItem:
1213 case PanAutomationControlPointItem:
1214 case RedirectAutomationControlPointItem:
1215 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1216 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1217 cp->set_visible (true);
1221 at_y = cp->get_y ();
1222 cp->item->i2w (at_x, at_y);
1226 fraction = 1.0 - (cp->get_y() / cp->line.height());
1228 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1229 show_verbose_canvas_cursor ();
1231 if (is_drawable()) {
1232 track_canvas.get_window()->set_cursor (*fader_cursor);
1238 if (mouse_mode == MouseGain) {
1239 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1241 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1242 if (is_drawable()) {
1243 track_canvas.get_window()->set_cursor (*fader_cursor);
1248 case GainAutomationLineItem:
1249 case RedirectAutomationLineItem:
1250 case PanAutomationLineItem:
1251 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1253 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1255 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1257 if (is_drawable()) {
1258 track_canvas.get_window()->set_cursor (*fader_cursor);
1263 case RegionViewNameHighlight:
1264 if (is_drawable() && mouse_mode == MouseObject) {
1265 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1269 case StartSelectionTrimItem:
1270 case EndSelectionTrimItem:
1271 /* <CMT Additions> */
1272 case ImageFrameHandleStartItem:
1273 case ImageFrameHandleEndItem:
1274 case MarkerViewHandleStartItem:
1275 case MarkerViewHandleEndItem:
1276 /* </CMT Additions> */
1278 if (is_drawable()) {
1279 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1283 case PlayheadCursorItem:
1284 if (is_drawable()) {
1285 switch (_edit_point) {
1287 track_canvas.get_window()->set_cursor (*grabber_edit_point_cursor);
1290 track_canvas.get_window()->set_cursor (*grabber_cursor);
1296 case RegionViewName:
1298 /* when the name is not an active item, the entire name highlight is for trimming */
1300 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1301 if (mouse_mode == MouseObject && is_drawable()) {
1302 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1308 case AutomationTrackItem:
1309 if (is_drawable()) {
1310 Gdk::Cursor *cursor;
1311 switch (mouse_mode) {
1313 cursor = selector_cursor;
1316 cursor = zoom_cursor;
1319 cursor = cross_hair_cursor;
1323 track_canvas.get_window()->set_cursor (*cursor);
1325 AutomationTimeAxisView* atv;
1326 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1327 clear_entered_track = false;
1328 set_entered_track (atv);
1334 case RangeMarkerBarItem:
1335 case TransportMarkerBarItem:
1336 case CdMarkerBarItem:
1339 if (is_drawable()) {
1340 time_canvas.get_window()->set_cursor (*timebar_cursor);
1345 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1348 entered_marker = marker;
1349 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1351 case MeterMarkerItem:
1352 case TempoMarkerItem:
1353 if (is_drawable()) {
1354 time_canvas.get_window()->set_cursor (*timebar_cursor);
1357 case FadeInHandleItem:
1358 case FadeOutHandleItem:
1359 if (mouse_mode == MouseObject) {
1360 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1362 rect->property_fill_color_rgba() = 0;
1363 rect->property_outline_pixels() = 1;
1372 /* second pass to handle entered track status in a comprehensible way.
1375 switch (item_type) {
1377 case GainAutomationLineItem:
1378 case RedirectAutomationLineItem:
1379 case PanAutomationLineItem:
1380 case GainControlPointItem:
1381 case GainAutomationControlPointItem:
1382 case PanAutomationControlPointItem:
1383 case RedirectAutomationControlPointItem:
1384 /* these do not affect the current entered track state */
1385 clear_entered_track = false;
1388 case AutomationTrackItem:
1389 /* handled above already */
1393 set_entered_track (0);
1401 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1410 switch (item_type) {
1411 case GainControlPointItem:
1412 case GainAutomationControlPointItem:
1413 case PanAutomationControlPointItem:
1414 case RedirectAutomationControlPointItem:
1415 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1416 if (cp->line.npoints() > 1) {
1417 if (!cp->selected) {
1418 cp->set_visible (false);
1422 if (is_drawable()) {
1423 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1426 hide_verbose_canvas_cursor ();
1429 case RegionViewNameHighlight:
1430 case StartSelectionTrimItem:
1431 case EndSelectionTrimItem:
1432 case PlayheadCursorItem:
1433 /* <CMT Additions> */
1434 case ImageFrameHandleStartItem:
1435 case ImageFrameHandleEndItem:
1436 case MarkerViewHandleStartItem:
1437 case MarkerViewHandleEndItem:
1438 /* </CMT Additions> */
1439 if (is_drawable()) {
1440 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1445 case GainAutomationLineItem:
1446 case RedirectAutomationLineItem:
1447 case PanAutomationLineItem:
1448 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1450 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1452 line->property_fill_color_rgba() = al->get_line_color();
1454 if (is_drawable()) {
1455 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1459 case RegionViewName:
1460 /* see enter_handler() for notes */
1461 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1462 if (is_drawable() && mouse_mode == MouseObject) {
1463 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1468 case RangeMarkerBarItem:
1469 case TransportMarkerBarItem:
1470 case CdMarkerBarItem:
1474 if (is_drawable()) {
1475 time_canvas.get_window()->set_cursor (*timebar_cursor);
1480 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1484 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1485 location_flags_changed (loc, this);
1488 case MeterMarkerItem:
1489 case TempoMarkerItem:
1491 if (is_drawable()) {
1492 time_canvas.get_window()->set_cursor (*timebar_cursor);
1497 case FadeInHandleItem:
1498 case FadeOutHandleItem:
1499 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1501 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1503 rect->property_fill_color_rgba() = rv->get_fill_color();
1504 rect->property_outline_pixels() = 0;
1509 case AutomationTrackItem:
1510 if (is_drawable()) {
1511 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1512 clear_entered_track = true;
1513 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1525 Editor::left_automation_track ()
1527 if (clear_entered_track) {
1528 set_entered_track (0);
1529 clear_entered_track = false;
1535 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1537 if (event->motion.is_hint) {
1540 /* We call this so that MOTION_NOTIFY events continue to be
1541 delivered to the canvas. We need to do this because we set
1542 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1543 the density of the events, at the expense of a round-trip
1544 to the server. Given that this will mostly occur on cases
1545 where DISPLAY = :0.0, and given the cost of what the motion
1546 event might do, its a good tradeoff.
1549 track_canvas.get_pointer (x, y);
1552 if (current_stepping_trackview) {
1553 /* don't keep the persistent stepped trackview if the mouse moves */
1554 current_stepping_trackview = 0;
1555 step_timeout.disconnect ();
1558 if (session && session->actively_recording()) {
1559 /* Sorry. no dragging stuff around while we record */
1563 drag_info.item_type = item_type;
1564 drag_info.last_pointer_x = drag_info.current_pointer_x;
1565 drag_info.last_pointer_y = drag_info.current_pointer_y;
1566 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1567 &drag_info.current_pointer_y);
1569 switch (mouse_mode) {
1575 if (scrubbing_direction == 0) {
1577 session->request_locate (drag_info.current_pointer_frame, false);
1578 session->request_transport_speed (0.1);
1579 scrubbing_direction = 1;
1583 if (last_scrub_x > drag_info.current_pointer_x) {
1585 /* pointer moved to the left */
1587 if (scrubbing_direction > 0) {
1589 /* we reversed direction to go backwards */
1592 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1596 /* still moving to the left (backwards) */
1598 scrub_reversals = 0;
1599 scrub_reverse_distance = 0;
1601 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1602 session->request_transport_speed (session->transport_speed() - delta);
1606 /* pointer moved to the right */
1608 if (scrubbing_direction < 0) {
1609 /* we reversed direction to go forward */
1612 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1615 /* still moving to the right */
1617 scrub_reversals = 0;
1618 scrub_reverse_distance = 0;
1620 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1621 session->request_transport_speed (session->transport_speed() + delta);
1625 /* if there have been more than 2 opposite motion moves detected, or one that moves
1626 back more than 10 pixels, reverse direction
1629 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1631 if (scrubbing_direction > 0) {
1632 /* was forwards, go backwards */
1633 session->request_transport_speed (-0.1);
1634 scrubbing_direction = -1;
1636 /* was backwards, go forwards */
1637 session->request_transport_speed (0.1);
1638 scrubbing_direction = 1;
1641 scrub_reverse_distance = 0;
1642 scrub_reversals = 0;
1646 last_scrub_x = drag_info.current_pointer_x;
1653 if (!from_autoscroll && drag_info.item) {
1654 /* item != 0 is the best test i can think of for dragging.
1656 if (!drag_info.move_threshold_passed) {
1658 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1659 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1661 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1663 // and change the initial grab loc/frame if this drag info wants us to
1665 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1666 drag_info.grab_frame = drag_info.current_pointer_frame;
1667 drag_info.grab_x = drag_info.current_pointer_x;
1668 drag_info.grab_y = drag_info.current_pointer_y;
1669 drag_info.last_pointer_frame = drag_info.grab_frame;
1670 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1675 switch (item_type) {
1676 case PlayheadCursorItem:
1678 case GainControlPointItem:
1679 case RedirectAutomationControlPointItem:
1680 case GainAutomationControlPointItem:
1681 case PanAutomationControlPointItem:
1682 case TempoMarkerItem:
1683 case MeterMarkerItem:
1684 case RegionViewNameHighlight:
1685 case StartSelectionTrimItem:
1686 case EndSelectionTrimItem:
1689 case RedirectAutomationLineItem:
1690 case GainAutomationLineItem:
1691 case PanAutomationLineItem:
1692 case FadeInHandleItem:
1693 case FadeOutHandleItem:
1694 /* <CMT Additions> */
1695 case ImageFrameHandleStartItem:
1696 case ImageFrameHandleEndItem:
1697 case MarkerViewHandleStartItem:
1698 case MarkerViewHandleEndItem:
1699 /* </CMT Additions> */
1700 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1701 (event->motion.state & Gdk::BUTTON2_MASK))) {
1702 if (!from_autoscroll) {
1703 maybe_autoscroll (event);
1705 (this->*(drag_info.motion_callback)) (item, event);
1714 switch (mouse_mode) {
1719 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1720 (event->motion.state & GDK_BUTTON2_MASK))) {
1721 if (!from_autoscroll) {
1722 maybe_autoscroll (event);
1724 (this->*(drag_info.motion_callback)) (item, event);
1735 track_canvas_motion (event);
1736 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1744 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1746 if (drag_info.item == 0) {
1747 fatal << _("programming error: start_grab called without drag item") << endmsg;
1753 cursor = which_grabber_cursor ();
1756 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1758 if (event->button.button == 2) {
1759 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1760 drag_info.y_constrained = true;
1761 drag_info.x_constrained = false;
1763 drag_info.y_constrained = false;
1764 drag_info.x_constrained = true;
1767 drag_info.x_constrained = false;
1768 drag_info.y_constrained = false;
1771 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1772 drag_info.last_pointer_frame = drag_info.grab_frame;
1773 drag_info.current_pointer_frame = drag_info.grab_frame;
1774 drag_info.current_pointer_x = drag_info.grab_x;
1775 drag_info.current_pointer_y = drag_info.grab_y;
1776 drag_info.last_pointer_x = drag_info.current_pointer_x;
1777 drag_info.last_pointer_y = drag_info.current_pointer_y;
1778 drag_info.cumulative_x_drag = 0;
1779 drag_info.cumulative_y_drag = 0;
1780 drag_info.first_move = true;
1781 drag_info.move_threshold_passed = false;
1782 drag_info.want_move_threshold = false;
1783 drag_info.pointer_frame_offset = 0;
1784 drag_info.brushing = false;
1785 drag_info.copied_location = 0;
1787 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1789 event->button.time);
1791 if (session && session->transport_rolling()) {
1792 drag_info.was_rolling = true;
1794 drag_info.was_rolling = false;
1797 switch (snap_type) {
1798 case SnapToRegionStart:
1799 case SnapToRegionEnd:
1800 case SnapToRegionSync:
1801 case SnapToRegionBoundary:
1802 build_region_boundary_cache ();
1810 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1812 drag_info.item->ungrab (0);
1813 drag_info.item = new_item;
1816 cursor = which_grabber_cursor ();
1819 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1823 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1825 bool did_drag = false;
1827 stop_canvas_autoscroll ();
1829 if (drag_info.item == 0) {
1833 drag_info.item->ungrab (event->button.time);
1835 if (drag_info.finished_callback) {
1836 drag_info.last_pointer_x = drag_info.current_pointer_x;
1837 drag_info.last_pointer_y = drag_info.current_pointer_y;
1838 (this->*(drag_info.finished_callback)) (item, event);
1841 did_drag = !drag_info.first_move;
1843 hide_verbose_canvas_cursor();
1846 drag_info.copy = false;
1847 drag_info.motion_callback = 0;
1848 drag_info.finished_callback = 0;
1849 drag_info.last_trackview = 0;
1850 drag_info.last_frame_position = 0;
1851 drag_info.grab_frame = 0;
1852 drag_info.last_pointer_frame = 0;
1853 drag_info.current_pointer_frame = 0;
1854 drag_info.brushing = false;
1856 if (drag_info.copied_location) {
1857 delete drag_info.copied_location;
1858 drag_info.copied_location = 0;
1865 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1867 drag_info.item = item;
1868 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1869 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1873 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1874 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1878 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1880 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1884 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1886 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1888 nframes_t fade_length;
1890 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1891 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1897 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1901 if (pos < (arv->region()->position() + 64)) {
1902 fade_length = 64; // this should be a minimum defined somewhere
1903 } else if (pos > arv->region()->last_frame()) {
1904 fade_length = arv->region()->length();
1906 fade_length = pos - arv->region()->position();
1908 /* mapover the region selection */
1910 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1912 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1918 tmp->reset_fade_in_shape_width (fade_length);
1921 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1923 drag_info.first_move = false;
1927 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1929 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1931 nframes_t fade_length;
1933 if (drag_info.first_move) return;
1935 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1936 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1941 if (pos < (arv->region()->position() + 64)) {
1942 fade_length = 64; // this should be a minimum defined somewhere
1943 } else if (pos > arv->region()->last_frame()) {
1944 fade_length = arv->region()->length();
1946 fade_length = pos - arv->region()->position();
1949 begin_reversible_command (_("change fade in length"));
1951 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1953 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1959 AutomationList& alist = tmp->audio_region()->fade_in();
1960 XMLNode &before = alist.get_state();
1962 tmp->audio_region()->set_fade_in_length (fade_length);
1963 tmp->audio_region()->set_fade_in_active (true);
1965 XMLNode &after = alist.get_state();
1966 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1969 commit_reversible_command ();
1973 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1975 drag_info.item = item;
1976 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1977 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1981 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1982 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1986 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1988 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1992 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1994 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1996 nframes_t fade_length;
1998 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1999 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2004 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2008 if (pos > (arv->region()->last_frame() - 64)) {
2009 fade_length = 64; // this should really be a minimum fade defined somewhere
2011 else if (pos < arv->region()->position()) {
2012 fade_length = arv->region()->length();
2015 fade_length = arv->region()->last_frame() - pos;
2018 /* mapover the region selection */
2020 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2022 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2028 tmp->reset_fade_out_shape_width (fade_length);
2031 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2033 drag_info.first_move = false;
2037 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2039 if (drag_info.first_move) return;
2041 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2043 nframes_t fade_length;
2045 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2046 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2052 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2056 if (pos > (arv->region()->last_frame() - 64)) {
2057 fade_length = 64; // this should really be a minimum fade defined somewhere
2059 else if (pos < arv->region()->position()) {
2060 fade_length = arv->region()->length();
2063 fade_length = arv->region()->last_frame() - pos;
2066 begin_reversible_command (_("change fade out length"));
2068 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2070 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2076 AutomationList& alist = tmp->audio_region()->fade_out();
2077 XMLNode &before = alist.get_state();
2079 tmp->audio_region()->set_fade_out_length (fade_length);
2080 tmp->audio_region()->set_fade_out_active (true);
2082 XMLNode &after = alist.get_state();
2083 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2086 commit_reversible_command ();
2090 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2092 drag_info.item = item;
2093 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2094 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2098 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2099 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2103 Cursor* cursor = (Cursor *) drag_info.data;
2105 if (cursor == playhead_cursor) {
2106 _dragging_playhead = true;
2108 if (session && drag_info.was_rolling) {
2109 session->request_stop ();
2112 if (session && session->is_auditioning()) {
2113 session->cancel_audition ();
2117 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2119 show_verbose_time_cursor (cursor->current_frame, 10);
2123 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2125 Cursor* cursor = (Cursor *) drag_info.data;
2126 nframes_t adjusted_frame;
2128 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2129 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2135 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2136 if (cursor == playhead_cursor) {
2137 snap_to (adjusted_frame);
2141 if (adjusted_frame == drag_info.last_pointer_frame) return;
2143 cursor->set_position (adjusted_frame);
2145 UpdateAllTransportClocks (cursor->current_frame);
2147 show_verbose_time_cursor (cursor->current_frame, 10);
2149 drag_info.last_pointer_frame = adjusted_frame;
2150 drag_info.first_move = false;
2154 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2156 if (drag_info.first_move) return;
2158 cursor_drag_motion_callback (item, event);
2160 _dragging_playhead = false;
2162 if (item == &playhead_cursor->canvas_item) {
2164 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2170 Editor::update_marker_drag_item (Location *location)
2172 double x1 = frame_to_pixel (location->start());
2173 double x2 = frame_to_pixel (location->end());
2175 if (location->is_mark()) {
2176 marker_drag_line_points.front().set_x(x1);
2177 marker_drag_line_points.back().set_x(x1);
2178 marker_drag_line->property_points() = marker_drag_line_points;
2181 range_marker_drag_rect->property_x1() = x1;
2182 range_marker_drag_rect->property_x2() = x2;
2187 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2191 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2192 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2198 Location *location = find_location_from_marker (marker, is_start);
2200 drag_info.item = item;
2201 drag_info.data = marker;
2202 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2203 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2207 _dragging_edit_point = true;
2209 drag_info.copied_location = new Location (*location);
2210 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2212 update_marker_drag_item (location);
2214 if (location->is_mark()) {
2215 // marker_drag_line->show();
2216 // marker_drag_line->raise_to_top();
2218 range_marker_drag_rect->show();
2219 range_marker_drag_rect->raise_to_top();
2223 show_verbose_time_cursor (location->start(), 10);
2225 show_verbose_time_cursor (location->end(), 10);
2228 Selection::Operation op = Keyboard::selection_type (event->button.state);
2231 case Selection::Toggle:
2232 selection->toggle (marker);
2234 case Selection::Set:
2235 selection->set (marker);
2237 case Selection::Extend:
2238 selection->add (marker);
2240 case Selection::Add:
2241 selection->add (marker);
2247 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2250 Marker* marker = (Marker *) drag_info.data;
2251 Location *real_location;
2252 Location *copy_location;
2254 bool move_both = false;
2257 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2258 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2263 nframes_t next = newframe;
2265 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2266 snap_to (newframe, 0, true);
2269 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2273 /* call this to find out if its the start or end */
2275 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2279 if (real_location->locked()) {
2283 /* use the copy that we're "dragging" around */
2285 copy_location = drag_info.copied_location;
2287 f_delta = copy_location->end() - copy_location->start();
2289 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2293 if (copy_location->is_mark()) {
2296 copy_location->set_start (newframe);
2300 if (is_start) { // start-of-range marker
2303 copy_location->set_start (newframe);
2304 copy_location->set_end (newframe + f_delta);
2305 } else if (newframe < copy_location->end()) {
2306 copy_location->set_start (newframe);
2308 snap_to (next, 1, true);
2309 copy_location->set_end (next);
2310 copy_location->set_start (newframe);
2313 } else { // end marker
2316 copy_location->set_end (newframe);
2317 copy_location->set_start (newframe - f_delta);
2318 } else if (newframe > copy_location->start()) {
2319 copy_location->set_end (newframe);
2321 } else if (newframe > 0) {
2322 snap_to (next, -1, true);
2323 copy_location->set_start (next);
2324 copy_location->set_end (newframe);
2329 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2330 drag_info.first_move = false;
2332 update_marker_drag_item (copy_location);
2334 LocationMarkers* lm = find_location_markers (real_location);
2335 lm->set_position (copy_location->start(), copy_location->end());
2336 edit_point_clock.set (copy_location->start());
2338 show_verbose_time_cursor (newframe, 10);
2342 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2344 if (drag_info.first_move) {
2345 marker_drag_motion_callback (item, event);
2349 _dragging_edit_point = false;
2351 Marker* marker = (Marker *) drag_info.data;
2354 begin_reversible_command ( _("move marker") );
2355 XMLNode &before = session->locations()->get_state();
2357 Location * location = find_location_from_marker (marker, is_start);
2361 if (location->locked()) {
2365 if (location->is_mark()) {
2366 location->set_start (drag_info.copied_location->start());
2368 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2372 XMLNode &after = session->locations()->get_state();
2373 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2374 commit_reversible_command ();
2376 marker_drag_line->hide();
2377 range_marker_drag_rect->hide();
2381 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2384 MeterMarker* meter_marker;
2386 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2387 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2391 meter_marker = dynamic_cast<MeterMarker*> (marker);
2393 MetricSection& section (meter_marker->meter());
2395 if (!section.movable()) {
2399 drag_info.item = item;
2400 drag_info.copy = false;
2401 drag_info.data = marker;
2402 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2403 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2407 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2409 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2413 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2416 MeterMarker* meter_marker;
2418 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2419 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2423 meter_marker = dynamic_cast<MeterMarker*> (marker);
2425 // create a dummy marker for visual representation of moving the copy.
2426 // The actual copying is not done before we reach the finish callback.
2428 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2429 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2430 *new MeterSection(meter_marker->meter()));
2432 drag_info.item = &new_marker->the_item();
2433 drag_info.copy = true;
2434 drag_info.data = new_marker;
2435 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2436 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2440 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2442 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2446 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2448 MeterMarker* marker = (MeterMarker *) drag_info.data;
2449 nframes_t adjusted_frame;
2451 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2452 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2458 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2459 snap_to (adjusted_frame);
2462 if (adjusted_frame == drag_info.last_pointer_frame) return;
2464 marker->set_position (adjusted_frame);
2467 drag_info.last_pointer_frame = adjusted_frame;
2468 drag_info.first_move = false;
2470 show_verbose_time_cursor (adjusted_frame, 10);
2474 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2476 if (drag_info.first_move) return;
2478 meter_marker_drag_motion_callback (drag_info.item, event);
2480 MeterMarker* marker = (MeterMarker *) drag_info.data;
2483 TempoMap& map (session->tempo_map());
2484 map.bbt_time (drag_info.last_pointer_frame, when);
2486 if (drag_info.copy == true) {
2487 begin_reversible_command (_("copy meter mark"));
2488 XMLNode &before = map.get_state();
2489 map.add_meter (marker->meter(), when);
2490 XMLNode &after = map.get_state();
2491 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2492 commit_reversible_command ();
2494 // delete the dummy marker we used for visual representation of copying.
2495 // a new visual marker will show up automatically.
2498 begin_reversible_command (_("move meter mark"));
2499 XMLNode &before = map.get_state();
2500 map.move_meter (marker->meter(), when);
2501 XMLNode &after = map.get_state();
2502 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2503 commit_reversible_command ();
2508 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2511 TempoMarker* tempo_marker;
2513 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2514 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2518 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2519 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2523 MetricSection& section (tempo_marker->tempo());
2525 if (!section.movable()) {
2529 drag_info.item = item;
2530 drag_info.copy = false;
2531 drag_info.data = marker;
2532 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2533 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2537 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2538 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2542 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2545 TempoMarker* tempo_marker;
2547 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2548 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2552 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2553 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2557 // create a dummy marker for visual representation of moving the copy.
2558 // The actual copying is not done before we reach the finish callback.
2560 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2561 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2562 *new TempoSection(tempo_marker->tempo()));
2564 drag_info.item = &new_marker->the_item();
2565 drag_info.copy = true;
2566 drag_info.data = new_marker;
2567 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2568 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2572 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2574 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2578 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2580 TempoMarker* marker = (TempoMarker *) drag_info.data;
2581 nframes_t adjusted_frame;
2583 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2584 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2590 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2591 snap_to (adjusted_frame);
2594 if (adjusted_frame == drag_info.last_pointer_frame) return;
2596 /* OK, we've moved far enough to make it worth actually move the thing. */
2598 marker->set_position (adjusted_frame);
2600 show_verbose_time_cursor (adjusted_frame, 10);
2602 drag_info.last_pointer_frame = adjusted_frame;
2603 drag_info.first_move = false;
2607 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2609 if (drag_info.first_move) return;
2611 tempo_marker_drag_motion_callback (drag_info.item, event);
2613 TempoMarker* marker = (TempoMarker *) drag_info.data;
2616 TempoMap& map (session->tempo_map());
2617 map.bbt_time (drag_info.last_pointer_frame, when);
2619 if (drag_info.copy == true) {
2620 begin_reversible_command (_("copy tempo mark"));
2621 XMLNode &before = map.get_state();
2622 map.add_tempo (marker->tempo(), when);
2623 XMLNode &after = map.get_state();
2624 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2625 commit_reversible_command ();
2627 // delete the dummy marker we used for visual representation of copying.
2628 // a new visual marker will show up automatically.
2631 begin_reversible_command (_("move tempo mark"));
2632 XMLNode &before = map.get_state();
2633 map.move_tempo (marker->tempo(), when);
2634 XMLNode &after = map.get_state();
2635 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2636 commit_reversible_command ();
2641 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2643 ControlPoint* control_point;
2645 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2646 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2650 // We shouldn't remove the first or last gain point
2651 if (control_point->line.is_last_point(*control_point) ||
2652 control_point->line.is_first_point(*control_point)) {
2656 control_point->line.remove_point (*control_point);
2660 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2662 ControlPoint* control_point;
2664 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2665 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2669 control_point->line.remove_point (*control_point);
2673 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2675 ControlPoint* control_point;
2677 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2678 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2682 drag_info.item = item;
2683 drag_info.data = control_point;
2684 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2685 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2687 start_grab (event, fader_cursor);
2689 // start the grab at the center of the control point so
2690 // the point doesn't 'jump' to the mouse after the first drag
2691 drag_info.grab_x = control_point->get_x();
2692 drag_info.grab_y = control_point->get_y();
2693 control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2694 track_canvas.w2c(drag_info.grab_x, drag_info.grab_y,
2695 drag_info.grab_x, drag_info.grab_y);
2697 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2699 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2701 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2702 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2703 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2705 show_verbose_canvas_cursor ();
2709 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2711 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2713 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2714 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2716 if (event->button.state & Keyboard::SecondaryModifier) {
2721 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2722 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2724 // calculate zero crossing point. back off by .01 to stay on the
2725 // positive side of zero
2727 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01;
2728 cp->line.parent_group().i2w(_unused, zero_gain_y);
2730 // make sure we hit zero when passing through
2731 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2732 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2736 if (drag_info.x_constrained) {
2737 cx = drag_info.grab_x;
2739 if (drag_info.y_constrained) {
2740 cy = drag_info.grab_y;
2743 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2744 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2746 cp->line.parent_group().w2i (cx, cy);
2750 cy = min ((double) cp->line.height(), cy);
2752 //translate cx to frames
2753 nframes_t cx_frames = unit_to_frame (cx);
2755 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2756 snap_to (cx_frames);
2759 float fraction = 1.0 - (cy / cp->line.height());
2763 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2769 cp->line.point_drag (*cp, cx_frames , fraction, push);
2771 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2773 drag_info.first_move = false;
2777 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2779 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2781 if (drag_info.first_move) {
2785 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2786 reset_point_selection ();
2790 control_point_drag_motion_callback (item, event);
2792 cp->line.end_drag (cp);
2796 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2798 switch (mouse_mode) {
2800 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2801 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2809 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2813 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2814 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2818 start_line_grab (al, event);
2822 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2826 nframes_t frame_within_region;
2828 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2832 cx = event->button.x;
2833 cy = event->button.y;
2834 line->parent_group().w2i (cx, cy);
2835 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2837 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2838 current_line_drag_info.after)) {
2839 /* no adjacent points */
2843 drag_info.item = &line->grab_item();
2844 drag_info.data = line;
2845 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2846 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2848 start_grab (event, fader_cursor);
2850 double fraction = 1.0 - (cy / line->height());
2852 line->start_drag (0, drag_info.grab_frame, fraction);
2854 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2855 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2856 show_verbose_canvas_cursor ();
2860 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2862 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2864 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2866 if (event->button.state & Keyboard::SecondaryModifier) {
2870 double cx = drag_info.current_pointer_x;
2871 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2873 // calculate zero crossing point. back off by .01 to stay on the
2874 // positive side of zero
2876 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
2877 line->parent_group().i2w(_unused, zero_gain_y);
2879 // make sure we hit zero when passing through
2880 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2881 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2885 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2887 line->parent_group().w2i (cx, cy);
2890 cy = min ((double) line->height(), cy);
2893 fraction = 1.0 - (cy / line->height());
2897 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2903 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2905 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2909 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2911 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2912 line_drag_motion_callback (item, event);
2917 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2919 if (selection->regions.empty() || clicked_regionview == 0) {
2923 drag_info.copy = false;
2924 drag_info.item = item;
2925 drag_info.data = clicked_regionview;
2927 if (Config->get_edit_mode() == Splice) {
2928 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
2929 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
2931 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2932 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2938 TimeAxisView* tvp = clicked_trackview;
2939 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2941 if (tv && tv->is_audio_track()) {
2942 speed = tv->get_diskstream()->speed();
2945 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2946 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2947 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2948 // we want a move threshold
2949 drag_info.want_move_threshold = true;
2951 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2953 begin_reversible_command (_("move region(s)"));
2957 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2959 if (selection->regions.empty() || clicked_regionview == 0) {
2963 drag_info.copy = true;
2964 drag_info.item = item;
2965 drag_info.data = clicked_regionview;
2969 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2970 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2973 if (atv && atv->is_audio_track()) {
2974 speed = atv->get_diskstream()->speed();
2977 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2978 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2979 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2980 // we want a move threshold
2981 drag_info.want_move_threshold = true;
2982 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2983 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2984 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2988 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2990 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
2994 drag_info.copy = false;
2995 drag_info.item = item;
2996 drag_info.data = clicked_regionview;
2997 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2998 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3003 TimeAxisView* tvp = clicked_trackview;
3004 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3006 if (tv && tv->is_audio_track()) {
3007 speed = tv->get_diskstream()->speed();
3010 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
3011 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3012 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
3013 // we want a move threshold
3014 drag_info.want_move_threshold = true;
3015 drag_info.brushing = true;
3017 begin_reversible_command (_("Drag region brush"));
3021 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3023 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3025 drag_info.want_move_threshold = false; // don't copy again
3027 /* duplicate the region(s) */
3029 vector<RegionView*> new_regionviews;
3031 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3034 AudioRegionView* arv;
3039 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
3040 /* XXX handle MIDI here */
3044 nrv = new AudioRegionView (*arv);
3045 nrv->get_canvas_group()->show ();
3047 new_regionviews.push_back (nrv);
3050 if (new_regionviews.empty()) {
3054 /* reset selection to new regionviews */
3056 selection->set (new_regionviews);
3058 /* reset drag_info data to reflect the fact that we are dragging the copies */
3060 drag_info.data = new_regionviews.front();
3062 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3067 Editor::check_region_drag_possible (AudioTimeAxisView** tv)
3069 /* Which trackview is this ? */
3071 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3072 (*tv) = dynamic_cast<AudioTimeAxisView*>(tvp);
3074 /* The region motion is only processed if the pointer is over
3078 if (!(*tv) || !(*tv)->is_audio_track()) {
3079 /* To make sure we hide the verbose canvas cursor when the mouse is
3080 not held over and audiotrack.
3082 hide_verbose_canvas_cursor ();
3089 struct RegionSelectionByPosition {
3090 bool operator() (RegionView*a, RegionView* b) {
3091 return a->region()->position () < b->region()->position();
3096 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3098 AudioTimeAxisView* tv;
3100 if (!check_region_drag_possible (&tv)) {
3104 if (!drag_info.move_threshold_passed) {
3110 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3116 RegionSelection copy (selection->regions);
3118 RegionSelectionByPosition cmp;
3121 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3123 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(*i)->get_time_axis_view());
3129 boost::shared_ptr<Playlist> playlist;
3131 if ((playlist = atv->playlist()) == 0) {
3135 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3140 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3144 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3150 playlist->shuffle ((*i)->region(), dir);
3152 drag_info.grab_x = drag_info.current_pointer_x;
3157 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3162 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3166 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3167 nframes_t pending_region_position = 0;
3168 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3169 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3170 bool clamp_y_axis = false;
3171 vector<int32_t> height_list(512) ;
3172 vector<int32_t>::iterator j;
3173 AudioTimeAxisView* tv;
3175 possibly_copy_regions_during_grab (event);
3177 if (!check_region_drag_possible (&tv)) {
3181 original_pointer_order = drag_info.last_trackview->order;
3183 /************************************************************
3185 ************************************************************/
3187 if (drag_info.brushing) {
3188 clamp_y_axis = true;
3193 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
3195 int32_t children = 0, numtracks = 0;
3196 // XXX hard coding track limit, oh my, so very very bad
3197 bitset <1024> tracks (0x00);
3198 /* get a bitmask representing the visible tracks */
3200 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3201 TimeAxisView *tracklist_timeview;
3202 tracklist_timeview = (*i);
3203 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3204 list<TimeAxisView*> children_list;
3206 /* zeroes are audio tracks. ones are other types. */
3208 if (!atv2->hidden()) {
3210 if (visible_y_high < atv2->order) {
3211 visible_y_high = atv2->order;
3213 if (visible_y_low > atv2->order) {
3214 visible_y_low = atv2->order;
3217 if (!atv2->is_audio_track()) {
3218 tracks = tracks |= (0x01 << atv2->order);
3221 height_list[atv2->order] = (*i)->height;
3223 if ((children_list = atv2->get_child_list()).size() > 0) {
3224 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3225 tracks = tracks |= (0x01 << (atv2->order + children));
3226 height_list[atv2->order + children] = (*j)->height;
3234 /* find the actual span according to the canvas */
3236 canvas_pointer_y_span = pointer_y_span;
3237 if (drag_info.last_trackview->order >= tv->order) {
3239 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
3240 if (height_list[y] == 0 ) {
3241 canvas_pointer_y_span--;
3246 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
3247 if ( height_list[y] == 0 ) {
3248 canvas_pointer_y_span++;
3253 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3254 RegionView* rv2 = (*i);
3255 double ix1, ix2, iy1, iy2;
3258 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3259 rv2->get_canvas_group()->i2w (ix1, iy1);
3260 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3261 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3263 if (atv2->order != original_pointer_order) {
3264 /* this isn't the pointer track */
3266 if (canvas_pointer_y_span > 0) {
3268 /* moving up the canvas */
3269 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3271 int32_t visible_tracks = 0;
3272 while (visible_tracks < canvas_pointer_y_span ) {
3275 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3276 /* we're passing through a hidden track */
3281 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3282 clamp_y_axis = true;
3286 clamp_y_axis = true;
3289 } else if (canvas_pointer_y_span < 0) {
3291 /*moving down the canvas*/
3293 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3296 int32_t visible_tracks = 0;
3298 while (visible_tracks > canvas_pointer_y_span ) {
3301 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3305 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3306 clamp_y_axis = true;
3311 clamp_y_axis = true;
3317 /* this is the pointer's track */
3318 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3319 clamp_y_axis = true;
3320 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3321 clamp_y_axis = true;
3329 } else if (drag_info.last_trackview == tv) {
3330 clamp_y_axis = true;
3334 if (!clamp_y_axis) {
3335 drag_info.last_trackview = tv;
3338 /************************************************************
3340 ************************************************************/
3342 /* compute the amount of pointer motion in frames, and where
3343 the region would be if we moved it by that much.
3346 if ( drag_info.move_threshold_passed ) {
3348 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3350 nframes_t sync_frame;
3351 nframes_t sync_offset;
3354 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3356 sync_offset = rv->region()->sync_offset (sync_dir);
3358 /* we don't handle a sync point that lies before zero.
3360 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3361 sync_frame = pending_region_position + (sync_dir*sync_offset);
3363 /* we snap if the snap modifier is not enabled.
3366 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3367 snap_to (sync_frame);
3370 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3373 pending_region_position = drag_info.last_frame_position;
3377 pending_region_position = 0;
3380 if (pending_region_position > max_frames - rv->region()->length()) {
3381 pending_region_position = drag_info.last_frame_position;
3384 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3386 bool x_move_allowed = ( !drag_info.x_constrained && (Config->get_edit_mode() != Lock)) || ( drag_info.x_constrained && (Config->get_edit_mode() == Lock)) ;
3387 if ( pending_region_position != drag_info.last_frame_position && x_move_allowed ) {
3389 /* now compute the canvas unit distance we need to move the regionview
3390 to make it appear at the new location.
3393 if (pending_region_position > drag_info.last_frame_position) {
3394 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3396 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3399 drag_info.last_frame_position = pending_region_position;
3406 /* threshold not passed */
3411 /*************************************************************
3413 ************************************************************/
3415 if (x_delta == 0 && (pointer_y_span == 0)) {
3416 /* haven't reached next snap point, and we're not switching
3417 trackviews. nothing to do.
3424 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3426 RegionView* rv2 = (*i);
3428 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3430 double ix1, ix2, iy1, iy2;
3431 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3432 rv2->get_canvas_group()->i2w (ix1, iy1);
3441 /*************************************************************
3443 ************************************************************/
3447 if (drag_info.first_move) {
3448 if (drag_info.move_threshold_passed) {
3459 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3460 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3462 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3464 RegionView* rv = (*i);
3465 double ix1, ix2, iy1, iy2;
3466 int32_t temp_pointer_y_span = pointer_y_span;
3468 /* get item BBox, which will be relative to parent. so we have
3469 to query on a child, then convert to world coordinates using
3473 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3474 rv->get_canvas_group()->i2w (ix1, iy1);
3475 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3476 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3477 AudioTimeAxisView* temp_atv;
3479 if ((pointer_y_span != 0) && !clamp_y_axis) {
3482 for (j = height_list.begin(); j!= height_list.end(); j++) {
3483 if (x == canvas_atv->order) {
3484 /* we found the track the region is on */
3485 if (x != original_pointer_order) {
3486 /*this isn't from the same track we're dragging from */
3487 temp_pointer_y_span = canvas_pointer_y_span;
3489 while (temp_pointer_y_span > 0) {
3490 /* we're moving up canvas-wise,
3491 so we need to find the next track height
3493 if (j != height_list.begin()) {
3496 if (x != original_pointer_order) {
3497 /* we're not from the dragged track, so ignore hidden tracks. */
3499 temp_pointer_y_span++;
3503 temp_pointer_y_span--;
3505 while (temp_pointer_y_span < 0) {
3507 if (x != original_pointer_order) {
3509 temp_pointer_y_span--;
3513 if (j != height_list.end()) {
3516 temp_pointer_y_span++;
3518 /* find out where we'll be when we move and set height accordingly */
3520 tvp2 = trackview_by_y_position (iy1 + y_delta);
3521 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3522 rv->set_height (temp_atv->height);
3524 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3525 personally, i think this can confuse things, but never mind.
3528 //const GdkColor& col (temp_atv->view->get_region_color());
3529 //rv->set_color (const_cast<GdkColor&>(col));
3536 /* prevent the regionview from being moved to before
3537 the zero position on the canvas.
3542 if (-x_delta > ix1) {
3545 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3546 x_delta = max_frames - rv->region()->last_frame();
3550 if (drag_info.first_move) {
3552 /* hide any dependent views */
3554 rv->get_time_axis_view().hide_dependent_views (*rv);
3556 /* this is subtle. raising the regionview itself won't help,
3557 because raise_to_top() just puts the item on the top of
3558 its parent's stack. so, we need to put the trackview canvas_display group
3559 on the top, since its parent is the whole canvas.
3562 rv->get_canvas_group()->raise_to_top();
3563 rv->get_time_axis_view().canvas_display->raise_to_top();
3564 cursor_group->raise_to_top();
3565 rv->fake_set_opaque (true);
3568 if (drag_info.brushing) {
3569 mouse_brush_insert_region (rv, pending_region_position);
3571 rv->move (x_delta, y_delta);
3574 } /* foreach region */
3578 if (drag_info.first_move && drag_info.move_threshold_passed) {
3579 cursor_group->raise_to_top();
3580 drag_info.first_move = false;
3583 if (x_delta != 0 && !drag_info.brushing) {
3584 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3589 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3592 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3593 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3594 bool nocommit = true;
3596 RouteTimeAxisView* atv;
3597 bool regionview_y_movement;
3598 bool regionview_x_movement;
3599 vector<RegionView*> copies;
3601 /* first_move is set to false if the regionview has been moved in the
3605 if (drag_info.first_move) {
3612 /* The regionview has been moved at some stage during the grab so we need
3613 to account for any mouse movement between this event and the last one.
3616 region_drag_motion_callback (item, event);
3618 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3619 selection->set (pre_drag_region_selection);
3620 pre_drag_region_selection.clear ();
3623 if (drag_info.brushing) {
3624 /* all changes were made during motion event handlers */
3626 if (drag_info.copy) {
3627 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3628 copies.push_back (*i);
3635 /* adjust for track speed */
3638 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3639 if (atv && atv->get_diskstream()) {
3640 speed = atv->get_diskstream()->speed();
3643 regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3644 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3646 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3647 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3651 if (drag_info.copy) {
3652 if (drag_info.x_constrained) {
3653 op_string = _("fixed time region copy");
3655 op_string = _("region copy");
3658 if (drag_info.x_constrained) {
3659 op_string = _("fixed time region drag");
3661 op_string = _("region drag");
3665 begin_reversible_command (op_string);
3667 if (regionview_y_movement) {
3669 /* moved to a different audio track. */
3671 vector<RegionView*> new_selection;
3673 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3675 RegionView* rv = (*i);
3677 double ix1, ix2, iy1, iy2;
3679 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3680 rv->get_canvas_group()->i2w (ix1, iy1);
3681 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3682 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3684 boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3685 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3687 where = (nframes_t) (unit_to_frame (ix1) * speed);
3688 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3690 /* undo the previous hide_dependent_views so that xfades don't
3691 disappear on copying regions
3694 rv->get_time_axis_view().reveal_dependent_views (*rv);
3696 if (!drag_info.copy) {
3698 /* the region that used to be in the old playlist is not
3699 moved to the new one - we make a copy of it. as a result,
3700 any existing editor for the region should no longer be
3704 rv->hide_region_editor();
3705 rv->fake_set_opaque (false);
3707 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3708 from_playlist->remove_region ((rv->region()));
3709 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3713 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3715 copies.push_back (rv);
3718 latest_regionviews.clear ();
3720 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3721 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3722 to_playlist->add_region (new_region, where);
3723 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3726 if (!latest_regionviews.empty()) {
3727 new_selection.insert (new_selection.end(), latest_regionviews.begin(), latest_regionviews.end());
3730 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3731 was selected in all of them, then removing it from the playlist will have removed all
3732 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3733 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3734 corresponding regionview, and the selection is now empty).
3736 this could have invalidated any and all iterators into the region selection.
3738 the heuristic we use here is: if the region selection is empty, break out of the loop
3739 here. if the region selection is not empty, then restart the loop because we know that
3740 we must have removed at least the region(view) we've just been working on as well as any
3741 that we processed on previous iterations.
3743 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3744 we can just iterate.
3747 if (drag_info.copy) {
3750 if (selection->regions.empty()) {
3753 i = selection->regions.by_layer().begin();
3758 selection->set (new_selection);
3762 /* motion within a single track */
3764 list<RegionView*> regions = selection->regions.by_layer();
3766 if (drag_info.copy) {
3767 selection->clear_regions();
3770 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3774 if (rv->region()->locked()) {
3779 if (regionview_x_movement) {
3780 double ownspeed = 1.0;
3781 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3783 if (atv && atv->get_diskstream()) {
3784 ownspeed = atv->get_diskstream()->speed();
3787 /* base the new region position on the current position of the regionview.*/
3789 double ix1, ix2, iy1, iy2;
3791 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3792 rv->get_canvas_group()->i2w (ix1, iy1);
3793 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3797 where = rv->region()->position();
3800 boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3802 assert (to_playlist);
3806 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3808 if (drag_info.copy) {
3810 boost::shared_ptr<Region> newregion;
3811 boost::shared_ptr<Region> ar;
3813 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3814 newregion = RegionFactory::create (ar);
3816 /* XXX MIDI HERE drobilla */
3822 latest_regionviews.clear ();
3823 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3824 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3827 if (!latest_regionviews.empty()) {
3828 // XXX why just the first one ? we only expect one
3829 atv->reveal_dependent_views (*latest_regionviews.front());
3830 selection->add (latest_regionviews);
3833 /* if the original region was locked, we don't care for the new one */
3835 newregion->set_locked (false);
3839 /* just change the model */
3841 rv->region()->set_position (where, (void*) this);
3847 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3849 if (drag_info.copy) {
3850 copies.push_back (rv);
3858 commit_reversible_command ();
3861 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3867 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3869 /* Either add to or set the set the region selection, unless
3870 this is an alignment click (control used)
3873 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
3874 TimeAxisView* tv = &rv.get_time_axis_view();
3875 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3877 if (atv && atv->is_audio_track()) {
3878 speed = atv->get_diskstream()->speed();
3881 nframes64_t where = get_preferred_edit_position();
3885 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
3887 align_region (rv.region(), SyncPoint, (nframes_t) (where * speed));
3889 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
3891 align_region (rv.region(), End, (nframes_t) (where * speed));
3895 align_region (rv.region(), Start, (nframes_t) (where * speed));
3902 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3908 nframes_t frame_rate;
3915 switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3916 case AudioClock::BBT:
3917 session->bbt_time (frame, bbt);
3918 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3921 case AudioClock::SMPTE:
3922 session->smpte_time (frame, smpte);
3923 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3926 case AudioClock::MinSec:
3927 /* XXX this is copied from show_verbose_duration_cursor() */
3928 frame_rate = session->frame_rate();
3929 hours = frame / (frame_rate * 3600);
3930 frame = frame % (frame_rate * 3600);
3931 mins = frame / (frame_rate * 60);
3932 frame = frame % (frame_rate * 60);
3933 secs = (float) frame / (float) frame_rate;
3934 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3938 snprintf (buf, sizeof(buf), "%u", frame);
3942 if (xpos >= 0 && ypos >=0) {
3943 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3946 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3948 show_verbose_canvas_cursor ();
3952 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3959 nframes_t distance, frame_rate;
3961 Meter meter_at_start(session->tempo_map().meter_at(start));
3967 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3968 case AudioClock::BBT:
3969 session->bbt_time (start, sbbt);
3970 session->bbt_time (end, ebbt);
3973 /* XXX this computation won't work well if the
3974 user makes a selection that spans any meter changes.
3977 ebbt.bars -= sbbt.bars;
3978 if (ebbt.beats >= sbbt.beats) {
3979 ebbt.beats -= sbbt.beats;
3982 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3984 if (ebbt.ticks >= sbbt.ticks) {
3985 ebbt.ticks -= sbbt.ticks;
3988 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3991 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3994 case AudioClock::SMPTE:
3995 session->smpte_duration (end - start, smpte);
3996 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3999 case AudioClock::MinSec:
4000 /* XXX this stuff should be elsewhere.. */
4001 distance = end - start;
4002 frame_rate = session->frame_rate();
4003 hours = distance / (frame_rate * 3600);
4004 distance = distance % (frame_rate * 3600);
4005 mins = distance / (frame_rate * 60);
4006 distance = distance % (frame_rate * 60);
4007 secs = (float) distance / (float) frame_rate;
4008 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4012 snprintf (buf, sizeof(buf), "%u", end - start);
4016 if (xpos >= 0 && ypos >=0) {
4017 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4020 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4022 show_verbose_canvas_cursor ();
4026 Editor::collect_new_region_view (RegionView* rv)
4028 latest_regionviews.push_back (rv);
4032 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4034 if (clicked_regionview == 0) {
4038 /* lets try to create new Region for the selection */
4040 vector<boost::shared_ptr<AudioRegion> > new_regions;
4041 create_region_from_selection (new_regions);
4043 if (new_regions.empty()) {
4047 /* XXX fix me one day to use all new regions */
4049 boost::shared_ptr<Region> region (new_regions.front());
4051 /* add it to the current stream/playlist.
4053 tricky: the streamview for the track will add a new regionview. we will
4054 catch the signal it sends when it creates the regionview to
4055 set the regionview we want to then drag.
4058 latest_regionviews.clear();
4059 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4061 /* A selection grab currently creates two undo/redo operations, one for
4062 creating the new region and another for moving it.
4065 begin_reversible_command (_("selection grab"));
4067 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
4069 XMLNode *before = &(playlist->get_state());
4070 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
4071 XMLNode *after = &(playlist->get_state());
4072 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4074 commit_reversible_command ();
4078 if (latest_regionviews.empty()) {
4079 /* something went wrong */
4083 /* we need to deselect all other regionviews, and select this one
4084 i'm ignoring undo stuff, because the region creation will take care of it
4086 selection->set (latest_regionviews);
4088 drag_info.item = latest_regionviews.front()->get_canvas_group();
4089 drag_info.data = latest_regionviews.front();
4090 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4091 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4095 drag_info.last_trackview = clicked_trackview;
4096 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4097 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4099 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4103 Editor::cancel_selection ()
4105 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4106 (*i)->hide_selection ();
4108 selection->clear ();
4109 clicked_selection = 0;
4113 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4115 nframes_t start = 0;
4122 drag_info.item = item;
4123 drag_info.motion_callback = &Editor::drag_selection;
4124 drag_info.finished_callback = &Editor::end_selection_op;
4129 case CreateSelection:
4130 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4131 drag_info.copy = true;
4133 drag_info.copy = false;
4135 start_grab (event, selector_cursor);
4138 case SelectionStartTrim:
4139 if (clicked_trackview) {
4140 clicked_trackview->order_selection_trims (item, true);
4142 start_grab (event, trimmer_cursor);
4143 start = selection->time[clicked_selection].start;
4144 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4147 case SelectionEndTrim:
4148 if (clicked_trackview) {
4149 clicked_trackview->order_selection_trims (item, false);
4151 start_grab (event, trimmer_cursor);
4152 end = selection->time[clicked_selection].end;
4153 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4157 start = selection->time[clicked_selection].start;
4159 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4163 if (selection_op == SelectionMove) {
4164 show_verbose_time_cursor(start, 10);
4166 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4171 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4173 nframes_t start = 0;
4176 nframes_t pending_position;
4178 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4179 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4181 pending_position = 0;
4184 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4185 snap_to (pending_position);
4188 /* only alter selection if the current frame is
4189 different from the last frame position (adjusted)
4192 if (pending_position == drag_info.last_pointer_frame) return;
4194 switch (selection_op) {
4195 case CreateSelection:
4197 if (drag_info.first_move) {
4198 snap_to (drag_info.grab_frame);
4201 if (pending_position < drag_info.grab_frame) {
4202 start = pending_position;
4203 end = drag_info.grab_frame;
4205 end = pending_position;
4206 start = drag_info.grab_frame;
4209 /* first drag: Either add to the selection
4210 or create a new selection->
4213 if (drag_info.first_move) {
4215 begin_reversible_command (_("range selection"));
4217 if (drag_info.copy) {
4218 /* adding to the selection */
4219 clicked_selection = selection->add (start, end);
4220 drag_info.copy = false;
4222 /* new selection-> */
4223 clicked_selection = selection->set (clicked_trackview, start, end);
4228 case SelectionStartTrim:
4230 if (drag_info.first_move) {
4231 begin_reversible_command (_("trim selection start"));
4234 start = selection->time[clicked_selection].start;
4235 end = selection->time[clicked_selection].end;
4237 if (pending_position > end) {
4240 start = pending_position;
4244 case SelectionEndTrim:
4246 if (drag_info.first_move) {
4247 begin_reversible_command (_("trim selection end"));
4250 start = selection->time[clicked_selection].start;
4251 end = selection->time[clicked_selection].end;
4253 if (pending_position < start) {
4256 end = pending_position;
4263 if (drag_info.first_move) {
4264 begin_reversible_command (_("move selection"));
4267 start = selection->time[clicked_selection].start;
4268 end = selection->time[clicked_selection].end;
4270 length = end - start;
4272 start = pending_position;
4275 end = start + length;
4280 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4281 start_canvas_autoscroll (1);
4285 selection->replace (clicked_selection, start, end);
4288 drag_info.last_pointer_frame = pending_position;
4289 drag_info.first_move = false;
4291 if (selection_op == SelectionMove) {
4292 show_verbose_time_cursor(start, 10);
4294 show_verbose_time_cursor(pending_position, 10);
4299 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4301 if (!drag_info.first_move) {
4302 drag_selection (item, event);
4303 /* XXX this is not object-oriented programming at all. ick */
4304 if (selection->time.consolidate()) {
4305 selection->TimeChanged ();
4307 commit_reversible_command ();
4309 /* just a click, no pointer movement.*/
4311 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4313 selection->clear_time();
4318 /* XXX what happens if its a music selection? */
4319 session->set_audio_range (selection->time);
4320 stop_canvas_autoscroll ();
4324 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4327 TimeAxisView* tvp = clicked_trackview;
4328 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4330 if (tv && tv->is_audio_track()) {
4331 speed = tv->get_diskstream()->speed();
4334 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4335 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4336 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4338 //drag_info.item = clicked_regionview->get_name_highlight();
4339 drag_info.item = item;
4340 drag_info.motion_callback = &Editor::trim_motion_callback;
4341 drag_info.finished_callback = &Editor::trim_finished_callback;
4343 start_grab (event, trimmer_cursor);
4345 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4346 trim_op = ContentsTrim;
4348 /* These will get overridden for a point trim.*/
4349 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4350 /* closer to start */
4351 trim_op = StartTrim;
4352 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4360 show_verbose_time_cursor(region_start, 10);
4363 show_verbose_time_cursor(region_end, 10);
4366 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4372 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4374 RegionView* rv = clicked_regionview;
4375 nframes_t frame_delta = 0;
4376 bool left_direction;
4377 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4379 /* snap modifier works differently here..
4380 its' current state has to be passed to the
4381 various trim functions in order to work properly
4385 TimeAxisView* tvp = clicked_trackview;
4386 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4387 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4389 if (tv && tv->is_audio_track()) {
4390 speed = tv->get_diskstream()->speed();
4393 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4394 left_direction = true;
4396 left_direction = false;
4400 snap_to (drag_info.current_pointer_frame);
4403 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4407 if (drag_info.first_move) {
4413 trim_type = "Region start trim";
4416 trim_type = "Region end trim";
4419 trim_type = "Region content trim";
4423 begin_reversible_command (trim_type);
4425 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4426 (*i)->fake_set_opaque(false);
4427 (*i)->region()->freeze ();
4429 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4431 arv->temporarily_hide_envelope ();
4433 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4434 insert_result = motion_frozen_playlists.insert (pl);
4435 if (insert_result.second) {
4436 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4441 if (left_direction) {
4442 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4444 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4449 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4452 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4453 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4459 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4462 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4463 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4470 bool swap_direction = false;
4472 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4473 swap_direction = true;
4476 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4477 i != selection->regions.by_layer().end(); ++i)
4479 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4487 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4490 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4493 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4497 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4498 drag_info.first_move = false;
4502 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4504 boost::shared_ptr<Region> region (rv.region());
4506 if (region->locked()) {
4510 nframes_t new_bound;
4513 TimeAxisView* tvp = clicked_trackview;
4514 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4516 if (tv && tv->is_audio_track()) {
4517 speed = tv->get_diskstream()->speed();
4520 if (left_direction) {
4521 if (swap_direction) {
4522 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4524 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4527 if (swap_direction) {
4528 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4530 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4535 snap_to (new_bound);
4537 region->trim_start ((nframes_t) (new_bound * speed), this);
4538 rv.region_changed (StartChanged);
4542 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4544 boost::shared_ptr<Region> region (rv.region());
4546 if (region->locked()) {
4550 nframes_t new_bound;
4553 TimeAxisView* tvp = clicked_trackview;
4554 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4556 if (tv && tv->is_audio_track()) {
4557 speed = tv->get_diskstream()->speed();
4560 if (left_direction) {
4561 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4563 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4567 snap_to (new_bound, (left_direction ? 0 : 1));
4570 region->trim_front ((nframes_t) (new_bound * speed), this);
4572 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4576 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4578 boost::shared_ptr<Region> region (rv.region());
4580 if (region->locked()) {
4584 nframes_t new_bound;
4587 TimeAxisView* tvp = clicked_trackview;
4588 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4590 if (tv && tv->is_audio_track()) {
4591 speed = tv->get_diskstream()->speed();
4594 if (left_direction) {
4595 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4597 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4601 snap_to (new_bound);
4603 region->trim_end ((nframes_t) (new_bound * speed), this);
4604 rv.region_changed (LengthChanged);
4608 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4610 if (!drag_info.first_move) {
4611 trim_motion_callback (item, event);
4613 if (!selection->selected (clicked_regionview)) {
4614 thaw_region_after_trim (*clicked_regionview);
4617 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4618 i != selection->regions.by_layer().end(); ++i)
4620 thaw_region_after_trim (**i);
4621 (*i)->fake_set_opaque (true);
4625 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4627 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4630 motion_frozen_playlists.clear ();
4632 commit_reversible_command();
4634 /* no mouse movement */
4640 Editor::point_trim (GdkEvent* event)
4642 RegionView* rv = clicked_regionview;
4643 nframes_t new_bound = drag_info.current_pointer_frame;
4645 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4646 snap_to (new_bound);
4649 /* Choose action dependant on which button was pressed */
4650 switch (event->button.button) {
4652 trim_op = StartTrim;
4653 begin_reversible_command (_("Start point trim"));
4655 if (selection->selected (rv)) {
4657 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4658 i != selection->regions.by_layer().end(); ++i)
4660 if (!(*i)->region()->locked()) {
4661 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4662 XMLNode &before = pl->get_state();
4663 (*i)->region()->trim_front (new_bound, this);
4664 XMLNode &after = pl->get_state();
4665 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4671 if (!rv->region()->locked()) {
4672 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4673 XMLNode &before = pl->get_state();
4674 rv->region()->trim_front (new_bound, this);
4675 XMLNode &after = pl->get_state();
4676 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4680 commit_reversible_command();
4685 begin_reversible_command (_("End point trim"));
4687 if (selection->selected (rv)) {
4689 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4691 if (!(*i)->region()->locked()) {
4692 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4693 XMLNode &before = pl->get_state();
4694 (*i)->region()->trim_end (new_bound, this);
4695 XMLNode &after = pl->get_state();
4696 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4702 if (!rv->region()->locked()) {
4703 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4704 XMLNode &before = pl->get_state();
4705 rv->region()->trim_end (new_bound, this);
4706 XMLNode &after = pl->get_state();
4707 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4711 commit_reversible_command();
4720 Editor::thaw_region_after_trim (RegionView& rv)
4722 boost::shared_ptr<Region> region (rv.region());
4724 if (region->locked()) {
4728 region->thaw (_("trimmed region"));
4729 XMLNode &after = region->playlist()->get_state();
4730 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4732 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4734 arv->unhide_envelope ();
4738 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4743 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4744 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4748 Location* location = find_location_from_marker (marker, is_start);
4749 location->set_hidden (true, this);
4754 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4760 drag_info.item = item;
4761 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4762 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4764 range_marker_op = op;
4766 if (!temp_location) {
4767 temp_location = new Location;
4771 case CreateRangeMarker:
4772 case CreateTransportMarker:
4773 case CreateCDMarker:
4775 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4776 drag_info.copy = true;
4778 drag_info.copy = false;
4780 start_grab (event, selector_cursor);
4784 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4789 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4791 nframes_t start = 0;
4793 ArdourCanvas::SimpleRect *crect;
4795 switch (range_marker_op) {
4796 case CreateRangeMarker:
4797 crect = range_bar_drag_rect;
4799 case CreateTransportMarker:
4800 crect = transport_bar_drag_rect;
4802 case CreateCDMarker:
4803 crect = cd_marker_bar_drag_rect;
4806 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
4811 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4812 snap_to (drag_info.current_pointer_frame);
4815 /* only alter selection if the current frame is
4816 different from the last frame position.
4819 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4821 switch (range_marker_op) {
4822 case CreateRangeMarker:
4823 case CreateTransportMarker:
4824 case CreateCDMarker:
4825 if (drag_info.first_move) {
4826 snap_to (drag_info.grab_frame);
4829 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4830 start = drag_info.current_pointer_frame;
4831 end = drag_info.grab_frame;
4833 end = drag_info.current_pointer_frame;
4834 start = drag_info.grab_frame;
4837 /* first drag: Either add to the selection
4838 or create a new selection.
4841 if (drag_info.first_move) {
4843 temp_location->set (start, end);
4847 update_marker_drag_item (temp_location);
4848 range_marker_drag_rect->show();
4849 range_marker_drag_rect->raise_to_top();
4855 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4856 start_canvas_autoscroll (1);
4860 temp_location->set (start, end);
4862 double x1 = frame_to_pixel (start);
4863 double x2 = frame_to_pixel (end);
4864 crect->property_x1() = x1;
4865 crect->property_x2() = x2;
4867 update_marker_drag_item (temp_location);
4870 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4871 drag_info.first_move = false;
4873 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4878 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4880 Location * newloc = 0;
4884 if (!drag_info.first_move) {
4885 drag_range_markerbar_op (item, event);
4887 switch (range_marker_op) {
4888 case CreateRangeMarker:
4889 case CreateCDMarker:
4891 begin_reversible_command (_("new range marker"));
4892 XMLNode &before = session->locations()->get_state();
4893 session->locations()->next_available_name(rangename,"unnamed");
4894 if (range_marker_op == CreateCDMarker) {
4895 flags = Location::IsRangeMarker|Location::IsCDMarker;
4896 cd_marker_bar_drag_rect->hide();
4899 flags = Location::IsRangeMarker;
4900 range_bar_drag_rect->hide();
4902 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
4903 session->locations()->add (newloc, true);
4904 XMLNode &after = session->locations()->get_state();
4905 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4906 commit_reversible_command ();
4908 range_marker_drag_rect->hide();
4912 case CreateTransportMarker:
4913 // popup menu to pick loop or punch
4914 new_transport_marker_context_menu (&event->button, item);
4919 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4921 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
4926 start = session->locations()->first_mark_before (drag_info.grab_frame);
4927 end = session->locations()->first_mark_after (drag_info.grab_frame);
4929 if (end == max_frames) {
4930 end = session->current_end_frame ();
4934 start = session->current_start_frame ();
4937 switch (mouse_mode) {
4939 /* find the two markers on either side and then make the selection from it */
4940 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4944 /* find the two markers on either side of the click and make the range out of it */
4945 selection->set (0, start, end);
4954 stop_canvas_autoscroll ();
4960 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4962 drag_info.item = item;
4963 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4964 drag_info.finished_callback = &Editor::end_mouse_zoom;
4966 start_grab (event, zoom_cursor);
4968 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4972 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4977 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4978 snap_to (drag_info.current_pointer_frame);
4980 if (drag_info.first_move) {
4981 snap_to (drag_info.grab_frame);
4985 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4987 /* base start and end on initial click position */
4988 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4989 start = drag_info.current_pointer_frame;
4990 end = drag_info.grab_frame;
4992 end = drag_info.current_pointer_frame;
4993 start = drag_info.grab_frame;
4998 if (drag_info.first_move) {
5000 zoom_rect->raise_to_top();
5003 reposition_zoom_rect(start, end);
5005 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5006 drag_info.first_move = false;
5008 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5013 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5015 if (!drag_info.first_move) {
5016 drag_mouse_zoom (item, event);
5018 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5019 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5021 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5024 temporal_zoom_to_frame (false, drag_info.grab_frame);
5026 temporal_zoom_step (false);
5027 center_screen (drag_info.grab_frame);
5035 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
5037 double x1 = frame_to_pixel (start);
5038 double x2 = frame_to_pixel (end);
5039 double y2 = full_canvas_height - 1.0;
5041 zoom_rect->property_x1() = x1;
5042 zoom_rect->property_y1() = 1.0;
5043 zoom_rect->property_x2() = x2;
5044 zoom_rect->property_y2() = y2;
5048 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5050 drag_info.item = item;
5051 drag_info.motion_callback = &Editor::drag_rubberband_select;
5052 drag_info.finished_callback = &Editor::end_rubberband_select;
5054 start_grab (event, cross_hair_cursor);
5056 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5060 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5067 /* use a bigger drag threshold than the default */
5069 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5073 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5074 if (drag_info.first_move) {
5075 snap_to (drag_info.grab_frame);
5077 snap_to (drag_info.current_pointer_frame);
5080 /* base start and end on initial click position */
5082 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5083 start = drag_info.current_pointer_frame;
5084 end = drag_info.grab_frame;
5086 end = drag_info.current_pointer_frame;
5087 start = drag_info.grab_frame;
5090 if (drag_info.current_pointer_y < drag_info.grab_y) {
5091 y1 = drag_info.current_pointer_y;
5092 y2 = drag_info.grab_y;
5094 y2 = drag_info.current_pointer_y;
5095 y1 = drag_info.grab_y;
5099 if (start != end || y1 != y2) {
5101 double x1 = frame_to_pixel (start);
5102 double x2 = frame_to_pixel (end);
5104 rubberband_rect->property_x1() = x1;
5105 rubberband_rect->property_y1() = y1;
5106 rubberband_rect->property_x2() = x2;
5107 rubberband_rect->property_y2() = y2;
5109 rubberband_rect->show();
5110 rubberband_rect->raise_to_top();
5112 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5113 drag_info.first_move = false;
5115 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5120 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5122 if (!drag_info.first_move) {
5124 drag_rubberband_select (item, event);
5127 if (drag_info.current_pointer_y < drag_info.grab_y) {
5128 y1 = drag_info.current_pointer_y;
5129 y2 = drag_info.grab_y;
5132 y2 = drag_info.current_pointer_y;
5133 y1 = drag_info.grab_y;
5137 Selection::Operation op = Keyboard::selection_type (event->button.state);
5140 begin_reversible_command (_("rubberband selection"));
5142 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5143 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5145 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5149 commit_reversible_command ();
5153 selection->clear_tracks();
5154 selection->clear_regions();
5155 selection->clear_points ();
5156 selection->clear_lines ();
5159 rubberband_rect->hide();
5164 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5166 using namespace Gtkmm2ext;
5168 ArdourPrompter prompter (false);
5170 prompter.set_prompt (_("Name for region:"));
5171 prompter.set_initial_text (clicked_regionview->region()->name());
5172 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5173 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5174 prompter.show_all ();
5175 switch (prompter.run ()) {
5176 case Gtk::RESPONSE_ACCEPT:
5178 prompter.get_result(str);
5180 clicked_regionview->region()->set_name (str);
5188 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5190 drag_info.item = item;
5191 drag_info.motion_callback = &Editor::time_fx_motion;
5192 drag_info.finished_callback = &Editor::end_time_fx;
5196 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5200 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5202 RegionView* rv = clicked_regionview;
5204 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5205 snap_to (drag_info.current_pointer_frame);
5208 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5212 if (drag_info.current_pointer_frame > rv->region()->position()) {
5213 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5216 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5217 drag_info.first_move = false;
5219 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5223 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5225 clicked_regionview->get_time_axis_view().hide_timestretch ();
5227 if (drag_info.first_move) {
5231 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5232 /* backwards drag of the left edge - not usable */
5236 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5237 #ifdef USE_RUBBERBAND
5238 float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5240 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5243 begin_reversible_command (_("timestretch"));
5245 // XXX how do timeFX on multiple regions ?
5248 rs.add (clicked_regionview);
5250 if (time_stretch (rs, percentage) == 0) {
5251 session->commit_reversible_command ();
5256 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5258 /* no brushing without a useful snap setting */
5261 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5264 switch (snap_mode) {
5266 return; /* can't work because it allows region to be placed anywhere */
5271 switch (snap_type) {
5279 /* don't brush a copy over the original */
5281 if (pos == rv->region()->position()) {
5285 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5287 if (atv == 0 || !atv->is_audio_track()) {
5291 boost::shared_ptr<Playlist> playlist = atv->playlist();
5292 double speed = atv->get_diskstream()->speed();
5294 XMLNode &before = playlist->get_state();
5295 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5296 XMLNode &after = playlist->get_state();
5297 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5299 // playlist is frozen, so we have to update manually
5301 playlist->Modified(); /* EMIT SIGNAL */
5305 Editor::track_height_step_timeout ()
5308 struct timeval delta;
5310 gettimeofday (&now, 0);
5311 timersub (&now, &last_track_height_step_timestamp, &delta);
5313 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5314 current_stepping_trackview = 0;