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;
3917 if (Profile->get_sae() || Profile->get_small_screen()) {
3918 m = ARDOUR_UI::instance()->primary_clock.mode();
3920 m = ARDOUR_UI::instance()->secondary_clock.mode();
3924 case AudioClock::BBT:
3925 session->bbt_time (frame, bbt);
3926 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3929 case AudioClock::SMPTE:
3930 session->smpte_time (frame, smpte);
3931 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3934 case AudioClock::MinSec:
3935 /* XXX this is copied from show_verbose_duration_cursor() */
3936 frame_rate = session->frame_rate();
3937 hours = frame / (frame_rate * 3600);
3938 frame = frame % (frame_rate * 3600);
3939 mins = frame / (frame_rate * 60);
3940 frame = frame % (frame_rate * 60);
3941 secs = (float) frame / (float) frame_rate;
3942 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3946 snprintf (buf, sizeof(buf), "%u", frame);
3950 if (xpos >= 0 && ypos >=0) {
3951 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3954 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3956 show_verbose_canvas_cursor ();
3960 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3967 nframes_t distance, frame_rate;
3969 Meter meter_at_start(session->tempo_map().meter_at(start));
3977 if (Profile->get_sae() || Profile->get_small_screen()) {
3978 m = ARDOUR_UI::instance()->primary_clock.mode ();
3980 m = ARDOUR_UI::instance()->secondary_clock.mode ();
3984 case AudioClock::BBT:
3985 session->bbt_time (start, sbbt);
3986 session->bbt_time (end, ebbt);
3989 /* XXX this computation won't work well if the
3990 user makes a selection that spans any meter changes.
3993 ebbt.bars -= sbbt.bars;
3994 if (ebbt.beats >= sbbt.beats) {
3995 ebbt.beats -= sbbt.beats;
3998 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4000 if (ebbt.ticks >= sbbt.ticks) {
4001 ebbt.ticks -= sbbt.ticks;
4004 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4007 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4010 case AudioClock::SMPTE:
4011 session->smpte_duration (end - start, smpte);
4012 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4015 case AudioClock::MinSec:
4016 /* XXX this stuff should be elsewhere.. */
4017 distance = end - start;
4018 frame_rate = session->frame_rate();
4019 hours = distance / (frame_rate * 3600);
4020 distance = distance % (frame_rate * 3600);
4021 mins = distance / (frame_rate * 60);
4022 distance = distance % (frame_rate * 60);
4023 secs = (float) distance / (float) frame_rate;
4024 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4028 snprintf (buf, sizeof(buf), "%u", end - start);
4032 if (xpos >= 0 && ypos >=0) {
4033 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4036 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4039 show_verbose_canvas_cursor ();
4043 Editor::collect_new_region_view (RegionView* rv)
4045 latest_regionviews.push_back (rv);
4049 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4051 if (clicked_regionview == 0) {
4055 /* lets try to create new Region for the selection */
4057 vector<boost::shared_ptr<AudioRegion> > new_regions;
4058 create_region_from_selection (new_regions);
4060 if (new_regions.empty()) {
4064 /* XXX fix me one day to use all new regions */
4066 boost::shared_ptr<Region> region (new_regions.front());
4068 /* add it to the current stream/playlist.
4070 tricky: the streamview for the track will add a new regionview. we will
4071 catch the signal it sends when it creates the regionview to
4072 set the regionview we want to then drag.
4075 latest_regionviews.clear();
4076 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4078 /* A selection grab currently creates two undo/redo operations, one for
4079 creating the new region and another for moving it.
4082 begin_reversible_command (_("selection grab"));
4084 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
4086 XMLNode *before = &(playlist->get_state());
4087 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
4088 XMLNode *after = &(playlist->get_state());
4089 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4091 commit_reversible_command ();
4095 if (latest_regionviews.empty()) {
4096 /* something went wrong */
4100 /* we need to deselect all other regionviews, and select this one
4101 i'm ignoring undo stuff, because the region creation will take care of it
4103 selection->set (latest_regionviews);
4105 drag_info.item = latest_regionviews.front()->get_canvas_group();
4106 drag_info.data = latest_regionviews.front();
4107 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4108 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4112 drag_info.last_trackview = clicked_trackview;
4113 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4114 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4116 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4120 Editor::cancel_selection ()
4122 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4123 (*i)->hide_selection ();
4125 selection->clear ();
4126 clicked_selection = 0;
4130 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4132 nframes_t start = 0;
4139 drag_info.item = item;
4140 drag_info.motion_callback = &Editor::drag_selection;
4141 drag_info.finished_callback = &Editor::end_selection_op;
4146 case CreateSelection:
4147 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4148 drag_info.copy = true;
4150 drag_info.copy = false;
4152 start_grab (event, selector_cursor);
4155 case SelectionStartTrim:
4156 if (clicked_trackview) {
4157 clicked_trackview->order_selection_trims (item, true);
4159 start_grab (event, trimmer_cursor);
4160 start = selection->time[clicked_selection].start;
4161 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4164 case SelectionEndTrim:
4165 if (clicked_trackview) {
4166 clicked_trackview->order_selection_trims (item, false);
4168 start_grab (event, trimmer_cursor);
4169 end = selection->time[clicked_selection].end;
4170 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4174 start = selection->time[clicked_selection].start;
4176 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4180 if (selection_op == SelectionMove) {
4181 show_verbose_time_cursor(start, 10);
4183 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4188 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4190 nframes_t start = 0;
4193 nframes_t pending_position;
4195 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4196 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4198 pending_position = 0;
4201 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4202 snap_to (pending_position);
4205 /* only alter selection if the current frame is
4206 different from the last frame position (adjusted)
4209 if (pending_position == drag_info.last_pointer_frame) return;
4211 switch (selection_op) {
4212 case CreateSelection:
4214 if (drag_info.first_move) {
4215 snap_to (drag_info.grab_frame);
4218 if (pending_position < drag_info.grab_frame) {
4219 start = pending_position;
4220 end = drag_info.grab_frame;
4222 end = pending_position;
4223 start = drag_info.grab_frame;
4226 /* first drag: Either add to the selection
4227 or create a new selection->
4230 if (drag_info.first_move) {
4232 begin_reversible_command (_("range selection"));
4234 if (drag_info.copy) {
4235 /* adding to the selection */
4236 clicked_selection = selection->add (start, end);
4237 drag_info.copy = false;
4239 /* new selection-> */
4240 clicked_selection = selection->set (clicked_trackview, start, end);
4245 case SelectionStartTrim:
4247 if (drag_info.first_move) {
4248 begin_reversible_command (_("trim selection start"));
4251 start = selection->time[clicked_selection].start;
4252 end = selection->time[clicked_selection].end;
4254 if (pending_position > end) {
4257 start = pending_position;
4261 case SelectionEndTrim:
4263 if (drag_info.first_move) {
4264 begin_reversible_command (_("trim selection end"));
4267 start = selection->time[clicked_selection].start;
4268 end = selection->time[clicked_selection].end;
4270 if (pending_position < start) {
4273 end = pending_position;
4280 if (drag_info.first_move) {
4281 begin_reversible_command (_("move selection"));
4284 start = selection->time[clicked_selection].start;
4285 end = selection->time[clicked_selection].end;
4287 length = end - start;
4289 start = pending_position;
4292 end = start + length;
4297 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4298 start_canvas_autoscroll (1);
4302 selection->replace (clicked_selection, start, end);
4305 drag_info.last_pointer_frame = pending_position;
4306 drag_info.first_move = false;
4308 if (selection_op == SelectionMove) {
4309 show_verbose_time_cursor(start, 10);
4311 show_verbose_time_cursor(pending_position, 10);
4316 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4318 if (!drag_info.first_move) {
4319 drag_selection (item, event);
4320 /* XXX this is not object-oriented programming at all. ick */
4321 if (selection->time.consolidate()) {
4322 selection->TimeChanged ();
4324 commit_reversible_command ();
4326 /* just a click, no pointer movement.*/
4328 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4330 selection->clear_time();
4335 /* XXX what happens if its a music selection? */
4336 session->set_audio_range (selection->time);
4337 stop_canvas_autoscroll ();
4341 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4344 TimeAxisView* tvp = clicked_trackview;
4345 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4347 if (tv && tv->is_audio_track()) {
4348 speed = tv->get_diskstream()->speed();
4351 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4352 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4353 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4355 //drag_info.item = clicked_regionview->get_name_highlight();
4356 drag_info.item = item;
4357 drag_info.motion_callback = &Editor::trim_motion_callback;
4358 drag_info.finished_callback = &Editor::trim_finished_callback;
4360 start_grab (event, trimmer_cursor);
4362 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4363 trim_op = ContentsTrim;
4365 /* These will get overridden for a point trim.*/
4366 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4367 /* closer to start */
4368 trim_op = StartTrim;
4369 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4377 show_verbose_time_cursor(region_start, 10);
4380 show_verbose_time_cursor(region_end, 10);
4383 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4389 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4391 RegionView* rv = clicked_regionview;
4392 nframes_t frame_delta = 0;
4393 bool left_direction;
4394 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4396 /* snap modifier works differently here..
4397 its' current state has to be passed to the
4398 various trim functions in order to work properly
4402 TimeAxisView* tvp = clicked_trackview;
4403 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4404 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4406 if (tv && tv->is_audio_track()) {
4407 speed = tv->get_diskstream()->speed();
4410 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4411 left_direction = true;
4413 left_direction = false;
4417 snap_to (drag_info.current_pointer_frame);
4420 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4424 if (drag_info.first_move) {
4430 trim_type = "Region start trim";
4433 trim_type = "Region end trim";
4436 trim_type = "Region content trim";
4440 begin_reversible_command (trim_type);
4442 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4443 (*i)->fake_set_opaque(false);
4444 (*i)->region()->freeze ();
4446 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4448 arv->temporarily_hide_envelope ();
4450 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4451 insert_result = motion_frozen_playlists.insert (pl);
4452 if (insert_result.second) {
4453 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4458 if (left_direction) {
4459 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4461 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4466 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4469 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4470 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4476 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4479 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4480 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4487 bool swap_direction = false;
4489 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4490 swap_direction = true;
4493 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4494 i != selection->regions.by_layer().end(); ++i)
4496 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4504 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4507 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4510 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4514 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4515 drag_info.first_move = false;
4519 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4521 boost::shared_ptr<Region> region (rv.region());
4523 if (region->locked()) {
4527 nframes_t new_bound;
4530 TimeAxisView* tvp = clicked_trackview;
4531 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4533 if (tv && tv->is_audio_track()) {
4534 speed = tv->get_diskstream()->speed();
4537 if (left_direction) {
4538 if (swap_direction) {
4539 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4541 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4544 if (swap_direction) {
4545 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4547 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4552 snap_to (new_bound);
4554 region->trim_start ((nframes_t) (new_bound * speed), this);
4555 rv.region_changed (StartChanged);
4559 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4561 boost::shared_ptr<Region> region (rv.region());
4563 if (region->locked()) {
4567 nframes_t new_bound;
4570 TimeAxisView* tvp = clicked_trackview;
4571 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4573 if (tv && tv->is_audio_track()) {
4574 speed = tv->get_diskstream()->speed();
4577 if (left_direction) {
4578 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4580 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4584 snap_to (new_bound, (left_direction ? 0 : 1));
4587 region->trim_front ((nframes_t) (new_bound * speed), this);
4589 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4593 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4595 boost::shared_ptr<Region> region (rv.region());
4597 if (region->locked()) {
4601 nframes_t new_bound;
4604 TimeAxisView* tvp = clicked_trackview;
4605 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4607 if (tv && tv->is_audio_track()) {
4608 speed = tv->get_diskstream()->speed();
4611 if (left_direction) {
4612 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4614 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4618 snap_to (new_bound);
4620 region->trim_end ((nframes_t) (new_bound * speed), this);
4621 rv.region_changed (LengthChanged);
4625 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4627 if (!drag_info.first_move) {
4628 trim_motion_callback (item, event);
4630 if (!selection->selected (clicked_regionview)) {
4631 thaw_region_after_trim (*clicked_regionview);
4634 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4635 i != selection->regions.by_layer().end(); ++i)
4637 thaw_region_after_trim (**i);
4638 (*i)->fake_set_opaque (true);
4642 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4644 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4647 motion_frozen_playlists.clear ();
4649 commit_reversible_command();
4651 /* no mouse movement */
4657 Editor::point_trim (GdkEvent* event)
4659 RegionView* rv = clicked_regionview;
4660 nframes_t new_bound = drag_info.current_pointer_frame;
4662 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4663 snap_to (new_bound);
4666 /* Choose action dependant on which button was pressed */
4667 switch (event->button.button) {
4669 trim_op = StartTrim;
4670 begin_reversible_command (_("Start point trim"));
4672 if (selection->selected (rv)) {
4674 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4675 i != selection->regions.by_layer().end(); ++i)
4677 if (!(*i)->region()->locked()) {
4678 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4679 XMLNode &before = pl->get_state();
4680 (*i)->region()->trim_front (new_bound, this);
4681 XMLNode &after = pl->get_state();
4682 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4688 if (!rv->region()->locked()) {
4689 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4690 XMLNode &before = pl->get_state();
4691 rv->region()->trim_front (new_bound, this);
4692 XMLNode &after = pl->get_state();
4693 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4697 commit_reversible_command();
4702 begin_reversible_command (_("End point trim"));
4704 if (selection->selected (rv)) {
4706 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4708 if (!(*i)->region()->locked()) {
4709 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4710 XMLNode &before = pl->get_state();
4711 (*i)->region()->trim_end (new_bound, this);
4712 XMLNode &after = pl->get_state();
4713 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4719 if (!rv->region()->locked()) {
4720 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4721 XMLNode &before = pl->get_state();
4722 rv->region()->trim_end (new_bound, this);
4723 XMLNode &after = pl->get_state();
4724 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4728 commit_reversible_command();
4737 Editor::thaw_region_after_trim (RegionView& rv)
4739 boost::shared_ptr<Region> region (rv.region());
4741 if (region->locked()) {
4745 region->thaw (_("trimmed region"));
4746 XMLNode &after = region->playlist()->get_state();
4747 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4749 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4751 arv->unhide_envelope ();
4755 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4760 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4761 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4765 Location* location = find_location_from_marker (marker, is_start);
4766 location->set_hidden (true, this);
4771 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4777 drag_info.item = item;
4778 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4779 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4781 range_marker_op = op;
4783 if (!temp_location) {
4784 temp_location = new Location;
4788 case CreateRangeMarker:
4789 case CreateTransportMarker:
4790 case CreateCDMarker:
4792 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4793 drag_info.copy = true;
4795 drag_info.copy = false;
4797 start_grab (event, selector_cursor);
4801 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4806 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4808 nframes_t start = 0;
4810 ArdourCanvas::SimpleRect *crect;
4812 switch (range_marker_op) {
4813 case CreateRangeMarker:
4814 crect = range_bar_drag_rect;
4816 case CreateTransportMarker:
4817 crect = transport_bar_drag_rect;
4819 case CreateCDMarker:
4820 crect = cd_marker_bar_drag_rect;
4823 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
4828 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4829 snap_to (drag_info.current_pointer_frame);
4832 /* only alter selection if the current frame is
4833 different from the last frame position.
4836 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4838 switch (range_marker_op) {
4839 case CreateRangeMarker:
4840 case CreateTransportMarker:
4841 case CreateCDMarker:
4842 if (drag_info.first_move) {
4843 snap_to (drag_info.grab_frame);
4846 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4847 start = drag_info.current_pointer_frame;
4848 end = drag_info.grab_frame;
4850 end = drag_info.current_pointer_frame;
4851 start = drag_info.grab_frame;
4854 /* first drag: Either add to the selection
4855 or create a new selection.
4858 if (drag_info.first_move) {
4860 temp_location->set (start, end);
4864 update_marker_drag_item (temp_location);
4865 range_marker_drag_rect->show();
4866 range_marker_drag_rect->raise_to_top();
4872 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4873 start_canvas_autoscroll (1);
4877 temp_location->set (start, end);
4879 double x1 = frame_to_pixel (start);
4880 double x2 = frame_to_pixel (end);
4881 crect->property_x1() = x1;
4882 crect->property_x2() = x2;
4884 update_marker_drag_item (temp_location);
4887 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4888 drag_info.first_move = false;
4890 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4895 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4897 Location * newloc = 0;
4901 if (!drag_info.first_move) {
4902 drag_range_markerbar_op (item, event);
4904 switch (range_marker_op) {
4905 case CreateRangeMarker:
4906 case CreateCDMarker:
4908 begin_reversible_command (_("new range marker"));
4909 XMLNode &before = session->locations()->get_state();
4910 session->locations()->next_available_name(rangename,"unnamed");
4911 if (range_marker_op == CreateCDMarker) {
4912 flags = Location::IsRangeMarker|Location::IsCDMarker;
4913 cd_marker_bar_drag_rect->hide();
4916 flags = Location::IsRangeMarker;
4917 range_bar_drag_rect->hide();
4919 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
4920 session->locations()->add (newloc, true);
4921 XMLNode &after = session->locations()->get_state();
4922 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4923 commit_reversible_command ();
4925 range_marker_drag_rect->hide();
4929 case CreateTransportMarker:
4930 // popup menu to pick loop or punch
4931 new_transport_marker_context_menu (&event->button, item);
4936 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4938 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
4943 start = session->locations()->first_mark_before (drag_info.grab_frame);
4944 end = session->locations()->first_mark_after (drag_info.grab_frame);
4946 if (end == max_frames) {
4947 end = session->current_end_frame ();
4951 start = session->current_start_frame ();
4954 switch (mouse_mode) {
4956 /* find the two markers on either side and then make the selection from it */
4957 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4961 /* find the two markers on either side of the click and make the range out of it */
4962 selection->set (0, start, end);
4971 stop_canvas_autoscroll ();
4977 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4979 drag_info.item = item;
4980 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4981 drag_info.finished_callback = &Editor::end_mouse_zoom;
4983 start_grab (event, zoom_cursor);
4985 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4989 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4994 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4995 snap_to (drag_info.current_pointer_frame);
4997 if (drag_info.first_move) {
4998 snap_to (drag_info.grab_frame);
5002 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5004 /* base start and end on initial click position */
5005 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5006 start = drag_info.current_pointer_frame;
5007 end = drag_info.grab_frame;
5009 end = drag_info.current_pointer_frame;
5010 start = drag_info.grab_frame;
5015 if (drag_info.first_move) {
5017 zoom_rect->raise_to_top();
5020 reposition_zoom_rect(start, end);
5022 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5023 drag_info.first_move = false;
5025 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5030 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5032 if (!drag_info.first_move) {
5033 drag_mouse_zoom (item, event);
5035 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5036 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5038 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5041 temporal_zoom_to_frame (false, drag_info.grab_frame);
5043 temporal_zoom_step (false);
5044 center_screen (drag_info.grab_frame);
5052 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
5054 double x1 = frame_to_pixel (start);
5055 double x2 = frame_to_pixel (end);
5056 double y2 = full_canvas_height - 1.0;
5058 zoom_rect->property_x1() = x1;
5059 zoom_rect->property_y1() = 1.0;
5060 zoom_rect->property_x2() = x2;
5061 zoom_rect->property_y2() = y2;
5065 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5067 drag_info.item = item;
5068 drag_info.motion_callback = &Editor::drag_rubberband_select;
5069 drag_info.finished_callback = &Editor::end_rubberband_select;
5071 start_grab (event, cross_hair_cursor);
5073 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5077 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5084 /* use a bigger drag threshold than the default */
5086 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5090 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5091 if (drag_info.first_move) {
5092 snap_to (drag_info.grab_frame);
5094 snap_to (drag_info.current_pointer_frame);
5097 /* base start and end on initial click position */
5099 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5100 start = drag_info.current_pointer_frame;
5101 end = drag_info.grab_frame;
5103 end = drag_info.current_pointer_frame;
5104 start = drag_info.grab_frame;
5107 if (drag_info.current_pointer_y < drag_info.grab_y) {
5108 y1 = drag_info.current_pointer_y;
5109 y2 = drag_info.grab_y;
5111 y2 = drag_info.current_pointer_y;
5112 y1 = drag_info.grab_y;
5116 if (start != end || y1 != y2) {
5118 double x1 = frame_to_pixel (start);
5119 double x2 = frame_to_pixel (end);
5121 rubberband_rect->property_x1() = x1;
5122 rubberband_rect->property_y1() = y1;
5123 rubberband_rect->property_x2() = x2;
5124 rubberband_rect->property_y2() = y2;
5126 rubberband_rect->show();
5127 rubberband_rect->raise_to_top();
5129 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5130 drag_info.first_move = false;
5132 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5137 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5139 if (!drag_info.first_move) {
5141 drag_rubberband_select (item, event);
5144 if (drag_info.current_pointer_y < drag_info.grab_y) {
5145 y1 = drag_info.current_pointer_y;
5146 y2 = drag_info.grab_y;
5149 y2 = drag_info.current_pointer_y;
5150 y1 = drag_info.grab_y;
5154 Selection::Operation op = Keyboard::selection_type (event->button.state);
5157 begin_reversible_command (_("rubberband selection"));
5159 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5160 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5162 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5166 commit_reversible_command ();
5170 selection->clear_tracks();
5171 selection->clear_regions();
5172 selection->clear_points ();
5173 selection->clear_lines ();
5176 rubberband_rect->hide();
5181 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5183 using namespace Gtkmm2ext;
5185 ArdourPrompter prompter (false);
5187 prompter.set_prompt (_("Name for region:"));
5188 prompter.set_initial_text (clicked_regionview->region()->name());
5189 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5190 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5191 prompter.show_all ();
5192 switch (prompter.run ()) {
5193 case Gtk::RESPONSE_ACCEPT:
5195 prompter.get_result(str);
5197 clicked_regionview->region()->set_name (str);
5205 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5207 drag_info.item = item;
5208 drag_info.motion_callback = &Editor::time_fx_motion;
5209 drag_info.finished_callback = &Editor::end_time_fx;
5213 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5217 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5219 RegionView* rv = clicked_regionview;
5221 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5222 snap_to (drag_info.current_pointer_frame);
5225 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5229 if (drag_info.current_pointer_frame > rv->region()->position()) {
5230 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5233 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5234 drag_info.first_move = false;
5236 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5240 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5242 clicked_regionview->get_time_axis_view().hide_timestretch ();
5244 if (drag_info.first_move) {
5248 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5249 /* backwards drag of the left edge - not usable */
5253 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5254 #ifdef USE_RUBBERBAND
5255 float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5257 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5260 begin_reversible_command (_("timestretch"));
5262 // XXX how do timeFX on multiple regions ?
5265 rs.add (clicked_regionview);
5267 if (time_stretch (rs, percentage) == 0) {
5268 session->commit_reversible_command ();
5273 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5275 /* no brushing without a useful snap setting */
5278 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5281 switch (snap_mode) {
5283 return; /* can't work because it allows region to be placed anywhere */
5288 switch (snap_type) {
5296 /* don't brush a copy over the original */
5298 if (pos == rv->region()->position()) {
5302 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5304 if (atv == 0 || !atv->is_audio_track()) {
5308 boost::shared_ptr<Playlist> playlist = atv->playlist();
5309 double speed = atv->get_diskstream()->speed();
5311 XMLNode &before = playlist->get_state();
5312 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5313 XMLNode &after = playlist->get_state();
5314 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5316 // playlist is frozen, so we have to update manually
5318 playlist->Modified(); /* EMIT SIGNAL */
5322 Editor::track_height_step_timeout ()
5325 struct timeval delta;
5327 gettimeofday (&now, 0);
5328 timersub (&now, &last_track_height_step_timestamp, &delta);
5330 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5331 current_stepping_trackview = 0;