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;
1539 if (scrubbing_direction == 0) {
1541 session->request_locate (drag_info.current_pointer_frame, false);
1542 session->request_transport_speed (0.1);
1543 scrubbing_direction = 1;
1547 if (last_scrub_x > drag_info.current_pointer_x) {
1549 /* pointer moved to the left */
1551 if (scrubbing_direction > 0) {
1553 /* we reversed direction to go backwards */
1556 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1560 /* still moving to the left (backwards) */
1562 scrub_reversals = 0;
1563 scrub_reverse_distance = 0;
1565 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1566 session->request_transport_speed (session->transport_speed() - delta);
1570 /* pointer moved to the right */
1572 if (scrubbing_direction < 0) {
1573 /* we reversed direction to go forward */
1576 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1579 /* still moving to the right */
1581 scrub_reversals = 0;
1582 scrub_reverse_distance = 0;
1584 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1585 session->request_transport_speed (session->transport_speed() + delta);
1589 /* if there have been more than 2 opposite motion moves detected, or one that moves
1590 back more than 10 pixels, reverse direction
1593 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1595 if (scrubbing_direction > 0) {
1596 /* was forwards, go backwards */
1597 session->request_transport_speed (-0.1);
1598 scrubbing_direction = -1;
1600 /* was backwards, go forwards */
1601 session->request_transport_speed (0.1);
1602 scrubbing_direction = 1;
1605 scrub_reverse_distance = 0;
1606 scrub_reversals = 0;
1610 last_scrub_x = drag_info.current_pointer_x;
1614 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1616 if (event->motion.is_hint) {
1619 /* We call this so that MOTION_NOTIFY events continue to be
1620 delivered to the canvas. We need to do this because we set
1621 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1622 the density of the events, at the expense of a round-trip
1623 to the server. Given that this will mostly occur on cases
1624 where DISPLAY = :0.0, and given the cost of what the motion
1625 event might do, its a good tradeoff.
1628 track_canvas->get_pointer (x, y);
1631 if (current_stepping_trackview) {
1632 /* don't keep the persistent stepped trackview if the mouse moves */
1633 current_stepping_trackview = 0;
1634 step_timeout.disconnect ();
1637 if (session && session->actively_recording()) {
1638 /* Sorry. no dragging stuff around while we record */
1642 drag_info.item_type = item_type;
1643 drag_info.last_pointer_x = drag_info.current_pointer_x;
1644 drag_info.last_pointer_y = drag_info.current_pointer_y;
1645 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1646 &drag_info.current_pointer_y);
1649 switch (mouse_mode) {
1660 if (!from_autoscroll && drag_info.item) {
1661 /* item != 0 is the best test i can think of for dragging.
1663 if (!drag_info.move_threshold_passed) {
1665 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1666 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1668 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1670 // and change the initial grab loc/frame if this drag info wants us to
1672 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1673 drag_info.grab_frame = drag_info.current_pointer_frame;
1674 drag_info.grab_x = drag_info.current_pointer_x;
1675 drag_info.grab_y = drag_info.current_pointer_y;
1676 drag_info.last_pointer_frame = drag_info.grab_frame;
1677 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1682 switch (item_type) {
1683 case PlayheadCursorItem:
1685 case GainControlPointItem:
1686 case RedirectAutomationControlPointItem:
1687 case GainAutomationControlPointItem:
1688 case PanAutomationControlPointItem:
1689 case TempoMarkerItem:
1690 case MeterMarkerItem:
1691 case RegionViewNameHighlight:
1692 case StartSelectionTrimItem:
1693 case EndSelectionTrimItem:
1696 case RedirectAutomationLineItem:
1697 case GainAutomationLineItem:
1698 case PanAutomationLineItem:
1699 case FadeInHandleItem:
1700 case FadeOutHandleItem:
1701 /* <CMT Additions> */
1702 case ImageFrameHandleStartItem:
1703 case ImageFrameHandleEndItem:
1704 case MarkerViewHandleStartItem:
1705 case MarkerViewHandleEndItem:
1706 /* </CMT Additions> */
1707 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1708 (event->motion.state & Gdk::BUTTON2_MASK))) {
1709 if (!from_autoscroll) {
1710 maybe_autoscroll (&event->motion);
1712 (this->*(drag_info.motion_callback)) (item, event);
1721 switch (mouse_mode) {
1726 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1727 (event->motion.state & GDK_BUTTON2_MASK))) {
1728 if (!from_autoscroll) {
1729 maybe_autoscroll (&event->motion);
1731 (this->*(drag_info.motion_callback)) (item, event);
1742 track_canvas_motion (event);
1743 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1751 Editor::break_drag ()
1753 stop_canvas_autoscroll ();
1754 hide_verbose_canvas_cursor ();
1756 if (drag_info.item) {
1757 drag_info.item->ungrab (0);
1759 /* put it back where it came from */
1764 drag_info.item->i2w (cxw, cyw);
1765 drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw);
1772 Editor::finalize_drag ()
1775 drag_info.copy = false;
1776 drag_info.motion_callback = 0;
1777 drag_info.finished_callback = 0;
1778 drag_info.dest_trackview = 0;
1779 drag_info.source_trackview = 0;
1780 drag_info.last_frame_position = 0;
1781 drag_info.grab_frame = 0;
1782 drag_info.last_pointer_frame = 0;
1783 drag_info.current_pointer_frame = 0;
1784 drag_info.brushing = false;
1786 if (drag_info.copied_location) {
1787 delete drag_info.copied_location;
1788 drag_info.copied_location = 0;
1793 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1795 if (drag_info.item == 0) {
1796 fatal << _("programming error: start_grab called without drag item") << endmsg;
1802 cursor = which_grabber_cursor ();
1805 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1807 if (event->button.button == 2) {
1808 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1809 drag_info.y_constrained = true;
1810 drag_info.x_constrained = false;
1812 drag_info.y_constrained = false;
1813 drag_info.x_constrained = true;
1816 drag_info.x_constrained = false;
1817 drag_info.y_constrained = false;
1820 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1821 drag_info.last_pointer_frame = drag_info.grab_frame;
1822 drag_info.current_pointer_frame = drag_info.grab_frame;
1823 drag_info.current_pointer_x = drag_info.grab_x;
1824 drag_info.current_pointer_y = drag_info.grab_y;
1825 drag_info.last_pointer_x = drag_info.current_pointer_x;
1826 drag_info.last_pointer_y = drag_info.current_pointer_y;
1827 drag_info.cumulative_x_drag = 0;
1828 drag_info.cumulative_y_drag = 0;
1829 drag_info.first_move = true;
1830 drag_info.move_threshold_passed = false;
1831 drag_info.want_move_threshold = false;
1832 drag_info.pointer_frame_offset = 0;
1833 drag_info.brushing = false;
1834 drag_info.copied_location = 0;
1836 drag_info.original_x = 0;
1837 drag_info.original_y = 0;
1838 drag_info.item->i2w (drag_info.original_x, drag_info.original_y);
1840 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1842 event->button.time);
1844 if (session && session->transport_rolling()) {
1845 drag_info.was_rolling = true;
1847 drag_info.was_rolling = false;
1850 switch (snap_type) {
1851 case SnapToRegionStart:
1852 case SnapToRegionEnd:
1853 case SnapToRegionSync:
1854 case SnapToRegionBoundary:
1855 build_region_boundary_cache ();
1863 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1865 drag_info.item->ungrab (0);
1866 drag_info.item = new_item;
1869 cursor = which_grabber_cursor ();
1872 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1876 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1878 bool did_drag = false;
1880 stop_canvas_autoscroll ();
1882 if (drag_info.item == 0) {
1886 drag_info.item->ungrab (event->button.time);
1888 if (drag_info.finished_callback) {
1889 drag_info.last_pointer_x = drag_info.current_pointer_x;
1890 drag_info.last_pointer_y = drag_info.current_pointer_y;
1891 (this->*(drag_info.finished_callback)) (item, event);
1894 did_drag = !drag_info.first_move;
1896 hide_verbose_canvas_cursor();
1904 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1906 drag_info.item = item;
1907 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1908 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1912 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1913 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1917 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1919 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1923 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1925 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1927 nframes_t fade_length;
1929 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1930 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1936 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1940 if (pos < (arv->region()->position() + 64)) {
1941 fade_length = 64; // this should be a minimum defined somewhere
1942 } else if (pos > arv->region()->last_frame()) {
1943 fade_length = arv->region()->length();
1945 fade_length = pos - arv->region()->position();
1947 /* mapover the region selection */
1949 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1951 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1957 tmp->reset_fade_in_shape_width (fade_length);
1960 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1962 drag_info.first_move = false;
1966 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1968 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1970 nframes_t fade_length;
1972 if (drag_info.first_move) return;
1974 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1975 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1980 if (pos < (arv->region()->position() + 64)) {
1981 fade_length = 64; // this should be a minimum defined somewhere
1982 } else if (pos > arv->region()->last_frame()) {
1983 fade_length = arv->region()->length();
1985 fade_length = pos - arv->region()->position();
1988 begin_reversible_command (_("change fade in length"));
1990 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1992 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1998 AutomationList& alist = tmp->audio_region()->fade_in();
1999 XMLNode &before = alist.get_state();
2001 tmp->audio_region()->set_fade_in_length (fade_length);
2002 tmp->audio_region()->set_fade_in_active (true);
2004 XMLNode &after = alist.get_state();
2005 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2008 commit_reversible_command ();
2012 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2014 drag_info.item = item;
2015 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2016 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2020 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2021 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2025 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2027 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
2031 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2033 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2035 nframes_t fade_length;
2037 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2038 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2043 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2047 if (pos > (arv->region()->last_frame() - 64)) {
2048 fade_length = 64; // this should really be a minimum fade defined somewhere
2050 else if (pos < arv->region()->position()) {
2051 fade_length = arv->region()->length();
2054 fade_length = arv->region()->last_frame() - pos;
2057 /* mapover the region selection */
2059 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2061 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2067 tmp->reset_fade_out_shape_width (fade_length);
2070 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2072 drag_info.first_move = false;
2076 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2078 if (drag_info.first_move) return;
2080 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2082 nframes_t fade_length;
2084 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2085 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2091 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2095 if (pos > (arv->region()->last_frame() - 64)) {
2096 fade_length = 64; // this should really be a minimum fade defined somewhere
2098 else if (pos < arv->region()->position()) {
2099 fade_length = arv->region()->length();
2102 fade_length = arv->region()->last_frame() - pos;
2105 begin_reversible_command (_("change fade out length"));
2107 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2109 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2115 AutomationList& alist = tmp->audio_region()->fade_out();
2116 XMLNode &before = alist.get_state();
2118 tmp->audio_region()->set_fade_out_length (fade_length);
2119 tmp->audio_region()->set_fade_out_active (true);
2121 XMLNode &after = alist.get_state();
2122 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2125 commit_reversible_command ();
2129 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2131 drag_info.item = item;
2132 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2133 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2137 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2138 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2142 Cursor* cursor = (Cursor *) drag_info.data;
2144 if (cursor == playhead_cursor) {
2145 _dragging_playhead = true;
2147 if (session && drag_info.was_rolling) {
2148 session->request_stop ();
2151 if (session && session->is_auditioning()) {
2152 session->cancel_audition ();
2156 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2158 show_verbose_time_cursor (cursor->current_frame, 10);
2162 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2164 Cursor* cursor = (Cursor *) drag_info.data;
2165 nframes_t adjusted_frame;
2167 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2168 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2174 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2175 if (cursor == playhead_cursor) {
2176 snap_to (adjusted_frame);
2180 if (adjusted_frame == drag_info.last_pointer_frame) return;
2182 cursor->set_position (adjusted_frame);
2184 UpdateAllTransportClocks (cursor->current_frame);
2186 show_verbose_time_cursor (cursor->current_frame, 10);
2188 drag_info.last_pointer_frame = adjusted_frame;
2189 drag_info.first_move = false;
2193 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2195 if (drag_info.first_move) return;
2197 cursor_drag_motion_callback (item, event);
2199 _dragging_playhead = false;
2201 if (item == &playhead_cursor->canvas_item) {
2203 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2209 Editor::update_marker_drag_item (Location *location)
2211 double x1 = frame_to_pixel (location->start());
2212 double x2 = frame_to_pixel (location->end());
2214 if (location->is_mark()) {
2215 marker_drag_line_points.front().set_x(x1);
2216 marker_drag_line_points.back().set_x(x1);
2217 marker_drag_line->property_points() = marker_drag_line_points;
2220 range_marker_drag_rect->property_x1() = x1;
2221 range_marker_drag_rect->property_x2() = x2;
2226 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2230 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2231 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2237 Location *location = find_location_from_marker (marker, is_start);
2239 drag_info.item = item;
2240 drag_info.data = marker;
2241 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2242 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2246 _dragging_edit_point = true;
2248 drag_info.copied_location = new Location (*location);
2249 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2251 update_marker_drag_item (location);
2253 if (location->is_mark()) {
2254 // marker_drag_line->show();
2255 // marker_drag_line->raise_to_top();
2257 range_marker_drag_rect->show();
2258 range_marker_drag_rect->raise_to_top();
2262 show_verbose_time_cursor (location->start(), 10);
2264 show_verbose_time_cursor (location->end(), 10);
2267 Selection::Operation op = Keyboard::selection_type (event->button.state);
2270 case Selection::Toggle:
2271 selection->toggle (marker);
2273 case Selection::Set:
2274 selection->set (marker);
2276 case Selection::Extend:
2277 selection->add (marker);
2279 case Selection::Add:
2280 selection->add (marker);
2286 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2289 Marker* marker = (Marker *) drag_info.data;
2290 Location *real_location;
2291 Location *copy_location;
2293 bool move_both = false;
2296 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2297 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2302 nframes_t next = newframe;
2304 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2305 snap_to (newframe, 0, true);
2308 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2312 /* call this to find out if its the start or end */
2314 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2318 if (real_location->locked()) {
2322 /* use the copy that we're "dragging" around */
2324 copy_location = drag_info.copied_location;
2326 f_delta = copy_location->end() - copy_location->start();
2328 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2332 if (copy_location->is_mark()) {
2335 copy_location->set_start (newframe);
2339 if (is_start) { // start-of-range marker
2342 copy_location->set_start (newframe);
2343 copy_location->set_end (newframe + f_delta);
2344 } else if (newframe < copy_location->end()) {
2345 copy_location->set_start (newframe);
2347 snap_to (next, 1, true);
2348 copy_location->set_end (next);
2349 copy_location->set_start (newframe);
2352 } else { // end marker
2355 copy_location->set_end (newframe);
2356 copy_location->set_start (newframe - f_delta);
2357 } else if (newframe > copy_location->start()) {
2358 copy_location->set_end (newframe);
2360 } else if (newframe > 0) {
2361 snap_to (next, -1, true);
2362 copy_location->set_start (next);
2363 copy_location->set_end (newframe);
2368 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2369 drag_info.first_move = false;
2371 update_marker_drag_item (copy_location);
2373 LocationMarkers* lm = find_location_markers (real_location);
2374 lm->set_position (copy_location->start(), copy_location->end());
2375 edit_point_clock.set (copy_location->start());
2377 show_verbose_time_cursor (newframe, 10);
2381 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2383 if (drag_info.first_move) {
2384 /* just a click, do nothing but whatever selection occured */
2388 _dragging_edit_point = false;
2390 Marker* marker = (Marker *) drag_info.data;
2393 begin_reversible_command ( _("move marker") );
2394 XMLNode &before = session->locations()->get_state();
2396 Location * location = find_location_from_marker (marker, is_start);
2400 if (location->locked()) {
2404 if (location->is_mark()) {
2405 location->set_start (drag_info.copied_location->start());
2407 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2411 XMLNode &after = session->locations()->get_state();
2412 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2413 commit_reversible_command ();
2415 marker_drag_line->hide();
2416 range_marker_drag_rect->hide();
2420 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2423 MeterMarker* meter_marker;
2425 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2426 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2430 meter_marker = dynamic_cast<MeterMarker*> (marker);
2432 MetricSection& section (meter_marker->meter());
2434 if (!section.movable()) {
2438 drag_info.item = item;
2439 drag_info.copy = false;
2440 drag_info.data = marker;
2441 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2442 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2446 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2448 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2452 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2455 MeterMarker* meter_marker;
2457 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2458 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2462 meter_marker = dynamic_cast<MeterMarker*> (marker);
2464 // create a dummy marker for visual representation of moving the copy.
2465 // The actual copying is not done before we reach the finish callback.
2467 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2468 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2469 *new MeterSection(meter_marker->meter()));
2471 drag_info.item = &new_marker->the_item();
2472 drag_info.copy = true;
2473 drag_info.data = new_marker;
2474 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2475 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2479 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2481 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2485 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2487 MeterMarker* marker = (MeterMarker *) drag_info.data;
2488 nframes_t adjusted_frame;
2490 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2491 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2497 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2498 snap_to (adjusted_frame);
2501 if (adjusted_frame == drag_info.last_pointer_frame) return;
2503 marker->set_position (adjusted_frame);
2506 drag_info.last_pointer_frame = adjusted_frame;
2507 drag_info.first_move = false;
2509 show_verbose_time_cursor (adjusted_frame, 10);
2513 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2515 if (drag_info.first_move) return;
2517 meter_marker_drag_motion_callback (drag_info.item, event);
2519 MeterMarker* marker = (MeterMarker *) drag_info.data;
2522 TempoMap& map (session->tempo_map());
2523 map.bbt_time (drag_info.last_pointer_frame, when);
2525 if (drag_info.copy == true) {
2526 begin_reversible_command (_("copy meter mark"));
2527 XMLNode &before = map.get_state();
2528 map.add_meter (marker->meter(), when);
2529 XMLNode &after = map.get_state();
2530 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2531 commit_reversible_command ();
2533 // delete the dummy marker we used for visual representation of copying.
2534 // a new visual marker will show up automatically.
2537 begin_reversible_command (_("move meter mark"));
2538 XMLNode &before = map.get_state();
2539 map.move_meter (marker->meter(), when);
2540 XMLNode &after = map.get_state();
2541 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2542 commit_reversible_command ();
2547 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2550 TempoMarker* tempo_marker;
2552 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2553 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2557 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2558 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2562 MetricSection& section (tempo_marker->tempo());
2564 if (!section.movable()) {
2568 drag_info.item = item;
2569 drag_info.copy = false;
2570 drag_info.data = marker;
2571 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2572 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2576 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2577 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2581 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2584 TempoMarker* tempo_marker;
2586 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2587 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2591 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2592 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2596 // create a dummy marker for visual representation of moving the copy.
2597 // The actual copying is not done before we reach the finish callback.
2599 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2600 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2601 *new TempoSection(tempo_marker->tempo()));
2603 drag_info.item = &new_marker->the_item();
2604 drag_info.copy = true;
2605 drag_info.data = new_marker;
2606 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2607 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2611 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2613 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2617 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2619 TempoMarker* marker = (TempoMarker *) drag_info.data;
2620 nframes_t adjusted_frame;
2622 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2623 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2629 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2630 snap_to (adjusted_frame);
2633 if (adjusted_frame == drag_info.last_pointer_frame) return;
2635 /* OK, we've moved far enough to make it worth actually move the thing. */
2637 marker->set_position (adjusted_frame);
2639 show_verbose_time_cursor (adjusted_frame, 10);
2641 drag_info.last_pointer_frame = adjusted_frame;
2642 drag_info.first_move = false;
2646 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2648 if (drag_info.first_move) return;
2650 tempo_marker_drag_motion_callback (drag_info.item, event);
2652 TempoMarker* marker = (TempoMarker *) drag_info.data;
2655 TempoMap& map (session->tempo_map());
2656 map.bbt_time (drag_info.last_pointer_frame, when);
2658 if (drag_info.copy == true) {
2659 begin_reversible_command (_("copy tempo mark"));
2660 XMLNode &before = map.get_state();
2661 map.add_tempo (marker->tempo(), when);
2662 XMLNode &after = map.get_state();
2663 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2664 commit_reversible_command ();
2666 // delete the dummy marker we used for visual representation of copying.
2667 // a new visual marker will show up automatically.
2670 begin_reversible_command (_("move tempo mark"));
2671 XMLNode &before = map.get_state();
2672 map.move_tempo (marker->tempo(), when);
2673 XMLNode &after = map.get_state();
2674 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2675 commit_reversible_command ();
2680 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2682 ControlPoint* control_point;
2684 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2685 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2689 // We shouldn't remove the first or last gain point
2690 if (control_point->line.is_last_point(*control_point) ||
2691 control_point->line.is_first_point(*control_point)) {
2695 control_point->line.remove_point (*control_point);
2699 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2701 ControlPoint* control_point;
2703 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2704 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2708 control_point->line.remove_point (*control_point);
2712 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2714 ControlPoint* control_point;
2716 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2717 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2721 drag_info.item = item;
2722 drag_info.data = control_point;
2723 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2724 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2726 start_grab (event, fader_cursor);
2728 // start the grab at the center of the control point so
2729 // the point doesn't 'jump' to the mouse after the first drag
2730 drag_info.grab_x = control_point->get_x();
2731 drag_info.grab_y = control_point->get_y();
2732 control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2733 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y,
2734 drag_info.grab_x, drag_info.grab_y);
2736 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2738 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2740 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2741 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2742 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2744 show_verbose_canvas_cursor ();
2748 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2750 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2752 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2753 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2755 if (event->button.state & Keyboard::SecondaryModifier) {
2760 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2761 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2763 // calculate zero crossing point. back off by .01 to stay on the
2764 // positive side of zero
2766 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01;
2767 cp->line.parent_group().i2w(_unused, zero_gain_y);
2769 // make sure we hit zero when passing through
2770 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2771 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2775 if (drag_info.x_constrained) {
2776 cx = drag_info.grab_x;
2778 if (drag_info.y_constrained) {
2779 cy = drag_info.grab_y;
2782 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2783 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2785 cp->line.parent_group().w2i (cx, cy);
2789 cy = min ((double) cp->line.height(), cy);
2791 //translate cx to frames
2792 nframes_t cx_frames = unit_to_frame (cx);
2794 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2795 snap_to (cx_frames);
2798 float fraction = 1.0 - (cy / cp->line.height());
2802 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2808 cp->line.point_drag (*cp, cx_frames , fraction, push);
2810 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2812 drag_info.first_move = false;
2816 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2818 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2820 if (drag_info.first_move) {
2824 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2825 reset_point_selection ();
2829 control_point_drag_motion_callback (item, event);
2831 cp->line.end_drag (cp);
2835 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2837 switch (mouse_mode) {
2839 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2840 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2848 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2852 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2853 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2857 start_line_grab (al, event);
2861 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2865 nframes_t frame_within_region;
2867 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2871 cx = event->button.x;
2872 cy = event->button.y;
2873 line->parent_group().w2i (cx, cy);
2874 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2876 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2877 current_line_drag_info.after)) {
2878 /* no adjacent points */
2882 drag_info.item = &line->grab_item();
2883 drag_info.data = line;
2884 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2885 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2887 start_grab (event, fader_cursor);
2889 double fraction = 1.0 - (cy / line->height());
2891 line->start_drag (0, drag_info.grab_frame, fraction);
2893 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2894 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2895 show_verbose_canvas_cursor ();
2899 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2901 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2903 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2905 if (event->button.state & Keyboard::SecondaryModifier) {
2909 double cx = drag_info.current_pointer_x;
2910 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2912 // calculate zero crossing point. back off by .01 to stay on the
2913 // positive side of zero
2915 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
2916 line->parent_group().i2w(_unused, zero_gain_y);
2918 // make sure we hit zero when passing through
2919 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2920 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2924 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2926 line->parent_group().w2i (cx, cy);
2929 cy = min ((double) line->height(), cy);
2932 fraction = 1.0 - (cy / line->height());
2936 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2942 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2944 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2948 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2950 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2951 line_drag_motion_callback (item, event);
2956 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2958 if (selection->regions.empty() || clicked_regionview == 0) {
2962 drag_info.copy = false;
2963 drag_info.item = item;
2964 drag_info.data = clicked_regionview;
2966 if (Config->get_edit_mode() == Splice) {
2967 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
2968 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
2970 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2971 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2977 TimeAxisView* tvp = clicked_trackview;
2978 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2980 if (tv && tv->is_audio_track()) {
2981 speed = tv->get_diskstream()->speed();
2984 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2985 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2986 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
2987 drag_info.dest_trackview = drag_info.source_trackview;
2988 // we want a move threshold
2989 drag_info.want_move_threshold = true;
2991 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2993 begin_reversible_command (_("move region(s)"));
2997 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2999 if (selection->regions.empty() || clicked_regionview == 0) {
3003 drag_info.copy = true;
3004 drag_info.item = item;
3005 drag_info.data = clicked_regionview;
3009 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3010 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
3013 if (atv && atv->is_audio_track()) {
3014 speed = atv->get_diskstream()->speed();
3017 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3018 drag_info.dest_trackview = drag_info.source_trackview;
3019 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
3020 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3021 // we want a move threshold
3022 drag_info.want_move_threshold = true;
3023 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3024 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3025 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3029 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3031 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3035 drag_info.copy = false;
3036 drag_info.item = item;
3037 drag_info.data = clicked_regionview;
3038 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3039 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3044 TimeAxisView* tvp = clicked_trackview;
3045 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3047 if (tv && tv->is_audio_track()) {
3048 speed = tv->get_diskstream()->speed();
3051 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
3052 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3053 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3054 drag_info.dest_trackview = drag_info.source_trackview;
3055 // we want a move threshold
3056 drag_info.want_move_threshold = true;
3057 drag_info.brushing = true;
3059 begin_reversible_command (_("Drag region brush"));
3063 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3065 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3067 drag_info.want_move_threshold = false; // don't copy again
3069 /* duplicate the regionview(s) and region(s) */
3071 vector<RegionView*> new_regionviews;
3073 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3076 AudioRegionView* arv;
3081 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
3082 /* XXX handle MIDI here */
3086 const boost::shared_ptr<const Region> original = arv->region();
3087 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3088 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (region_copy);
3090 nrv = new AudioRegionView (*arv, ar);
3091 nrv->get_canvas_group()->show ();
3093 new_regionviews.push_back (nrv);
3096 if (new_regionviews.empty()) {
3100 /* reset selection to new regionviews. This will not set selection visual status for
3101 these regionviews since they don't belong to a track, so do that by hand too.
3104 selection->set (new_regionviews);
3106 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3107 (*i)->set_selected (true);
3110 /* reset drag_info data to reflect the fact that we are dragging the copies */
3112 drag_info.data = new_regionviews.front();
3114 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3119 Editor::check_region_drag_possible (AudioTimeAxisView** tv)
3121 /* Which trackview is this ? */
3123 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3124 (*tv) = dynamic_cast<AudioTimeAxisView*>(tvp);
3126 /* The region motion is only processed if the pointer is over
3130 if (!(*tv) || !(*tv)->is_audio_track()) {
3131 /* To make sure we hide the verbose canvas cursor when the mouse is
3132 not held over and audiotrack.
3134 hide_verbose_canvas_cursor ();
3141 struct RegionSelectionByPosition {
3142 bool operator() (RegionView*a, RegionView* b) {
3143 return a->region()->position () < b->region()->position();
3148 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3150 AudioTimeAxisView* tv;
3152 if (!check_region_drag_possible (&tv)) {
3156 if (!drag_info.move_threshold_passed) {
3162 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3168 RegionSelection copy (selection->regions);
3170 RegionSelectionByPosition cmp;
3173 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3175 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(*i)->get_time_axis_view());
3181 boost::shared_ptr<Playlist> playlist;
3183 if ((playlist = atv->playlist()) == 0) {
3187 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3192 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3196 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3202 playlist->shuffle ((*i)->region(), dir);
3204 drag_info.grab_x = drag_info.current_pointer_x;
3209 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3214 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3218 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3219 nframes_t pending_region_position = 0;
3220 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3221 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3222 bool clamp_y_axis = false;
3223 vector<int32_t> height_list(512) ;
3224 vector<int32_t>::iterator j;
3225 AudioTimeAxisView* tv;
3227 possibly_copy_regions_during_grab (event);
3229 if (!check_region_drag_possible (&tv)) {
3233 original_pointer_order = drag_info.dest_trackview->order;
3235 /************************************************************
3237 ************************************************************/
3239 if (drag_info.brushing) {
3240 clamp_y_axis = true;
3245 if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3247 int32_t children = 0, numtracks = 0;
3248 // XXX hard coding track limit, oh my, so very very bad
3249 bitset <1024> tracks (0x00);
3250 /* get a bitmask representing the visible tracks */
3252 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3253 TimeAxisView *tracklist_timeview;
3254 tracklist_timeview = (*i);
3255 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3256 list<TimeAxisView*> children_list;
3258 /* zeroes are audio tracks. ones are other types. */
3260 if (!atv2->hidden()) {
3262 if (visible_y_high < atv2->order) {
3263 visible_y_high = atv2->order;
3265 if (visible_y_low > atv2->order) {
3266 visible_y_low = atv2->order;
3269 if (!atv2->is_audio_track()) {
3270 tracks = tracks |= (0x01 << atv2->order);
3273 height_list[atv2->order] = (*i)->height;
3275 if ((children_list = atv2->get_child_list()).size() > 0) {
3276 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3277 tracks = tracks |= (0x01 << (atv2->order + children));
3278 height_list[atv2->order + children] = (*j)->height;
3286 /* find the actual span according to the canvas */
3288 canvas_pointer_y_span = pointer_y_span;
3289 if (drag_info.dest_trackview->order >= tv->order) {
3291 for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3292 if (height_list[y] == 0 ) {
3293 canvas_pointer_y_span--;
3298 for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3299 if ( height_list[y] == 0 ) {
3300 canvas_pointer_y_span++;
3305 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3306 RegionView* rv2 = (*i);
3307 double ix1, ix2, iy1, iy2;
3310 if (rv2->region()->locked()) {
3314 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3315 rv2->get_canvas_group()->i2w (ix1, iy1);
3316 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3317 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3319 if (atv2->order != original_pointer_order) {
3320 /* this isn't the pointer track */
3322 if (canvas_pointer_y_span > 0) {
3324 /* moving up the canvas */
3325 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3327 int32_t visible_tracks = 0;
3328 while (visible_tracks < canvas_pointer_y_span ) {
3331 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3332 /* we're passing through a hidden track */
3337 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3338 clamp_y_axis = true;
3342 clamp_y_axis = true;
3345 } else if (canvas_pointer_y_span < 0) {
3347 /*moving down the canvas*/
3349 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3352 int32_t visible_tracks = 0;
3354 while (visible_tracks > canvas_pointer_y_span ) {
3357 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3361 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3362 clamp_y_axis = true;
3367 clamp_y_axis = true;
3373 /* this is the pointer's track */
3374 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3375 clamp_y_axis = true;
3376 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3377 clamp_y_axis = true;
3385 } else if (drag_info.dest_trackview == tv) {
3386 clamp_y_axis = true;
3390 if (!clamp_y_axis) {
3391 drag_info.dest_trackview = tv;
3394 /************************************************************
3396 ************************************************************/
3398 /* compute the amount of pointer motion in frames, and where
3399 the region would be if we moved it by that much.
3402 if ( drag_info.move_threshold_passed ) {
3404 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3406 nframes_t sync_frame;
3407 nframes_t sync_offset;
3410 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3412 sync_offset = rv->region()->sync_offset (sync_dir);
3414 /* we don't handle a sync point that lies before zero.
3416 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3417 sync_frame = pending_region_position + (sync_dir*sync_offset);
3419 /* we snap if the snap modifier is not enabled.
3422 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3423 snap_to (sync_frame);
3426 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3429 pending_region_position = drag_info.last_frame_position;
3433 pending_region_position = 0;
3436 if (pending_region_position > max_frames - rv->region()->length()) {
3437 pending_region_position = drag_info.last_frame_position;
3440 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3442 bool x_move_allowed;
3444 if (Config->get_edit_mode() == Lock) {
3445 if (drag_info.copy) {
3446 x_move_allowed = !drag_info.x_constrained;
3448 /* in locked edit mode, reverse the usual meaning of x_constrained */
3449 x_move_allowed = drag_info.x_constrained;
3452 x_move_allowed = !drag_info.x_constrained;
3455 if ( pending_region_position != drag_info.last_frame_position && x_move_allowed ) {
3457 /* now compute the canvas unit distance we need to move the regionview
3458 to make it appear at the new location.
3461 if (pending_region_position > drag_info.last_frame_position) {
3462 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3464 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3467 drag_info.last_frame_position = pending_region_position;
3474 /* threshold not passed */
3479 /*************************************************************
3481 ************************************************************/
3483 if (x_delta == 0 && (pointer_y_span == 0)) {
3484 /* haven't reached next snap point, and we're not switching
3485 trackviews. nothing to do.
3492 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3494 RegionView* rv2 = (*i);
3496 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3498 double ix1, ix2, iy1, iy2;
3499 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3500 rv2->get_canvas_group()->i2w (ix1, iy1);
3509 /*************************************************************
3511 ************************************************************/
3515 if (drag_info.first_move) {
3516 if (drag_info.move_threshold_passed) {
3527 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3528 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3530 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3532 RegionView* rv = (*i);
3533 double ix1, ix2, iy1, iy2;
3534 int32_t temp_pointer_y_span = pointer_y_span;
3536 if (rv->region()->locked()) {
3540 /* get item BBox, which will be relative to parent. so we have
3541 to query on a child, then convert to world coordinates using
3545 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3546 rv->get_canvas_group()->i2w (ix1, iy1);
3547 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3548 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3549 AudioTimeAxisView* temp_atv;
3551 if ((pointer_y_span != 0) && !clamp_y_axis) {
3554 for (j = height_list.begin(); j!= height_list.end(); j++) {
3555 if (x == canvas_atv->order) {
3556 /* we found the track the region is on */
3557 if (x != original_pointer_order) {
3558 /*this isn't from the same track we're dragging from */
3559 temp_pointer_y_span = canvas_pointer_y_span;
3561 while (temp_pointer_y_span > 0) {
3562 /* we're moving up canvas-wise,
3563 so we need to find the next track height
3565 if (j != height_list.begin()) {
3568 if (x != original_pointer_order) {
3569 /* we're not from the dragged track, so ignore hidden tracks. */
3571 temp_pointer_y_span++;
3575 temp_pointer_y_span--;
3578 while (temp_pointer_y_span < 0) {
3580 if (x != original_pointer_order) {
3582 temp_pointer_y_span--;
3586 if (j != height_list.end()) {
3589 temp_pointer_y_span++;
3591 /* find out where we'll be when we move and set height accordingly */
3593 tvp2 = trackview_by_y_position (iy1 + y_delta);
3594 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3595 rv->set_height (temp_atv->height);
3597 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3598 personally, i think this can confuse things, but never mind.
3601 //const GdkColor& col (temp_atv->view->get_region_color());
3602 //rv->set_color (const_cast<GdkColor&>(col));
3610 /* prevent the regionview from being moved to before
3611 the zero position on the canvas.
3616 if (-x_delta > ix1) {
3619 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3620 x_delta = max_frames - rv->region()->last_frame();
3624 if (drag_info.first_move) {
3626 /* hide any dependent views */
3628 rv->get_time_axis_view().hide_dependent_views (*rv);
3630 /* this is subtle. raising the regionview itself won't help,
3631 because raise_to_top() just puts the item on the top of
3632 its parent's stack. so, we need to put the trackview canvas_display group
3633 on the top, since its parent is the whole canvas.
3636 rv->get_canvas_group()->raise_to_top();
3637 rv->get_time_axis_view().canvas_display->raise_to_top();
3638 cursor_group->raise_to_top();
3639 rv->fake_set_opaque (true);
3642 if (drag_info.brushing) {
3643 mouse_brush_insert_region (rv, pending_region_position);
3645 rv->move (x_delta, y_delta);
3648 } /* foreach region */
3652 if (drag_info.first_move && drag_info.move_threshold_passed) {
3653 cursor_group->raise_to_top();
3654 drag_info.first_move = false;
3657 if (x_delta != 0 && !drag_info.brushing) {
3658 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3663 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3665 bool nocommit = true;
3666 vector<RegionView*> copies;
3667 RouteTimeAxisView* source_tv;
3668 boost::shared_ptr<Diskstream> ds;
3669 boost::shared_ptr<Playlist> from_playlist;
3670 vector<RegionView*> new_selection;
3672 /* first_move is set to false if the regionview has been moved in the
3676 if (drag_info.first_move) {
3683 /* The regionview has been moved at some stage during the grab so we need
3684 to account for any mouse movement between this event and the last one.
3687 region_drag_motion_callback (item, event);
3689 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3690 selection->set (pre_drag_region_selection);
3691 pre_drag_region_selection.clear ();
3694 if (drag_info.brushing) {
3695 /* all changes were made during motion event handlers */
3697 if (drag_info.copy) {
3698 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3699 copies.push_back (*i);
3708 if (drag_info.copy) {
3709 if (drag_info.x_constrained) {
3710 op_string = _("fixed time region copy");
3712 op_string = _("region copy");
3715 if (drag_info.x_constrained) {
3716 op_string = _("fixed time region drag");
3718 op_string = _("region drag");
3722 begin_reversible_command (op_string);
3724 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3726 RegionView* rv = (*i);
3727 double ix1, ix2, iy1, iy2;
3728 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3729 rv->get_canvas_group()->i2w (ix1, iy1);
3730 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
3731 AudioTimeAxisView* dest_atv = dynamic_cast<AudioTimeAxisView*>(dest_tv);
3733 bool changed_tracks;
3734 bool changed_position;
3737 if (rv->region()->locked()) {
3742 /* adjust for track speed */
3746 if (dest_atv && dest_atv->get_diskstream()) {
3747 speed = dest_atv->get_diskstream()->speed();
3750 changed_position = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3751 changed_tracks = (dest_tv != &rv->get_time_axis_view());
3753 if (changed_position && !drag_info.x_constrained) {
3754 where = (nframes_t) (unit_to_frame (ix1) * speed);
3756 where = rv->region()->position();
3759 /* undo the previous hide_dependent_views so that xfades don't
3760 disappear on copying regions
3763 rv->get_time_axis_view().reveal_dependent_views (*rv);
3765 boost::shared_ptr<Region> new_region;
3767 if (drag_info.copy) {
3768 /* we already made a copy */
3769 new_region = rv->region();
3771 new_region = RegionFactory::create (rv->region());
3774 if (changed_tracks || drag_info.copy) {
3776 boost::shared_ptr<Playlist> to_playlist = dest_atv->playlist();
3778 latest_regionviews.clear ();
3780 sigc::connection c = dest_atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3781 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3782 to_playlist->add_region (new_region, where);
3783 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3786 if (!latest_regionviews.empty()) {
3787 // XXX why just the first one ? we only expect one
3788 dest_atv->reveal_dependent_views (*latest_regionviews.front());
3789 new_selection.push_back (latest_regionviews.front());
3794 /* just change the model */
3796 rv->region()->set_position (where, (void*) this);
3799 if (changed_tracks && !drag_info.copy) {
3801 /* get the playlist where this drag started. we can't use rv->region()->playlist()
3802 because we may have copied the region and it has not been attached to a playlist.
3805 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
3806 assert ((ds = source_tv->get_diskstream()));
3807 assert ((from_playlist = ds->playlist()));
3809 /* moved to a different audio track, without copying */
3811 /* the region that used to be in the old playlist is not
3812 moved to the new one - we use a copy of it. as a result,
3813 any existing editor for the region should no longer be
3817 rv->hide_region_editor();
3818 rv->fake_set_opaque (false);
3820 /* remove the region from the old playlist */
3822 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3823 from_playlist->remove_region ((rv->region()));
3824 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3826 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3827 was selected in all of them, then removing it from a playlist will have removed all
3828 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3829 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3830 corresponding regionview, and the selection is now empty).
3832 this could have invalidated any and all iterators into the region selection.
3834 the heuristic we use here is: if the region selection is empty, break out of the loop
3835 here. if the region selection is not empty, then restart the loop because we know that
3836 we must have removed at least the region(view) we've just been working on as well as any
3837 that we processed on previous iterations.
3839 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3840 we can just iterate.
3843 if (selection->regions.empty()) {
3846 i = selection->regions.by_layer().begin();
3853 if (drag_info.copy) {
3854 copies.push_back (rv);
3858 if (new_selection.empty()) {
3859 if (drag_info.copy) {
3860 /* the region(view)s that are selected and being dragged around
3861 are copies and do not belong to any track. remove them
3862 from the selection right here.
3864 selection->clear_regions();
3867 /* this will clear any existing selection that would have been
3868 cleared in the other clause above
3870 selection->set (new_selection);
3875 commit_reversible_command ();
3878 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3884 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3886 /* Either add to or set the set the region selection, unless
3887 this is an alignment click (control used)
3890 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
3891 TimeAxisView* tv = &rv.get_time_axis_view();
3892 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3894 if (atv && atv->is_audio_track()) {
3895 speed = atv->get_diskstream()->speed();
3898 nframes64_t where = get_preferred_edit_position();
3902 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
3904 align_region (rv.region(), SyncPoint, (nframes_t) (where * speed));
3906 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
3908 align_region (rv.region(), End, (nframes_t) (where * speed));
3912 align_region (rv.region(), Start, (nframes_t) (where * speed));
3919 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3925 nframes_t frame_rate;
3934 if (Profile->get_sae() || Profile->get_small_screen()) {
3935 m = ARDOUR_UI::instance()->primary_clock.mode();
3937 m = ARDOUR_UI::instance()->secondary_clock.mode();
3941 case AudioClock::BBT:
3942 session->bbt_time (frame, bbt);
3943 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3946 case AudioClock::SMPTE:
3947 session->smpte_time (frame, smpte);
3948 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3951 case AudioClock::MinSec:
3952 /* XXX this is copied from show_verbose_duration_cursor() */
3953 frame_rate = session->frame_rate();
3954 hours = frame / (frame_rate * 3600);
3955 frame = frame % (frame_rate * 3600);
3956 mins = frame / (frame_rate * 60);
3957 frame = frame % (frame_rate * 60);
3958 secs = (float) frame / (float) frame_rate;
3959 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3963 snprintf (buf, sizeof(buf), "%u", frame);
3967 if (xpos >= 0 && ypos >=0) {
3968 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3971 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3973 show_verbose_canvas_cursor ();
3977 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3984 nframes_t distance, frame_rate;
3986 Meter meter_at_start(session->tempo_map().meter_at(start));
3994 if (Profile->get_sae() || Profile->get_small_screen()) {
3995 m = ARDOUR_UI::instance()->primary_clock.mode ();
3997 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4001 case AudioClock::BBT:
4002 session->bbt_time (start, sbbt);
4003 session->bbt_time (end, ebbt);
4006 /* XXX this computation won't work well if the
4007 user makes a selection that spans any meter changes.
4010 ebbt.bars -= sbbt.bars;
4011 if (ebbt.beats >= sbbt.beats) {
4012 ebbt.beats -= sbbt.beats;
4015 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4017 if (ebbt.ticks >= sbbt.ticks) {
4018 ebbt.ticks -= sbbt.ticks;
4021 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4024 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4027 case AudioClock::SMPTE:
4028 session->smpte_duration (end - start, smpte);
4029 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4032 case AudioClock::MinSec:
4033 /* XXX this stuff should be elsewhere.. */
4034 distance = end - start;
4035 frame_rate = session->frame_rate();
4036 hours = distance / (frame_rate * 3600);
4037 distance = distance % (frame_rate * 3600);
4038 mins = distance / (frame_rate * 60);
4039 distance = distance % (frame_rate * 60);
4040 secs = (float) distance / (float) frame_rate;
4041 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4045 snprintf (buf, sizeof(buf), "%u", end - start);
4049 if (xpos >= 0 && ypos >=0) {
4050 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4053 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4056 show_verbose_canvas_cursor ();
4060 Editor::collect_new_region_view (RegionView* rv)
4062 latest_regionviews.push_back (rv);
4066 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4068 if (clicked_regionview == 0) {
4072 /* lets try to create new Region for the selection */
4074 vector<boost::shared_ptr<AudioRegion> > new_regions;
4075 create_region_from_selection (new_regions);
4077 if (new_regions.empty()) {
4081 /* XXX fix me one day to use all new regions */
4083 boost::shared_ptr<Region> region (new_regions.front());
4085 /* add it to the current stream/playlist.
4087 tricky: the streamview for the track will add a new regionview. we will
4088 catch the signal it sends when it creates the regionview to
4089 set the regionview we want to then drag.
4092 latest_regionviews.clear();
4093 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4095 /* A selection grab currently creates two undo/redo operations, one for
4096 creating the new region and another for moving it.
4099 begin_reversible_command (_("selection grab"));
4101 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
4103 XMLNode *before = &(playlist->get_state());
4104 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
4105 XMLNode *after = &(playlist->get_state());
4106 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4108 commit_reversible_command ();
4112 if (latest_regionviews.empty()) {
4113 /* something went wrong */
4117 /* we need to deselect all other regionviews, and select this one
4118 i'm ignoring undo stuff, because the region creation will take care of it
4120 selection->set (latest_regionviews);
4122 drag_info.item = latest_regionviews.front()->get_canvas_group();
4123 drag_info.data = latest_regionviews.front();
4124 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4125 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4129 drag_info.source_trackview = clicked_trackview;
4130 drag_info.dest_trackview = drag_info.source_trackview;
4131 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4132 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4134 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4138 Editor::cancel_selection ()
4140 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4141 (*i)->hide_selection ();
4143 selection->clear ();
4144 clicked_selection = 0;
4148 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4150 nframes_t start = 0;
4157 drag_info.item = item;
4158 drag_info.motion_callback = &Editor::drag_selection;
4159 drag_info.finished_callback = &Editor::end_selection_op;
4164 case CreateSelection:
4165 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4166 drag_info.copy = true;
4168 drag_info.copy = false;
4170 start_grab (event, selector_cursor);
4173 case SelectionStartTrim:
4174 if (clicked_trackview) {
4175 clicked_trackview->order_selection_trims (item, true);
4177 start_grab (event, trimmer_cursor);
4178 start = selection->time[clicked_selection].start;
4179 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4182 case SelectionEndTrim:
4183 if (clicked_trackview) {
4184 clicked_trackview->order_selection_trims (item, false);
4186 start_grab (event, trimmer_cursor);
4187 end = selection->time[clicked_selection].end;
4188 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4192 start = selection->time[clicked_selection].start;
4194 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4198 if (selection_op == SelectionMove) {
4199 show_verbose_time_cursor(start, 10);
4201 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4206 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4208 nframes_t start = 0;
4211 nframes_t pending_position;
4213 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4214 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4216 pending_position = 0;
4219 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4220 snap_to (pending_position);
4223 /* only alter selection if the current frame is
4224 different from the last frame position (adjusted)
4227 if (pending_position == drag_info.last_pointer_frame) return;
4229 switch (selection_op) {
4230 case CreateSelection:
4232 if (drag_info.first_move) {
4233 snap_to (drag_info.grab_frame);
4236 if (pending_position < drag_info.grab_frame) {
4237 start = pending_position;
4238 end = drag_info.grab_frame;
4240 end = pending_position;
4241 start = drag_info.grab_frame;
4244 /* first drag: Either add to the selection
4245 or create a new selection->
4248 if (drag_info.first_move) {
4250 begin_reversible_command (_("range selection"));
4252 if (drag_info.copy) {
4253 /* adding to the selection */
4254 clicked_selection = selection->add (start, end);
4255 drag_info.copy = false;
4257 /* new selection-> */
4258 clicked_selection = selection->set (clicked_trackview, start, end);
4263 case SelectionStartTrim:
4265 if (drag_info.first_move) {
4266 begin_reversible_command (_("trim selection start"));
4269 start = selection->time[clicked_selection].start;
4270 end = selection->time[clicked_selection].end;
4272 if (pending_position > end) {
4275 start = pending_position;
4279 case SelectionEndTrim:
4281 if (drag_info.first_move) {
4282 begin_reversible_command (_("trim selection end"));
4285 start = selection->time[clicked_selection].start;
4286 end = selection->time[clicked_selection].end;
4288 if (pending_position < start) {
4291 end = pending_position;
4298 if (drag_info.first_move) {
4299 begin_reversible_command (_("move selection"));
4302 start = selection->time[clicked_selection].start;
4303 end = selection->time[clicked_selection].end;
4305 length = end - start;
4307 start = pending_position;
4310 end = start + length;
4315 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4316 start_canvas_autoscroll (1, 0);
4320 selection->replace (clicked_selection, start, end);
4323 drag_info.last_pointer_frame = pending_position;
4324 drag_info.first_move = false;
4326 if (selection_op == SelectionMove) {
4327 show_verbose_time_cursor(start, 10);
4329 show_verbose_time_cursor(pending_position, 10);
4334 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4336 if (!drag_info.first_move) {
4337 drag_selection (item, event);
4338 /* XXX this is not object-oriented programming at all. ick */
4339 if (selection->time.consolidate()) {
4340 selection->TimeChanged ();
4342 commit_reversible_command ();
4344 /* just a click, no pointer movement.*/
4346 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4348 selection->clear_time();
4353 /* XXX what happens if its a music selection? */
4354 session->set_audio_range (selection->time);
4355 stop_canvas_autoscroll ();
4359 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4362 TimeAxisView* tvp = clicked_trackview;
4363 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4365 if (tv && tv->is_audio_track()) {
4366 speed = tv->get_diskstream()->speed();
4369 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4370 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4371 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4373 //drag_info.item = clicked_regionview->get_name_highlight();
4374 drag_info.item = item;
4375 drag_info.motion_callback = &Editor::trim_motion_callback;
4376 drag_info.finished_callback = &Editor::trim_finished_callback;
4378 start_grab (event, trimmer_cursor);
4380 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4381 trim_op = ContentsTrim;
4383 /* These will get overridden for a point trim.*/
4384 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4385 /* closer to start */
4386 trim_op = StartTrim;
4387 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4395 show_verbose_time_cursor(region_start, 10);
4398 show_verbose_time_cursor(region_end, 10);
4401 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4407 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4409 RegionView* rv = clicked_regionview;
4410 nframes_t frame_delta = 0;
4411 bool left_direction;
4412 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4414 /* snap modifier works differently here..
4415 its' current state has to be passed to the
4416 various trim functions in order to work properly
4420 TimeAxisView* tvp = clicked_trackview;
4421 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4422 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4424 if (tv && tv->is_audio_track()) {
4425 speed = tv->get_diskstream()->speed();
4428 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4429 left_direction = true;
4431 left_direction = false;
4435 snap_to (drag_info.current_pointer_frame);
4438 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4442 if (drag_info.first_move) {
4448 trim_type = "Region start trim";
4451 trim_type = "Region end trim";
4454 trim_type = "Region content trim";
4458 begin_reversible_command (trim_type);
4460 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4461 (*i)->fake_set_opaque(false);
4462 (*i)->region()->freeze ();
4464 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4466 arv->temporarily_hide_envelope ();
4468 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4469 insert_result = motion_frozen_playlists.insert (pl);
4470 if (insert_result.second) {
4471 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4476 if (left_direction) {
4477 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4479 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4484 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4487 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4488 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4494 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4497 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4498 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4505 bool swap_direction = false;
4507 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4508 swap_direction = true;
4511 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4512 i != selection->regions.by_layer().end(); ++i)
4514 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4522 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4525 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4528 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4532 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4533 drag_info.first_move = false;
4537 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4539 boost::shared_ptr<Region> region (rv.region());
4541 if (region->locked()) {
4545 nframes_t new_bound;
4548 TimeAxisView* tvp = clicked_trackview;
4549 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4551 if (tv && tv->is_audio_track()) {
4552 speed = tv->get_diskstream()->speed();
4555 if (left_direction) {
4556 if (swap_direction) {
4557 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4559 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4562 if (swap_direction) {
4563 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4565 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4570 snap_to (new_bound);
4572 region->trim_start ((nframes_t) (new_bound * speed), this);
4573 rv.region_changed (StartChanged);
4577 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4579 boost::shared_ptr<Region> region (rv.region());
4581 if (region->locked()) {
4585 nframes_t new_bound;
4588 TimeAxisView* tvp = clicked_trackview;
4589 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4591 if (tv && tv->is_audio_track()) {
4592 speed = tv->get_diskstream()->speed();
4595 if (left_direction) {
4596 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4598 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4602 snap_to (new_bound, (left_direction ? 0 : 1));
4605 region->trim_front ((nframes_t) (new_bound * speed), this);
4607 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4611 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4613 boost::shared_ptr<Region> region (rv.region());
4615 if (region->locked()) {
4619 nframes_t new_bound;
4622 TimeAxisView* tvp = clicked_trackview;
4623 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4625 if (tv && tv->is_audio_track()) {
4626 speed = tv->get_diskstream()->speed();
4629 if (left_direction) {
4630 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4632 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4636 snap_to (new_bound);
4638 region->trim_end ((nframes_t) (new_bound * speed), this);
4639 rv.region_changed (LengthChanged);
4643 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4645 if (!drag_info.first_move) {
4646 trim_motion_callback (item, event);
4648 if (!selection->selected (clicked_regionview)) {
4649 thaw_region_after_trim (*clicked_regionview);
4652 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4653 i != selection->regions.by_layer().end(); ++i)
4655 thaw_region_after_trim (**i);
4656 (*i)->fake_set_opaque (true);
4660 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4662 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4665 motion_frozen_playlists.clear ();
4667 commit_reversible_command();
4669 /* no mouse movement */
4675 Editor::point_trim (GdkEvent* event)
4677 RegionView* rv = clicked_regionview;
4678 nframes_t new_bound = drag_info.current_pointer_frame;
4680 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4681 snap_to (new_bound);
4684 /* Choose action dependant on which button was pressed */
4685 switch (event->button.button) {
4687 trim_op = StartTrim;
4688 begin_reversible_command (_("Start point trim"));
4690 if (selection->selected (rv)) {
4692 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4693 i != selection->regions.by_layer().end(); ++i)
4695 if (!(*i)->region()->locked()) {
4696 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4697 XMLNode &before = pl->get_state();
4698 (*i)->region()->trim_front (new_bound, this);
4699 XMLNode &after = pl->get_state();
4700 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4706 if (!rv->region()->locked()) {
4707 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4708 XMLNode &before = pl->get_state();
4709 rv->region()->trim_front (new_bound, this);
4710 XMLNode &after = pl->get_state();
4711 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4715 commit_reversible_command();
4720 begin_reversible_command (_("End point trim"));
4722 if (selection->selected (rv)) {
4724 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4726 if (!(*i)->region()->locked()) {
4727 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4728 XMLNode &before = pl->get_state();
4729 (*i)->region()->trim_end (new_bound, this);
4730 XMLNode &after = pl->get_state();
4731 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4737 if (!rv->region()->locked()) {
4738 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4739 XMLNode &before = pl->get_state();
4740 rv->region()->trim_end (new_bound, this);
4741 XMLNode &after = pl->get_state();
4742 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4746 commit_reversible_command();
4755 Editor::thaw_region_after_trim (RegionView& rv)
4757 boost::shared_ptr<Region> region (rv.region());
4759 if (region->locked()) {
4763 region->thaw (_("trimmed region"));
4764 XMLNode &after = region->playlist()->get_state();
4765 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4767 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4769 arv->unhide_envelope ();
4773 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4778 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4779 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4783 Location* location = find_location_from_marker (marker, is_start);
4784 location->set_hidden (true, this);
4789 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4795 drag_info.item = item;
4796 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4797 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4799 range_marker_op = op;
4801 if (!temp_location) {
4802 temp_location = new Location;
4806 case CreateRangeMarker:
4807 case CreateTransportMarker:
4808 case CreateCDMarker:
4810 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4811 drag_info.copy = true;
4813 drag_info.copy = false;
4815 start_grab (event, selector_cursor);
4819 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4824 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4826 nframes_t start = 0;
4828 ArdourCanvas::SimpleRect *crect;
4830 switch (range_marker_op) {
4831 case CreateRangeMarker:
4832 crect = range_bar_drag_rect;
4834 case CreateTransportMarker:
4835 crect = transport_bar_drag_rect;
4837 case CreateCDMarker:
4838 crect = cd_marker_bar_drag_rect;
4841 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
4846 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4847 snap_to (drag_info.current_pointer_frame);
4850 /* only alter selection if the current frame is
4851 different from the last frame position.
4854 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4856 switch (range_marker_op) {
4857 case CreateRangeMarker:
4858 case CreateTransportMarker:
4859 case CreateCDMarker:
4860 if (drag_info.first_move) {
4861 snap_to (drag_info.grab_frame);
4864 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4865 start = drag_info.current_pointer_frame;
4866 end = drag_info.grab_frame;
4868 end = drag_info.current_pointer_frame;
4869 start = drag_info.grab_frame;
4872 /* first drag: Either add to the selection
4873 or create a new selection.
4876 if (drag_info.first_move) {
4878 temp_location->set (start, end);
4882 update_marker_drag_item (temp_location);
4883 range_marker_drag_rect->show();
4884 range_marker_drag_rect->raise_to_top();
4890 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4891 start_canvas_autoscroll (1, 0);
4895 temp_location->set (start, end);
4897 double x1 = frame_to_pixel (start);
4898 double x2 = frame_to_pixel (end);
4899 crect->property_x1() = x1;
4900 crect->property_x2() = x2;
4902 update_marker_drag_item (temp_location);
4905 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4906 drag_info.first_move = false;
4908 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4913 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4915 Location * newloc = 0;
4919 if (!drag_info.first_move) {
4920 drag_range_markerbar_op (item, event);
4922 switch (range_marker_op) {
4923 case CreateRangeMarker:
4924 case CreateCDMarker:
4926 begin_reversible_command (_("new range marker"));
4927 XMLNode &before = session->locations()->get_state();
4928 session->locations()->next_available_name(rangename,"unnamed");
4929 if (range_marker_op == CreateCDMarker) {
4930 flags = Location::IsRangeMarker|Location::IsCDMarker;
4931 cd_marker_bar_drag_rect->hide();
4934 flags = Location::IsRangeMarker;
4935 range_bar_drag_rect->hide();
4937 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
4938 session->locations()->add (newloc, true);
4939 XMLNode &after = session->locations()->get_state();
4940 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4941 commit_reversible_command ();
4943 range_marker_drag_rect->hide();
4947 case CreateTransportMarker:
4948 // popup menu to pick loop or punch
4949 new_transport_marker_context_menu (&event->button, item);
4954 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4956 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
4961 start = session->locations()->first_mark_before (drag_info.grab_frame);
4962 end = session->locations()->first_mark_after (drag_info.grab_frame);
4964 if (end == max_frames) {
4965 end = session->current_end_frame ();
4969 start = session->current_start_frame ();
4972 switch (mouse_mode) {
4974 /* find the two markers on either side and then make the selection from it */
4975 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4979 /* find the two markers on either side of the click and make the range out of it */
4980 selection->set (0, start, end);
4989 stop_canvas_autoscroll ();
4995 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4997 drag_info.item = item;
4998 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4999 drag_info.finished_callback = &Editor::end_mouse_zoom;
5001 start_grab (event, zoom_cursor);
5003 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5007 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5012 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5013 snap_to (drag_info.current_pointer_frame);
5015 if (drag_info.first_move) {
5016 snap_to (drag_info.grab_frame);
5020 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5022 /* base start and end on initial click position */
5023 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5024 start = drag_info.current_pointer_frame;
5025 end = drag_info.grab_frame;
5027 end = drag_info.current_pointer_frame;
5028 start = drag_info.grab_frame;
5033 if (drag_info.first_move) {
5035 zoom_rect->raise_to_top();
5038 reposition_zoom_rect(start, end);
5040 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5041 drag_info.first_move = false;
5043 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5048 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5050 if (!drag_info.first_move) {
5051 drag_mouse_zoom (item, event);
5053 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5054 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5056 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5059 temporal_zoom_to_frame (false, drag_info.grab_frame);
5061 temporal_zoom_step (false);
5062 center_screen (drag_info.grab_frame);
5070 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
5072 double x1 = frame_to_pixel (start);
5073 double x2 = frame_to_pixel (end);
5074 double y2 = full_canvas_height - 1.0;
5076 zoom_rect->property_x1() = x1;
5077 zoom_rect->property_y1() = 1.0;
5078 zoom_rect->property_x2() = x2;
5079 zoom_rect->property_y2() = y2;
5083 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5085 drag_info.item = item;
5086 drag_info.motion_callback = &Editor::drag_rubberband_select;
5087 drag_info.finished_callback = &Editor::end_rubberband_select;
5089 start_grab (event, cross_hair_cursor);
5091 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5095 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5102 /* use a bigger drag threshold than the default */
5104 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5108 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5109 if (drag_info.first_move) {
5110 snap_to (drag_info.grab_frame);
5112 snap_to (drag_info.current_pointer_frame);
5115 /* base start and end on initial click position */
5117 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5118 start = drag_info.current_pointer_frame;
5119 end = drag_info.grab_frame;
5121 end = drag_info.current_pointer_frame;
5122 start = drag_info.grab_frame;
5125 if (drag_info.current_pointer_y < drag_info.grab_y) {
5126 y1 = drag_info.current_pointer_y;
5127 y2 = drag_info.grab_y;
5129 y2 = drag_info.current_pointer_y;
5130 y1 = drag_info.grab_y;
5134 if (start != end || y1 != y2) {
5136 double x1 = frame_to_pixel (start);
5137 double x2 = frame_to_pixel (end);
5139 rubberband_rect->property_x1() = x1;
5140 rubberband_rect->property_y1() = y1;
5141 rubberband_rect->property_x2() = x2;
5142 rubberband_rect->property_y2() = y2;
5144 rubberband_rect->show();
5145 rubberband_rect->raise_to_top();
5147 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5148 drag_info.first_move = false;
5150 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5155 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5157 if (!drag_info.first_move) {
5159 drag_rubberband_select (item, event);
5162 if (drag_info.current_pointer_y < drag_info.grab_y) {
5163 y1 = drag_info.current_pointer_y;
5164 y2 = drag_info.grab_y;
5167 y2 = drag_info.current_pointer_y;
5168 y1 = drag_info.grab_y;
5172 Selection::Operation op = Keyboard::selection_type (event->button.state);
5175 begin_reversible_command (_("rubberband selection"));
5177 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5178 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5180 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5184 commit_reversible_command ();
5188 selection->clear_tracks();
5189 selection->clear_regions();
5190 selection->clear_points ();
5191 selection->clear_lines ();
5194 rubberband_rect->hide();
5199 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5201 using namespace Gtkmm2ext;
5203 ArdourPrompter prompter (false);
5205 prompter.set_prompt (_("Name for region:"));
5206 prompter.set_initial_text (clicked_regionview->region()->name());
5207 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5208 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5209 prompter.show_all ();
5210 switch (prompter.run ()) {
5211 case Gtk::RESPONSE_ACCEPT:
5213 prompter.get_result(str);
5215 clicked_regionview->region()->set_name (str);
5223 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5225 drag_info.item = item;
5226 drag_info.motion_callback = &Editor::time_fx_motion;
5227 drag_info.finished_callback = &Editor::end_time_fx;
5231 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5235 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5237 RegionView* rv = clicked_regionview;
5239 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5240 snap_to (drag_info.current_pointer_frame);
5243 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5247 if (drag_info.current_pointer_frame > rv->region()->position()) {
5248 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5251 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5252 drag_info.first_move = false;
5254 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5258 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5260 clicked_regionview->get_time_axis_view().hide_timestretch ();
5262 if (drag_info.first_move) {
5266 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5267 /* backwards drag of the left edge - not usable */
5271 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5272 #ifdef USE_RUBBERBAND
5273 float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5275 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5278 begin_reversible_command (_("timestretch"));
5280 // XXX how do timeFX on multiple regions ?
5283 rs.add (clicked_regionview);
5285 if (time_stretch (rs, percentage) == 0) {
5286 session->commit_reversible_command ();
5291 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5293 /* no brushing without a useful snap setting */
5296 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5299 switch (snap_mode) {
5301 return; /* can't work because it allows region to be placed anywhere */
5306 switch (snap_type) {
5314 /* don't brush a copy over the original */
5316 if (pos == rv->region()->position()) {
5320 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5322 if (atv == 0 || !atv->is_audio_track()) {
5326 boost::shared_ptr<Playlist> playlist = atv->playlist();
5327 double speed = atv->get_diskstream()->speed();
5329 XMLNode &before = playlist->get_state();
5330 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5331 XMLNode &after = playlist->get_state();
5332 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5334 // playlist is frozen, so we have to update manually
5336 playlist->Modified(); /* EMIT SIGNAL */
5340 Editor::track_height_step_timeout ()
5343 struct timeval delta;
5345 gettimeofday (&now, 0);
5346 timersub (&now, &last_track_height_step_timestamp, &delta);
5348 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5349 current_stepping_trackview = 0;