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.dest_trackview = 0;
1850 drag_info.source_trackview = 0;
1851 drag_info.last_frame_position = 0;
1852 drag_info.grab_frame = 0;
1853 drag_info.last_pointer_frame = 0;
1854 drag_info.current_pointer_frame = 0;
1855 drag_info.brushing = false;
1857 if (drag_info.copied_location) {
1858 delete drag_info.copied_location;
1859 drag_info.copied_location = 0;
1866 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1868 drag_info.item = item;
1869 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1870 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1874 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1875 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1879 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1881 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1885 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1887 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1889 nframes_t fade_length;
1891 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1892 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1898 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1902 if (pos < (arv->region()->position() + 64)) {
1903 fade_length = 64; // this should be a minimum defined somewhere
1904 } else if (pos > arv->region()->last_frame()) {
1905 fade_length = arv->region()->length();
1907 fade_length = pos - arv->region()->position();
1909 /* mapover the region selection */
1911 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1913 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1919 tmp->reset_fade_in_shape_width (fade_length);
1922 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1924 drag_info.first_move = false;
1928 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1930 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1932 nframes_t fade_length;
1934 if (drag_info.first_move) return;
1936 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1937 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1942 if (pos < (arv->region()->position() + 64)) {
1943 fade_length = 64; // this should be a minimum defined somewhere
1944 } else if (pos > arv->region()->last_frame()) {
1945 fade_length = arv->region()->length();
1947 fade_length = pos - arv->region()->position();
1950 begin_reversible_command (_("change fade in length"));
1952 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1954 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1960 AutomationList& alist = tmp->audio_region()->fade_in();
1961 XMLNode &before = alist.get_state();
1963 tmp->audio_region()->set_fade_in_length (fade_length);
1964 tmp->audio_region()->set_fade_in_active (true);
1966 XMLNode &after = alist.get_state();
1967 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1970 commit_reversible_command ();
1974 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1976 drag_info.item = item;
1977 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1978 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1982 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1983 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1987 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1989 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1993 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1995 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1997 nframes_t fade_length;
1999 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2000 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2005 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2009 if (pos > (arv->region()->last_frame() - 64)) {
2010 fade_length = 64; // this should really be a minimum fade defined somewhere
2012 else if (pos < arv->region()->position()) {
2013 fade_length = arv->region()->length();
2016 fade_length = arv->region()->last_frame() - pos;
2019 /* mapover the region selection */
2021 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2023 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2029 tmp->reset_fade_out_shape_width (fade_length);
2032 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2034 drag_info.first_move = false;
2038 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2040 if (drag_info.first_move) return;
2042 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2044 nframes_t fade_length;
2046 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2047 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2053 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2057 if (pos > (arv->region()->last_frame() - 64)) {
2058 fade_length = 64; // this should really be a minimum fade defined somewhere
2060 else if (pos < arv->region()->position()) {
2061 fade_length = arv->region()->length();
2064 fade_length = arv->region()->last_frame() - pos;
2067 begin_reversible_command (_("change fade out length"));
2069 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2071 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2077 AutomationList& alist = tmp->audio_region()->fade_out();
2078 XMLNode &before = alist.get_state();
2080 tmp->audio_region()->set_fade_out_length (fade_length);
2081 tmp->audio_region()->set_fade_out_active (true);
2083 XMLNode &after = alist.get_state();
2084 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2087 commit_reversible_command ();
2091 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2093 drag_info.item = item;
2094 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2095 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2099 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2100 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2104 Cursor* cursor = (Cursor *) drag_info.data;
2106 if (cursor == playhead_cursor) {
2107 _dragging_playhead = true;
2109 if (session && drag_info.was_rolling) {
2110 session->request_stop ();
2113 if (session && session->is_auditioning()) {
2114 session->cancel_audition ();
2118 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2120 show_verbose_time_cursor (cursor->current_frame, 10);
2124 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2126 Cursor* cursor = (Cursor *) drag_info.data;
2127 nframes_t adjusted_frame;
2129 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2130 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2136 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2137 if (cursor == playhead_cursor) {
2138 snap_to (adjusted_frame);
2142 if (adjusted_frame == drag_info.last_pointer_frame) return;
2144 cursor->set_position (adjusted_frame);
2146 UpdateAllTransportClocks (cursor->current_frame);
2148 show_verbose_time_cursor (cursor->current_frame, 10);
2150 drag_info.last_pointer_frame = adjusted_frame;
2151 drag_info.first_move = false;
2155 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2157 if (drag_info.first_move) return;
2159 cursor_drag_motion_callback (item, event);
2161 _dragging_playhead = false;
2163 if (item == &playhead_cursor->canvas_item) {
2165 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2171 Editor::update_marker_drag_item (Location *location)
2173 double x1 = frame_to_pixel (location->start());
2174 double x2 = frame_to_pixel (location->end());
2176 if (location->is_mark()) {
2177 marker_drag_line_points.front().set_x(x1);
2178 marker_drag_line_points.back().set_x(x1);
2179 marker_drag_line->property_points() = marker_drag_line_points;
2182 range_marker_drag_rect->property_x1() = x1;
2183 range_marker_drag_rect->property_x2() = x2;
2188 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2192 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2193 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2199 Location *location = find_location_from_marker (marker, is_start);
2201 drag_info.item = item;
2202 drag_info.data = marker;
2203 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2204 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2208 _dragging_edit_point = true;
2210 drag_info.copied_location = new Location (*location);
2211 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2213 update_marker_drag_item (location);
2215 if (location->is_mark()) {
2216 // marker_drag_line->show();
2217 // marker_drag_line->raise_to_top();
2219 range_marker_drag_rect->show();
2220 range_marker_drag_rect->raise_to_top();
2224 show_verbose_time_cursor (location->start(), 10);
2226 show_verbose_time_cursor (location->end(), 10);
2229 Selection::Operation op = Keyboard::selection_type (event->button.state);
2232 case Selection::Toggle:
2233 selection->toggle (marker);
2235 case Selection::Set:
2236 selection->set (marker);
2238 case Selection::Extend:
2239 selection->add (marker);
2241 case Selection::Add:
2242 selection->add (marker);
2248 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2251 Marker* marker = (Marker *) drag_info.data;
2252 Location *real_location;
2253 Location *copy_location;
2255 bool move_both = false;
2258 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2259 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2264 nframes_t next = newframe;
2266 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2267 snap_to (newframe, 0, true);
2270 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2274 /* call this to find out if its the start or end */
2276 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2280 if (real_location->locked()) {
2284 /* use the copy that we're "dragging" around */
2286 copy_location = drag_info.copied_location;
2288 f_delta = copy_location->end() - copy_location->start();
2290 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2294 if (copy_location->is_mark()) {
2297 copy_location->set_start (newframe);
2301 if (is_start) { // start-of-range marker
2304 copy_location->set_start (newframe);
2305 copy_location->set_end (newframe + f_delta);
2306 } else if (newframe < copy_location->end()) {
2307 copy_location->set_start (newframe);
2309 snap_to (next, 1, true);
2310 copy_location->set_end (next);
2311 copy_location->set_start (newframe);
2314 } else { // end marker
2317 copy_location->set_end (newframe);
2318 copy_location->set_start (newframe - f_delta);
2319 } else if (newframe > copy_location->start()) {
2320 copy_location->set_end (newframe);
2322 } else if (newframe > 0) {
2323 snap_to (next, -1, true);
2324 copy_location->set_start (next);
2325 copy_location->set_end (newframe);
2330 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2331 drag_info.first_move = false;
2333 update_marker_drag_item (copy_location);
2335 LocationMarkers* lm = find_location_markers (real_location);
2336 lm->set_position (copy_location->start(), copy_location->end());
2337 edit_point_clock.set (copy_location->start());
2339 show_verbose_time_cursor (newframe, 10);
2343 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2345 if (drag_info.first_move) {
2346 marker_drag_motion_callback (item, event);
2350 _dragging_edit_point = false;
2352 Marker* marker = (Marker *) drag_info.data;
2355 begin_reversible_command ( _("move marker") );
2356 XMLNode &before = session->locations()->get_state();
2358 Location * location = find_location_from_marker (marker, is_start);
2362 if (location->locked()) {
2366 if (location->is_mark()) {
2367 location->set_start (drag_info.copied_location->start());
2369 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2373 XMLNode &after = session->locations()->get_state();
2374 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2375 commit_reversible_command ();
2377 marker_drag_line->hide();
2378 range_marker_drag_rect->hide();
2382 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2385 MeterMarker* meter_marker;
2387 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2388 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2392 meter_marker = dynamic_cast<MeterMarker*> (marker);
2394 MetricSection& section (meter_marker->meter());
2396 if (!section.movable()) {
2400 drag_info.item = item;
2401 drag_info.copy = false;
2402 drag_info.data = marker;
2403 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2404 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2408 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2410 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2414 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2417 MeterMarker* meter_marker;
2419 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2420 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2424 meter_marker = dynamic_cast<MeterMarker*> (marker);
2426 // create a dummy marker for visual representation of moving the copy.
2427 // The actual copying is not done before we reach the finish callback.
2429 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2430 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2431 *new MeterSection(meter_marker->meter()));
2433 drag_info.item = &new_marker->the_item();
2434 drag_info.copy = true;
2435 drag_info.data = new_marker;
2436 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2437 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2441 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2443 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2447 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2449 MeterMarker* marker = (MeterMarker *) drag_info.data;
2450 nframes_t adjusted_frame;
2452 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2453 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2459 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2460 snap_to (adjusted_frame);
2463 if (adjusted_frame == drag_info.last_pointer_frame) return;
2465 marker->set_position (adjusted_frame);
2468 drag_info.last_pointer_frame = adjusted_frame;
2469 drag_info.first_move = false;
2471 show_verbose_time_cursor (adjusted_frame, 10);
2475 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2477 if (drag_info.first_move) return;
2479 meter_marker_drag_motion_callback (drag_info.item, event);
2481 MeterMarker* marker = (MeterMarker *) drag_info.data;
2484 TempoMap& map (session->tempo_map());
2485 map.bbt_time (drag_info.last_pointer_frame, when);
2487 if (drag_info.copy == true) {
2488 begin_reversible_command (_("copy meter mark"));
2489 XMLNode &before = map.get_state();
2490 map.add_meter (marker->meter(), when);
2491 XMLNode &after = map.get_state();
2492 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2493 commit_reversible_command ();
2495 // delete the dummy marker we used for visual representation of copying.
2496 // a new visual marker will show up automatically.
2499 begin_reversible_command (_("move meter mark"));
2500 XMLNode &before = map.get_state();
2501 map.move_meter (marker->meter(), when);
2502 XMLNode &after = map.get_state();
2503 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2504 commit_reversible_command ();
2509 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2512 TempoMarker* tempo_marker;
2514 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2515 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2519 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2520 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2524 MetricSection& section (tempo_marker->tempo());
2526 if (!section.movable()) {
2530 drag_info.item = item;
2531 drag_info.copy = false;
2532 drag_info.data = marker;
2533 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2534 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2538 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2539 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2543 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2546 TempoMarker* tempo_marker;
2548 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2549 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2553 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2554 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2558 // create a dummy marker for visual representation of moving the copy.
2559 // The actual copying is not done before we reach the finish callback.
2561 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2562 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2563 *new TempoSection(tempo_marker->tempo()));
2565 drag_info.item = &new_marker->the_item();
2566 drag_info.copy = true;
2567 drag_info.data = new_marker;
2568 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2569 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2573 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2575 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2579 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2581 TempoMarker* marker = (TempoMarker *) drag_info.data;
2582 nframes_t adjusted_frame;
2584 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2585 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2591 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2592 snap_to (adjusted_frame);
2595 if (adjusted_frame == drag_info.last_pointer_frame) return;
2597 /* OK, we've moved far enough to make it worth actually move the thing. */
2599 marker->set_position (adjusted_frame);
2601 show_verbose_time_cursor (adjusted_frame, 10);
2603 drag_info.last_pointer_frame = adjusted_frame;
2604 drag_info.first_move = false;
2608 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2610 if (drag_info.first_move) return;
2612 tempo_marker_drag_motion_callback (drag_info.item, event);
2614 TempoMarker* marker = (TempoMarker *) drag_info.data;
2617 TempoMap& map (session->tempo_map());
2618 map.bbt_time (drag_info.last_pointer_frame, when);
2620 if (drag_info.copy == true) {
2621 begin_reversible_command (_("copy tempo mark"));
2622 XMLNode &before = map.get_state();
2623 map.add_tempo (marker->tempo(), when);
2624 XMLNode &after = map.get_state();
2625 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2626 commit_reversible_command ();
2628 // delete the dummy marker we used for visual representation of copying.
2629 // a new visual marker will show up automatically.
2632 begin_reversible_command (_("move tempo mark"));
2633 XMLNode &before = map.get_state();
2634 map.move_tempo (marker->tempo(), when);
2635 XMLNode &after = map.get_state();
2636 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2637 commit_reversible_command ();
2642 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2644 ControlPoint* control_point;
2646 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2647 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2651 // We shouldn't remove the first or last gain point
2652 if (control_point->line.is_last_point(*control_point) ||
2653 control_point->line.is_first_point(*control_point)) {
2657 control_point->line.remove_point (*control_point);
2661 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2663 ControlPoint* control_point;
2665 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2666 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2670 control_point->line.remove_point (*control_point);
2674 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2676 ControlPoint* control_point;
2678 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2679 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2683 drag_info.item = item;
2684 drag_info.data = control_point;
2685 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2686 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2688 start_grab (event, fader_cursor);
2690 // start the grab at the center of the control point so
2691 // the point doesn't 'jump' to the mouse after the first drag
2692 drag_info.grab_x = control_point->get_x();
2693 drag_info.grab_y = control_point->get_y();
2694 control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2695 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y,
2696 drag_info.grab_x, drag_info.grab_y);
2698 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2700 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2702 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2703 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2704 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2706 show_verbose_canvas_cursor ();
2710 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2712 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2714 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2715 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2717 if (event->button.state & Keyboard::SecondaryModifier) {
2722 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2723 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2725 // calculate zero crossing point. back off by .01 to stay on the
2726 // positive side of zero
2728 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01;
2729 cp->line.parent_group().i2w(_unused, zero_gain_y);
2731 // make sure we hit zero when passing through
2732 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2733 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2737 if (drag_info.x_constrained) {
2738 cx = drag_info.grab_x;
2740 if (drag_info.y_constrained) {
2741 cy = drag_info.grab_y;
2744 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2745 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2747 cp->line.parent_group().w2i (cx, cy);
2751 cy = min ((double) cp->line.height(), cy);
2753 //translate cx to frames
2754 nframes_t cx_frames = unit_to_frame (cx);
2756 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2757 snap_to (cx_frames);
2760 float fraction = 1.0 - (cy / cp->line.height());
2764 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2770 cp->line.point_drag (*cp, cx_frames , fraction, push);
2772 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2774 drag_info.first_move = false;
2778 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2780 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2782 if (drag_info.first_move) {
2786 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2787 reset_point_selection ();
2791 control_point_drag_motion_callback (item, event);
2793 cp->line.end_drag (cp);
2797 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2799 switch (mouse_mode) {
2801 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2802 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2810 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2814 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2815 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2819 start_line_grab (al, event);
2823 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2827 nframes_t frame_within_region;
2829 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2833 cx = event->button.x;
2834 cy = event->button.y;
2835 line->parent_group().w2i (cx, cy);
2836 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2838 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2839 current_line_drag_info.after)) {
2840 /* no adjacent points */
2844 drag_info.item = &line->grab_item();
2845 drag_info.data = line;
2846 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2847 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2849 start_grab (event, fader_cursor);
2851 double fraction = 1.0 - (cy / line->height());
2853 line->start_drag (0, drag_info.grab_frame, fraction);
2855 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2856 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2857 show_verbose_canvas_cursor ();
2861 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2863 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2865 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2867 if (event->button.state & Keyboard::SecondaryModifier) {
2871 double cx = drag_info.current_pointer_x;
2872 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2874 // calculate zero crossing point. back off by .01 to stay on the
2875 // positive side of zero
2877 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
2878 line->parent_group().i2w(_unused, zero_gain_y);
2880 // make sure we hit zero when passing through
2881 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2882 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2886 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2888 line->parent_group().w2i (cx, cy);
2891 cy = min ((double) line->height(), cy);
2894 fraction = 1.0 - (cy / line->height());
2898 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2904 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2906 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2910 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2912 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2913 line_drag_motion_callback (item, event);
2918 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2920 if (selection->regions.empty() || clicked_regionview == 0) {
2924 drag_info.copy = false;
2925 drag_info.item = item;
2926 drag_info.data = clicked_regionview;
2928 if (Config->get_edit_mode() == Splice) {
2929 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
2930 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
2932 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2933 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2939 TimeAxisView* tvp = clicked_trackview;
2940 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2942 if (tv && tv->is_audio_track()) {
2943 speed = tv->get_diskstream()->speed();
2946 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2947 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2948 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
2949 drag_info.dest_trackview = drag_info.source_trackview;
2950 // we want a move threshold
2951 drag_info.want_move_threshold = true;
2953 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2955 begin_reversible_command (_("move region(s)"));
2959 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2961 if (selection->regions.empty() || clicked_regionview == 0) {
2965 drag_info.copy = true;
2966 drag_info.item = item;
2967 drag_info.data = clicked_regionview;
2971 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2972 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2975 if (atv && atv->is_audio_track()) {
2976 speed = atv->get_diskstream()->speed();
2979 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
2980 drag_info.dest_trackview = drag_info.source_trackview;
2981 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2982 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2983 // we want a move threshold
2984 drag_info.want_move_threshold = true;
2985 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2986 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2987 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2991 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2993 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
2997 drag_info.copy = false;
2998 drag_info.item = item;
2999 drag_info.data = clicked_regionview;
3000 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3001 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3006 TimeAxisView* tvp = clicked_trackview;
3007 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3009 if (tv && tv->is_audio_track()) {
3010 speed = tv->get_diskstream()->speed();
3013 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
3014 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3015 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3016 drag_info.dest_trackview = drag_info.source_trackview;
3017 // we want a move threshold
3018 drag_info.want_move_threshold = true;
3019 drag_info.brushing = true;
3021 begin_reversible_command (_("Drag region brush"));
3025 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3027 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3029 drag_info.want_move_threshold = false; // don't copy again
3031 /* duplicate the regionview(s) and region(s) */
3033 vector<RegionView*> new_regionviews;
3035 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3038 AudioRegionView* arv;
3043 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
3044 /* XXX handle MIDI here */
3048 const boost::shared_ptr<const Region> original = arv->region();
3049 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3050 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (region_copy);
3052 nrv = new AudioRegionView (*arv, ar);
3053 nrv->get_canvas_group()->show ();
3055 new_regionviews.push_back (nrv);
3058 if (new_regionviews.empty()) {
3062 /* reset selection to new regionviews */
3064 selection->set (new_regionviews);
3066 /* reset drag_info data to reflect the fact that we are dragging the copies */
3068 drag_info.data = new_regionviews.front();
3070 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3075 Editor::check_region_drag_possible (AudioTimeAxisView** tv)
3077 /* Which trackview is this ? */
3079 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3080 (*tv) = dynamic_cast<AudioTimeAxisView*>(tvp);
3082 /* The region motion is only processed if the pointer is over
3086 if (!(*tv) || !(*tv)->is_audio_track()) {
3087 /* To make sure we hide the verbose canvas cursor when the mouse is
3088 not held over and audiotrack.
3090 hide_verbose_canvas_cursor ();
3097 struct RegionSelectionByPosition {
3098 bool operator() (RegionView*a, RegionView* b) {
3099 return a->region()->position () < b->region()->position();
3104 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3106 AudioTimeAxisView* tv;
3108 if (!check_region_drag_possible (&tv)) {
3112 if (!drag_info.move_threshold_passed) {
3118 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3124 RegionSelection copy (selection->regions);
3126 RegionSelectionByPosition cmp;
3129 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3131 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(*i)->get_time_axis_view());
3137 boost::shared_ptr<Playlist> playlist;
3139 if ((playlist = atv->playlist()) == 0) {
3143 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3148 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3152 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3158 playlist->shuffle ((*i)->region(), dir);
3160 drag_info.grab_x = drag_info.current_pointer_x;
3165 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3170 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3174 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3175 nframes_t pending_region_position = 0;
3176 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3177 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3178 bool clamp_y_axis = false;
3179 vector<int32_t> height_list(512) ;
3180 vector<int32_t>::iterator j;
3181 AudioTimeAxisView* tv;
3183 possibly_copy_regions_during_grab (event);
3185 if (!check_region_drag_possible (&tv)) {
3189 original_pointer_order = drag_info.dest_trackview->order;
3191 /************************************************************
3193 ************************************************************/
3195 if (drag_info.brushing) {
3196 clamp_y_axis = true;
3201 if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3203 int32_t children = 0, numtracks = 0;
3204 // XXX hard coding track limit, oh my, so very very bad
3205 bitset <1024> tracks (0x00);
3206 /* get a bitmask representing the visible tracks */
3208 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3209 TimeAxisView *tracklist_timeview;
3210 tracklist_timeview = (*i);
3211 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3212 list<TimeAxisView*> children_list;
3214 /* zeroes are audio tracks. ones are other types. */
3216 if (!atv2->hidden()) {
3218 if (visible_y_high < atv2->order) {
3219 visible_y_high = atv2->order;
3221 if (visible_y_low > atv2->order) {
3222 visible_y_low = atv2->order;
3225 if (!atv2->is_audio_track()) {
3226 tracks = tracks |= (0x01 << atv2->order);
3229 height_list[atv2->order] = (*i)->height;
3231 if ((children_list = atv2->get_child_list()).size() > 0) {
3232 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3233 tracks = tracks |= (0x01 << (atv2->order + children));
3234 height_list[atv2->order + children] = (*j)->height;
3242 /* find the actual span according to the canvas */
3244 canvas_pointer_y_span = pointer_y_span;
3245 if (drag_info.dest_trackview->order >= tv->order) {
3247 for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3248 if (height_list[y] == 0 ) {
3249 canvas_pointer_y_span--;
3254 for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3255 if ( height_list[y] == 0 ) {
3256 canvas_pointer_y_span++;
3261 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3262 RegionView* rv2 = (*i);
3263 double ix1, ix2, iy1, iy2;
3266 if (rv2->region()->locked()) {
3270 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3271 rv2->get_canvas_group()->i2w (ix1, iy1);
3272 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3273 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3275 if (atv2->order != original_pointer_order) {
3276 /* this isn't the pointer track */
3278 if (canvas_pointer_y_span > 0) {
3280 /* moving up the canvas */
3281 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3283 int32_t visible_tracks = 0;
3284 while (visible_tracks < canvas_pointer_y_span ) {
3287 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3288 /* we're passing through a hidden track */
3293 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3294 clamp_y_axis = true;
3298 clamp_y_axis = true;
3301 } else if (canvas_pointer_y_span < 0) {
3303 /*moving down the canvas*/
3305 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3308 int32_t visible_tracks = 0;
3310 while (visible_tracks > canvas_pointer_y_span ) {
3313 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3317 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3318 clamp_y_axis = true;
3323 clamp_y_axis = true;
3329 /* this is the pointer's track */
3330 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3331 clamp_y_axis = true;
3332 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3333 clamp_y_axis = true;
3341 } else if (drag_info.dest_trackview == tv) {
3342 clamp_y_axis = true;
3346 if (!clamp_y_axis) {
3347 drag_info.dest_trackview = tv;
3350 /************************************************************
3352 ************************************************************/
3354 /* compute the amount of pointer motion in frames, and where
3355 the region would be if we moved it by that much.
3358 if ( drag_info.move_threshold_passed ) {
3360 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3362 nframes_t sync_frame;
3363 nframes_t sync_offset;
3366 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3368 sync_offset = rv->region()->sync_offset (sync_dir);
3370 /* we don't handle a sync point that lies before zero.
3372 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3373 sync_frame = pending_region_position + (sync_dir*sync_offset);
3375 /* we snap if the snap modifier is not enabled.
3378 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3379 snap_to (sync_frame);
3382 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3385 pending_region_position = drag_info.last_frame_position;
3389 pending_region_position = 0;
3392 if (pending_region_position > max_frames - rv->region()->length()) {
3393 pending_region_position = drag_info.last_frame_position;
3396 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3398 bool x_move_allowed = ( !drag_info.x_constrained && (Config->get_edit_mode() != Lock)) || ( drag_info.x_constrained && (Config->get_edit_mode() == Lock)) ;
3399 if ( pending_region_position != drag_info.last_frame_position && x_move_allowed ) {
3401 /* now compute the canvas unit distance we need to move the regionview
3402 to make it appear at the new location.
3405 if (pending_region_position > drag_info.last_frame_position) {
3406 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3408 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3411 drag_info.last_frame_position = pending_region_position;
3418 /* threshold not passed */
3423 /*************************************************************
3425 ************************************************************/
3427 if (x_delta == 0 && (pointer_y_span == 0)) {
3428 /* haven't reached next snap point, and we're not switching
3429 trackviews. nothing to do.
3436 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3438 RegionView* rv2 = (*i);
3440 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3442 double ix1, ix2, iy1, iy2;
3443 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3444 rv2->get_canvas_group()->i2w (ix1, iy1);
3453 /*************************************************************
3455 ************************************************************/
3459 if (drag_info.first_move) {
3460 if (drag_info.move_threshold_passed) {
3471 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3472 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3474 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3476 RegionView* rv = (*i);
3477 double ix1, ix2, iy1, iy2;
3478 int32_t temp_pointer_y_span = pointer_y_span;
3480 if (rv->region()->locked()) {
3484 /* get item BBox, which will be relative to parent. so we have
3485 to query on a child, then convert to world coordinates using
3489 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3490 rv->get_canvas_group()->i2w (ix1, iy1);
3491 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3492 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3493 AudioTimeAxisView* temp_atv;
3495 if ((pointer_y_span != 0) && !clamp_y_axis) {
3498 for (j = height_list.begin(); j!= height_list.end(); j++) {
3499 if (x == canvas_atv->order) {
3500 /* we found the track the region is on */
3501 if (x != original_pointer_order) {
3502 /*this isn't from the same track we're dragging from */
3503 temp_pointer_y_span = canvas_pointer_y_span;
3505 while (temp_pointer_y_span > 0) {
3506 /* we're moving up canvas-wise,
3507 so we need to find the next track height
3509 if (j != height_list.begin()) {
3512 if (x != original_pointer_order) {
3513 /* we're not from the dragged track, so ignore hidden tracks. */
3515 temp_pointer_y_span++;
3519 temp_pointer_y_span--;
3522 while (temp_pointer_y_span < 0) {
3524 if (x != original_pointer_order) {
3526 temp_pointer_y_span--;
3530 if (j != height_list.end()) {
3533 temp_pointer_y_span++;
3535 /* find out where we'll be when we move and set height accordingly */
3537 tvp2 = trackview_by_y_position (iy1 + y_delta);
3538 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3539 rv->set_height (temp_atv->height);
3541 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3542 personally, i think this can confuse things, but never mind.
3545 //const GdkColor& col (temp_atv->view->get_region_color());
3546 //rv->set_color (const_cast<GdkColor&>(col));
3554 /* prevent the regionview from being moved to before
3555 the zero position on the canvas.
3560 if (-x_delta > ix1) {
3563 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3564 x_delta = max_frames - rv->region()->last_frame();
3568 if (drag_info.first_move) {
3570 /* hide any dependent views */
3572 rv->get_time_axis_view().hide_dependent_views (*rv);
3574 /* this is subtle. raising the regionview itself won't help,
3575 because raise_to_top() just puts the item on the top of
3576 its parent's stack. so, we need to put the trackview canvas_display group
3577 on the top, since its parent is the whole canvas.
3580 rv->get_canvas_group()->raise_to_top();
3581 rv->get_time_axis_view().canvas_display->raise_to_top();
3582 cursor_group->raise_to_top();
3583 rv->fake_set_opaque (true);
3586 if (drag_info.brushing) {
3587 mouse_brush_insert_region (rv, pending_region_position);
3589 rv->move (x_delta, y_delta);
3592 } /* foreach region */
3596 if (drag_info.first_move && drag_info.move_threshold_passed) {
3597 cursor_group->raise_to_top();
3598 drag_info.first_move = false;
3601 if (x_delta != 0 && !drag_info.brushing) {
3602 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3607 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3610 RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3611 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3612 bool nocommit = true;
3614 RouteTimeAxisView* atv;
3615 bool regionview_y_movement;
3616 bool regionview_x_movement;
3617 vector<RegionView*> copies;
3618 RouteTimeAxisView* tvp1;
3619 boost::shared_ptr<Diskstream> ds;
3620 boost::shared_ptr<Playlist> from_playlist;
3623 /* first_move is set to false if the regionview has been moved in the
3627 if (drag_info.first_move) {
3634 /* The regionview has been moved at some stage during the grab so we need
3635 to account for any mouse movement between this event and the last one.
3638 region_drag_motion_callback (item, event);
3640 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3641 selection->set (pre_drag_region_selection);
3642 pre_drag_region_selection.clear ();
3645 if (drag_info.brushing) {
3646 /* all changes were made during motion event handlers */
3648 if (drag_info.copy) {
3649 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3650 copies.push_back (*i);
3657 /* get the playlist where this drag started. we can't use rv->region()->playlist()
3658 because we may have copied the region and it has not been attached to a playlist.
3661 assert ((tvp1 = dynamic_cast<RouteTimeAxisView*> (drag_info.source_trackview)));
3662 assert ((ds = tvp1->get_diskstream()));
3663 assert ((from_playlist = ds->playlist()));
3665 /* adjust for track speed */
3668 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.dest_trackview);
3670 if (atv && atv->get_diskstream()) {
3671 speed = atv->get_diskstream()->speed();
3674 /* check all regions for motion because some might have been locked */
3676 regionview_x_movement = false;
3677 regionview_y_movement = false;
3679 for (list<RegionView*>::const_iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3680 if (drag_info.last_frame_position != (nframes_t) ((*i)->region()->position()/speed)) {
3681 regionview_x_movement = true;
3684 if (drag_info.dest_trackview != &(*i)->get_time_axis_view()) {
3685 regionview_y_movement = true;
3689 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3690 //printf ("last_rackview: %s \n", drag_info.dest_trackview->name().c_str());
3694 if (drag_info.copy) {
3695 if (drag_info.x_constrained) {
3696 op_string = _("fixed time region copy");
3698 op_string = _("region copy");
3701 if (drag_info.x_constrained) {
3702 op_string = _("fixed time region drag");
3704 op_string = _("region drag");
3708 begin_reversible_command (op_string);
3710 if (regionview_y_movement) {
3712 /* moved to a different audio track. */
3714 vector<RegionView*> new_selection;
3716 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3718 RegionView* rv = (*i);
3720 if (rv->region()->locked()) {
3725 double ix1, ix2, iy1, iy2;
3726 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3727 rv->get_canvas_group()->i2w (ix1, iy1);
3728 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3729 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3731 boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3733 where = (nframes_t) (unit_to_frame (ix1) * speed);
3734 boost::shared_ptr<Region> new_region;
3736 if (drag_info.copy) {
3737 /* we already made a copy */
3738 new_region = rv->region();
3740 new_region = RegionFactory::create (rv->region());
3743 /* undo the previous hide_dependent_views so that xfades don't
3744 disappear on copying regions
3747 rv->get_time_axis_view().reveal_dependent_views (*rv);
3749 if (!drag_info.copy) {
3751 /* the region that used to be in the old playlist is not
3752 moved to the new one - we make a copy of it. as a result,
3753 any existing editor for the region should no longer be
3757 rv->hide_region_editor();
3758 rv->fake_set_opaque (false);
3760 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3761 from_playlist->remove_region ((rv->region()));
3762 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3766 /* the regionview we dragged around is a temporary copy, queue it for deletion */
3768 copies.push_back (rv);
3771 latest_regionviews.clear ();
3773 sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3774 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3775 to_playlist->add_region (new_region, where);
3776 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3779 if (!latest_regionviews.empty()) {
3780 new_selection.insert (new_selection.end(), latest_regionviews.begin(), latest_regionviews.end());
3783 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3784 was selected in all of them, then removing it from the playlist will have removed all
3785 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3786 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3787 corresponding regionview, and the selection is now empty).
3789 this could have invalidated any and all iterators into the region selection.
3791 the heuristic we use here is: if the region selection is empty, break out of the loop
3792 here. if the region selection is not empty, then restart the loop because we know that
3793 we must have removed at least the region(view) we've just been working on as well as any
3794 that we processed on previous iterations.
3796 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3797 we can just iterate.
3800 if (drag_info.copy) {
3803 if (selection->regions.empty()) {
3806 i = selection->regions.by_layer().begin();
3811 selection->set (new_selection);
3815 /* motion within a single track */
3817 list<RegionView*> regions = selection->regions.by_layer();
3819 if (drag_info.copy) {
3820 selection->clear_regions();
3823 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3827 if (rv->region()->locked()) {
3832 if (regionview_x_movement) {
3833 double ownspeed = 1.0;
3834 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3836 if (atv && atv->get_diskstream()) {
3837 ownspeed = atv->get_diskstream()->speed();
3840 /* base the new region position on the current position of the regionview.*/
3842 double ix1, ix2, iy1, iy2;
3844 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3845 rv->get_canvas_group()->i2w (ix1, iy1);
3846 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3850 where = rv->region()->position();
3855 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3857 if (drag_info.copy) {
3859 /* we already made a copy of the region */
3861 boost::shared_ptr<Region> newregion = rv->region();
3865 latest_regionviews.clear ();
3866 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3867 from_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3870 if (!latest_regionviews.empty()) {
3871 // XXX why just the first one ? we only expect one
3872 atv->reveal_dependent_views (*latest_regionviews.front());
3873 selection->add (latest_regionviews);
3876 /* if the original region was locked, we don't care for the new one */
3878 newregion->set_locked (false);
3882 /* just change the model */
3884 rv->region()->set_position (where, (void*) this);
3890 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3892 if (drag_info.copy) {
3893 copies.push_back (rv);
3901 commit_reversible_command ();
3904 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3910 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3912 /* Either add to or set the set the region selection, unless
3913 this is an alignment click (control used)
3916 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
3917 TimeAxisView* tv = &rv.get_time_axis_view();
3918 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3920 if (atv && atv->is_audio_track()) {
3921 speed = atv->get_diskstream()->speed();
3924 nframes64_t where = get_preferred_edit_position();
3928 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
3930 align_region (rv.region(), SyncPoint, (nframes_t) (where * speed));
3932 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
3934 align_region (rv.region(), End, (nframes_t) (where * speed));
3938 align_region (rv.region(), Start, (nframes_t) (where * speed));
3945 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3951 nframes_t frame_rate;
3960 if (Profile->get_sae() || Profile->get_small_screen()) {
3961 m = ARDOUR_UI::instance()->primary_clock.mode();
3963 m = ARDOUR_UI::instance()->secondary_clock.mode();
3967 case AudioClock::BBT:
3968 session->bbt_time (frame, bbt);
3969 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3972 case AudioClock::SMPTE:
3973 session->smpte_time (frame, smpte);
3974 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3977 case AudioClock::MinSec:
3978 /* XXX this is copied from show_verbose_duration_cursor() */
3979 frame_rate = session->frame_rate();
3980 hours = frame / (frame_rate * 3600);
3981 frame = frame % (frame_rate * 3600);
3982 mins = frame / (frame_rate * 60);
3983 frame = frame % (frame_rate * 60);
3984 secs = (float) frame / (float) frame_rate;
3985 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3989 snprintf (buf, sizeof(buf), "%u", frame);
3993 if (xpos >= 0 && ypos >=0) {
3994 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3997 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3999 show_verbose_canvas_cursor ();
4003 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
4010 nframes_t distance, frame_rate;
4012 Meter meter_at_start(session->tempo_map().meter_at(start));
4020 if (Profile->get_sae() || Profile->get_small_screen()) {
4021 m = ARDOUR_UI::instance()->primary_clock.mode ();
4023 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4027 case AudioClock::BBT:
4028 session->bbt_time (start, sbbt);
4029 session->bbt_time (end, ebbt);
4032 /* XXX this computation won't work well if the
4033 user makes a selection that spans any meter changes.
4036 ebbt.bars -= sbbt.bars;
4037 if (ebbt.beats >= sbbt.beats) {
4038 ebbt.beats -= sbbt.beats;
4041 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4043 if (ebbt.ticks >= sbbt.ticks) {
4044 ebbt.ticks -= sbbt.ticks;
4047 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4050 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4053 case AudioClock::SMPTE:
4054 session->smpte_duration (end - start, smpte);
4055 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4058 case AudioClock::MinSec:
4059 /* XXX this stuff should be elsewhere.. */
4060 distance = end - start;
4061 frame_rate = session->frame_rate();
4062 hours = distance / (frame_rate * 3600);
4063 distance = distance % (frame_rate * 3600);
4064 mins = distance / (frame_rate * 60);
4065 distance = distance % (frame_rate * 60);
4066 secs = (float) distance / (float) frame_rate;
4067 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4071 snprintf (buf, sizeof(buf), "%u", end - start);
4075 if (xpos >= 0 && ypos >=0) {
4076 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4079 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4082 show_verbose_canvas_cursor ();
4086 Editor::collect_new_region_view (RegionView* rv)
4088 latest_regionviews.push_back (rv);
4092 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4094 if (clicked_regionview == 0) {
4098 /* lets try to create new Region for the selection */
4100 vector<boost::shared_ptr<AudioRegion> > new_regions;
4101 create_region_from_selection (new_regions);
4103 if (new_regions.empty()) {
4107 /* XXX fix me one day to use all new regions */
4109 boost::shared_ptr<Region> region (new_regions.front());
4111 /* add it to the current stream/playlist.
4113 tricky: the streamview for the track will add a new regionview. we will
4114 catch the signal it sends when it creates the regionview to
4115 set the regionview we want to then drag.
4118 latest_regionviews.clear();
4119 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4121 /* A selection grab currently creates two undo/redo operations, one for
4122 creating the new region and another for moving it.
4125 begin_reversible_command (_("selection grab"));
4127 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
4129 XMLNode *before = &(playlist->get_state());
4130 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
4131 XMLNode *after = &(playlist->get_state());
4132 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4134 commit_reversible_command ();
4138 if (latest_regionviews.empty()) {
4139 /* something went wrong */
4143 /* we need to deselect all other regionviews, and select this one
4144 i'm ignoring undo stuff, because the region creation will take care of it
4146 selection->set (latest_regionviews);
4148 drag_info.item = latest_regionviews.front()->get_canvas_group();
4149 drag_info.data = latest_regionviews.front();
4150 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4151 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4155 drag_info.source_trackview = clicked_trackview;
4156 drag_info.dest_trackview = drag_info.source_trackview;
4157 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4158 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4160 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4164 Editor::cancel_selection ()
4166 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4167 (*i)->hide_selection ();
4169 selection->clear ();
4170 clicked_selection = 0;
4174 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4176 nframes_t start = 0;
4183 drag_info.item = item;
4184 drag_info.motion_callback = &Editor::drag_selection;
4185 drag_info.finished_callback = &Editor::end_selection_op;
4190 case CreateSelection:
4191 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4192 drag_info.copy = true;
4194 drag_info.copy = false;
4196 start_grab (event, selector_cursor);
4199 case SelectionStartTrim:
4200 if (clicked_trackview) {
4201 clicked_trackview->order_selection_trims (item, true);
4203 start_grab (event, trimmer_cursor);
4204 start = selection->time[clicked_selection].start;
4205 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4208 case SelectionEndTrim:
4209 if (clicked_trackview) {
4210 clicked_trackview->order_selection_trims (item, false);
4212 start_grab (event, trimmer_cursor);
4213 end = selection->time[clicked_selection].end;
4214 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4218 start = selection->time[clicked_selection].start;
4220 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4224 if (selection_op == SelectionMove) {
4225 show_verbose_time_cursor(start, 10);
4227 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4232 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4234 nframes_t start = 0;
4237 nframes_t pending_position;
4239 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4240 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4242 pending_position = 0;
4245 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4246 snap_to (pending_position);
4249 /* only alter selection if the current frame is
4250 different from the last frame position (adjusted)
4253 if (pending_position == drag_info.last_pointer_frame) return;
4255 switch (selection_op) {
4256 case CreateSelection:
4258 if (drag_info.first_move) {
4259 snap_to (drag_info.grab_frame);
4262 if (pending_position < drag_info.grab_frame) {
4263 start = pending_position;
4264 end = drag_info.grab_frame;
4266 end = pending_position;
4267 start = drag_info.grab_frame;
4270 /* first drag: Either add to the selection
4271 or create a new selection->
4274 if (drag_info.first_move) {
4276 begin_reversible_command (_("range selection"));
4278 if (drag_info.copy) {
4279 /* adding to the selection */
4280 clicked_selection = selection->add (start, end);
4281 drag_info.copy = false;
4283 /* new selection-> */
4284 clicked_selection = selection->set (clicked_trackview, start, end);
4289 case SelectionStartTrim:
4291 if (drag_info.first_move) {
4292 begin_reversible_command (_("trim selection start"));
4295 start = selection->time[clicked_selection].start;
4296 end = selection->time[clicked_selection].end;
4298 if (pending_position > end) {
4301 start = pending_position;
4305 case SelectionEndTrim:
4307 if (drag_info.first_move) {
4308 begin_reversible_command (_("trim selection end"));
4311 start = selection->time[clicked_selection].start;
4312 end = selection->time[clicked_selection].end;
4314 if (pending_position < start) {
4317 end = pending_position;
4324 if (drag_info.first_move) {
4325 begin_reversible_command (_("move selection"));
4328 start = selection->time[clicked_selection].start;
4329 end = selection->time[clicked_selection].end;
4331 length = end - start;
4333 start = pending_position;
4336 end = start + length;
4341 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4342 start_canvas_autoscroll (1);
4346 selection->replace (clicked_selection, start, end);
4349 drag_info.last_pointer_frame = pending_position;
4350 drag_info.first_move = false;
4352 if (selection_op == SelectionMove) {
4353 show_verbose_time_cursor(start, 10);
4355 show_verbose_time_cursor(pending_position, 10);
4360 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4362 if (!drag_info.first_move) {
4363 drag_selection (item, event);
4364 /* XXX this is not object-oriented programming at all. ick */
4365 if (selection->time.consolidate()) {
4366 selection->TimeChanged ();
4368 commit_reversible_command ();
4370 /* just a click, no pointer movement.*/
4372 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4374 selection->clear_time();
4379 /* XXX what happens if its a music selection? */
4380 session->set_audio_range (selection->time);
4381 stop_canvas_autoscroll ();
4385 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4388 TimeAxisView* tvp = clicked_trackview;
4389 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4391 if (tv && tv->is_audio_track()) {
4392 speed = tv->get_diskstream()->speed();
4395 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4396 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4397 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4399 //drag_info.item = clicked_regionview->get_name_highlight();
4400 drag_info.item = item;
4401 drag_info.motion_callback = &Editor::trim_motion_callback;
4402 drag_info.finished_callback = &Editor::trim_finished_callback;
4404 start_grab (event, trimmer_cursor);
4406 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4407 trim_op = ContentsTrim;
4409 /* These will get overridden for a point trim.*/
4410 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4411 /* closer to start */
4412 trim_op = StartTrim;
4413 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4421 show_verbose_time_cursor(region_start, 10);
4424 show_verbose_time_cursor(region_end, 10);
4427 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4433 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4435 RegionView* rv = clicked_regionview;
4436 nframes_t frame_delta = 0;
4437 bool left_direction;
4438 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4440 /* snap modifier works differently here..
4441 its' current state has to be passed to the
4442 various trim functions in order to work properly
4446 TimeAxisView* tvp = clicked_trackview;
4447 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4448 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4450 if (tv && tv->is_audio_track()) {
4451 speed = tv->get_diskstream()->speed();
4454 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4455 left_direction = true;
4457 left_direction = false;
4461 snap_to (drag_info.current_pointer_frame);
4464 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4468 if (drag_info.first_move) {
4474 trim_type = "Region start trim";
4477 trim_type = "Region end trim";
4480 trim_type = "Region content trim";
4484 begin_reversible_command (trim_type);
4486 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4487 (*i)->fake_set_opaque(false);
4488 (*i)->region()->freeze ();
4490 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4492 arv->temporarily_hide_envelope ();
4494 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4495 insert_result = motion_frozen_playlists.insert (pl);
4496 if (insert_result.second) {
4497 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4502 if (left_direction) {
4503 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4505 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4510 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4513 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4514 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4520 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4523 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4524 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4531 bool swap_direction = false;
4533 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4534 swap_direction = true;
4537 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4538 i != selection->regions.by_layer().end(); ++i)
4540 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4548 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4551 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4554 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4558 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4559 drag_info.first_move = false;
4563 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4565 boost::shared_ptr<Region> region (rv.region());
4567 if (region->locked()) {
4571 nframes_t new_bound;
4574 TimeAxisView* tvp = clicked_trackview;
4575 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4577 if (tv && tv->is_audio_track()) {
4578 speed = tv->get_diskstream()->speed();
4581 if (left_direction) {
4582 if (swap_direction) {
4583 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4585 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4588 if (swap_direction) {
4589 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4591 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4596 snap_to (new_bound);
4598 region->trim_start ((nframes_t) (new_bound * speed), this);
4599 rv.region_changed (StartChanged);
4603 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4605 boost::shared_ptr<Region> region (rv.region());
4607 if (region->locked()) {
4611 nframes_t new_bound;
4614 TimeAxisView* tvp = clicked_trackview;
4615 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4617 if (tv && tv->is_audio_track()) {
4618 speed = tv->get_diskstream()->speed();
4621 if (left_direction) {
4622 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4624 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4628 snap_to (new_bound, (left_direction ? 0 : 1));
4631 region->trim_front ((nframes_t) (new_bound * speed), this);
4633 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4637 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4639 boost::shared_ptr<Region> region (rv.region());
4641 if (region->locked()) {
4645 nframes_t new_bound;
4648 TimeAxisView* tvp = clicked_trackview;
4649 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4651 if (tv && tv->is_audio_track()) {
4652 speed = tv->get_diskstream()->speed();
4655 if (left_direction) {
4656 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4658 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4662 snap_to (new_bound);
4664 region->trim_end ((nframes_t) (new_bound * speed), this);
4665 rv.region_changed (LengthChanged);
4669 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4671 if (!drag_info.first_move) {
4672 trim_motion_callback (item, event);
4674 if (!selection->selected (clicked_regionview)) {
4675 thaw_region_after_trim (*clicked_regionview);
4678 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4679 i != selection->regions.by_layer().end(); ++i)
4681 thaw_region_after_trim (**i);
4682 (*i)->fake_set_opaque (true);
4686 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4688 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4691 motion_frozen_playlists.clear ();
4693 commit_reversible_command();
4695 /* no mouse movement */
4701 Editor::point_trim (GdkEvent* event)
4703 RegionView* rv = clicked_regionview;
4704 nframes_t new_bound = drag_info.current_pointer_frame;
4706 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4707 snap_to (new_bound);
4710 /* Choose action dependant on which button was pressed */
4711 switch (event->button.button) {
4713 trim_op = StartTrim;
4714 begin_reversible_command (_("Start point trim"));
4716 if (selection->selected (rv)) {
4718 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4719 i != selection->regions.by_layer().end(); ++i)
4721 if (!(*i)->region()->locked()) {
4722 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4723 XMLNode &before = pl->get_state();
4724 (*i)->region()->trim_front (new_bound, this);
4725 XMLNode &after = pl->get_state();
4726 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4732 if (!rv->region()->locked()) {
4733 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4734 XMLNode &before = pl->get_state();
4735 rv->region()->trim_front (new_bound, this);
4736 XMLNode &after = pl->get_state();
4737 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4741 commit_reversible_command();
4746 begin_reversible_command (_("End point trim"));
4748 if (selection->selected (rv)) {
4750 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4752 if (!(*i)->region()->locked()) {
4753 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4754 XMLNode &before = pl->get_state();
4755 (*i)->region()->trim_end (new_bound, this);
4756 XMLNode &after = pl->get_state();
4757 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4763 if (!rv->region()->locked()) {
4764 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4765 XMLNode &before = pl->get_state();
4766 rv->region()->trim_end (new_bound, this);
4767 XMLNode &after = pl->get_state();
4768 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4772 commit_reversible_command();
4781 Editor::thaw_region_after_trim (RegionView& rv)
4783 boost::shared_ptr<Region> region (rv.region());
4785 if (region->locked()) {
4789 region->thaw (_("trimmed region"));
4790 XMLNode &after = region->playlist()->get_state();
4791 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4793 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4795 arv->unhide_envelope ();
4799 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4804 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4805 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4809 Location* location = find_location_from_marker (marker, is_start);
4810 location->set_hidden (true, this);
4815 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4821 drag_info.item = item;
4822 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4823 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4825 range_marker_op = op;
4827 if (!temp_location) {
4828 temp_location = new Location;
4832 case CreateRangeMarker:
4833 case CreateTransportMarker:
4834 case CreateCDMarker:
4836 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4837 drag_info.copy = true;
4839 drag_info.copy = false;
4841 start_grab (event, selector_cursor);
4845 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4850 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4852 nframes_t start = 0;
4854 ArdourCanvas::SimpleRect *crect;
4856 switch (range_marker_op) {
4857 case CreateRangeMarker:
4858 crect = range_bar_drag_rect;
4860 case CreateTransportMarker:
4861 crect = transport_bar_drag_rect;
4863 case CreateCDMarker:
4864 crect = cd_marker_bar_drag_rect;
4867 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
4872 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4873 snap_to (drag_info.current_pointer_frame);
4876 /* only alter selection if the current frame is
4877 different from the last frame position.
4880 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4882 switch (range_marker_op) {
4883 case CreateRangeMarker:
4884 case CreateTransportMarker:
4885 case CreateCDMarker:
4886 if (drag_info.first_move) {
4887 snap_to (drag_info.grab_frame);
4890 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4891 start = drag_info.current_pointer_frame;
4892 end = drag_info.grab_frame;
4894 end = drag_info.current_pointer_frame;
4895 start = drag_info.grab_frame;
4898 /* first drag: Either add to the selection
4899 or create a new selection.
4902 if (drag_info.first_move) {
4904 temp_location->set (start, end);
4908 update_marker_drag_item (temp_location);
4909 range_marker_drag_rect->show();
4910 range_marker_drag_rect->raise_to_top();
4916 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4917 start_canvas_autoscroll (1);
4921 temp_location->set (start, end);
4923 double x1 = frame_to_pixel (start);
4924 double x2 = frame_to_pixel (end);
4925 crect->property_x1() = x1;
4926 crect->property_x2() = x2;
4928 update_marker_drag_item (temp_location);
4931 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4932 drag_info.first_move = false;
4934 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4939 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4941 Location * newloc = 0;
4945 if (!drag_info.first_move) {
4946 drag_range_markerbar_op (item, event);
4948 switch (range_marker_op) {
4949 case CreateRangeMarker:
4950 case CreateCDMarker:
4952 begin_reversible_command (_("new range marker"));
4953 XMLNode &before = session->locations()->get_state();
4954 session->locations()->next_available_name(rangename,"unnamed");
4955 if (range_marker_op == CreateCDMarker) {
4956 flags = Location::IsRangeMarker|Location::IsCDMarker;
4957 cd_marker_bar_drag_rect->hide();
4960 flags = Location::IsRangeMarker;
4961 range_bar_drag_rect->hide();
4963 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
4964 session->locations()->add (newloc, true);
4965 XMLNode &after = session->locations()->get_state();
4966 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4967 commit_reversible_command ();
4969 range_marker_drag_rect->hide();
4973 case CreateTransportMarker:
4974 // popup menu to pick loop or punch
4975 new_transport_marker_context_menu (&event->button, item);
4980 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4982 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
4987 start = session->locations()->first_mark_before (drag_info.grab_frame);
4988 end = session->locations()->first_mark_after (drag_info.grab_frame);
4990 if (end == max_frames) {
4991 end = session->current_end_frame ();
4995 start = session->current_start_frame ();
4998 switch (mouse_mode) {
5000 /* find the two markers on either side and then make the selection from it */
5001 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5005 /* find the two markers on either side of the click and make the range out of it */
5006 selection->set (0, start, end);
5015 stop_canvas_autoscroll ();
5021 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5023 drag_info.item = item;
5024 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5025 drag_info.finished_callback = &Editor::end_mouse_zoom;
5027 start_grab (event, zoom_cursor);
5029 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5033 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5038 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5039 snap_to (drag_info.current_pointer_frame);
5041 if (drag_info.first_move) {
5042 snap_to (drag_info.grab_frame);
5046 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5048 /* base start and end on initial click position */
5049 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5050 start = drag_info.current_pointer_frame;
5051 end = drag_info.grab_frame;
5053 end = drag_info.current_pointer_frame;
5054 start = drag_info.grab_frame;
5059 if (drag_info.first_move) {
5061 zoom_rect->raise_to_top();
5064 reposition_zoom_rect(start, end);
5066 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5067 drag_info.first_move = false;
5069 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5074 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5076 if (!drag_info.first_move) {
5077 drag_mouse_zoom (item, event);
5079 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5080 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5082 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5085 temporal_zoom_to_frame (false, drag_info.grab_frame);
5087 temporal_zoom_step (false);
5088 center_screen (drag_info.grab_frame);
5096 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
5098 double x1 = frame_to_pixel (start);
5099 double x2 = frame_to_pixel (end);
5100 double y2 = full_canvas_height - 1.0;
5102 zoom_rect->property_x1() = x1;
5103 zoom_rect->property_y1() = 1.0;
5104 zoom_rect->property_x2() = x2;
5105 zoom_rect->property_y2() = y2;
5109 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5111 drag_info.item = item;
5112 drag_info.motion_callback = &Editor::drag_rubberband_select;
5113 drag_info.finished_callback = &Editor::end_rubberband_select;
5115 start_grab (event, cross_hair_cursor);
5117 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5121 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5128 /* use a bigger drag threshold than the default */
5130 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5134 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5135 if (drag_info.first_move) {
5136 snap_to (drag_info.grab_frame);
5138 snap_to (drag_info.current_pointer_frame);
5141 /* base start and end on initial click position */
5143 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5144 start = drag_info.current_pointer_frame;
5145 end = drag_info.grab_frame;
5147 end = drag_info.current_pointer_frame;
5148 start = drag_info.grab_frame;
5151 if (drag_info.current_pointer_y < drag_info.grab_y) {
5152 y1 = drag_info.current_pointer_y;
5153 y2 = drag_info.grab_y;
5155 y2 = drag_info.current_pointer_y;
5156 y1 = drag_info.grab_y;
5160 if (start != end || y1 != y2) {
5162 double x1 = frame_to_pixel (start);
5163 double x2 = frame_to_pixel (end);
5165 rubberband_rect->property_x1() = x1;
5166 rubberband_rect->property_y1() = y1;
5167 rubberband_rect->property_x2() = x2;
5168 rubberband_rect->property_y2() = y2;
5170 rubberband_rect->show();
5171 rubberband_rect->raise_to_top();
5173 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5174 drag_info.first_move = false;
5176 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5181 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5183 if (!drag_info.first_move) {
5185 drag_rubberband_select (item, event);
5188 if (drag_info.current_pointer_y < drag_info.grab_y) {
5189 y1 = drag_info.current_pointer_y;
5190 y2 = drag_info.grab_y;
5193 y2 = drag_info.current_pointer_y;
5194 y1 = drag_info.grab_y;
5198 Selection::Operation op = Keyboard::selection_type (event->button.state);
5201 begin_reversible_command (_("rubberband selection"));
5203 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5204 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5206 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5210 commit_reversible_command ();
5214 selection->clear_tracks();
5215 selection->clear_regions();
5216 selection->clear_points ();
5217 selection->clear_lines ();
5220 rubberband_rect->hide();
5225 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5227 using namespace Gtkmm2ext;
5229 ArdourPrompter prompter (false);
5231 prompter.set_prompt (_("Name for region:"));
5232 prompter.set_initial_text (clicked_regionview->region()->name());
5233 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5234 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5235 prompter.show_all ();
5236 switch (prompter.run ()) {
5237 case Gtk::RESPONSE_ACCEPT:
5239 prompter.get_result(str);
5241 clicked_regionview->region()->set_name (str);
5249 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5251 drag_info.item = item;
5252 drag_info.motion_callback = &Editor::time_fx_motion;
5253 drag_info.finished_callback = &Editor::end_time_fx;
5257 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5261 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5263 RegionView* rv = clicked_regionview;
5265 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5266 snap_to (drag_info.current_pointer_frame);
5269 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5273 if (drag_info.current_pointer_frame > rv->region()->position()) {
5274 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5277 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5278 drag_info.first_move = false;
5280 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5284 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5286 clicked_regionview->get_time_axis_view().hide_timestretch ();
5288 if (drag_info.first_move) {
5292 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5293 /* backwards drag of the left edge - not usable */
5297 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5298 #ifdef USE_RUBBERBAND
5299 float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5301 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5304 begin_reversible_command (_("timestretch"));
5306 // XXX how do timeFX on multiple regions ?
5309 rs.add (clicked_regionview);
5311 if (time_stretch (rs, percentage) == 0) {
5312 session->commit_reversible_command ();
5317 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5319 /* no brushing without a useful snap setting */
5322 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5325 switch (snap_mode) {
5327 return; /* can't work because it allows region to be placed anywhere */
5332 switch (snap_type) {
5340 /* don't brush a copy over the original */
5342 if (pos == rv->region()->position()) {
5346 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5348 if (atv == 0 || !atv->is_audio_track()) {
5352 boost::shared_ptr<Playlist> playlist = atv->playlist();
5353 double speed = atv->get_diskstream()->speed();
5355 XMLNode &before = playlist->get_state();
5356 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5357 XMLNode &after = playlist->get_state();
5358 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5360 // playlist is frozen, so we have to update manually
5362 playlist->Modified(); /* EMIT SIGNAL */
5366 Editor::track_height_step_timeout ()
5369 struct timeval delta;
5371 gettimeofday (&now, 0);
5372 timersub (&now, &last_track_height_step_timestamp, &delta);
5374 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5375 current_stepping_trackview = 0;