2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
31 #include <pbd/memento_command.h>
33 #include "ardour_ui.h"
35 #include "time_axis_view.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
39 #include "streamview.h"
40 #include "region_gain_line.h"
41 #include "automation_time_axis.h"
44 #include "selection.h"
47 #include "rgb_macros.h"
49 #include <ardour/types.h>
50 #include <ardour/profile.h>
51 #include <ardour/route.h>
52 #include <ardour/audio_track.h>
53 #include <ardour/audio_diskstream.h>
54 #include <ardour/playlist.h>
55 #include <ardour/audioplaylist.h>
56 #include <ardour/audioregion.h>
57 #include <ardour/dB.h>
58 #include <ardour/utils.h>
59 #include <ardour/region_factory.h>
66 using namespace ARDOUR;
70 using namespace Editing;
73 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
77 Gdk::ModifierType mask;
78 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
79 Glib::RefPtr<const Gdk::Window> pointer_window;
81 pointer_window = canvas_window->get_pointer (x, y, mask);
83 if (pointer_window == track_canvas->get_bin_window()) {
85 track_canvas->window_to_world (x, y, wx, wy);
86 in_track_canvas = true;
89 in_track_canvas = false;
91 if (pointer_window == time_canvas->get_bin_window()) {
92 time_canvas->window_to_world (x, y, wx, wy);
98 wx += horizontal_adjustment.get_value();
99 wy += vertical_adjustment.get_value();
102 event.type = GDK_BUTTON_RELEASE;
106 where = event_frame (&event, 0, 0);
111 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
125 switch (event->type) {
126 case GDK_BUTTON_RELEASE:
127 case GDK_BUTTON_PRESS:
128 case GDK_2BUTTON_PRESS:
129 case GDK_3BUTTON_PRESS:
130 track_canvas->w2c(event->button.x, event->button.y, *pcx, *pcy);
132 case GDK_MOTION_NOTIFY:
133 track_canvas->w2c(event->motion.x, event->motion.y, *pcx, *pcy);
135 case GDK_ENTER_NOTIFY:
136 case GDK_LEAVE_NOTIFY:
137 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
140 case GDK_KEY_RELEASE:
141 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
144 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
148 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
149 position is negative (as can be the case with motion events in particular),
150 the frame location is always positive.
153 return pixel_to_frame (*pcx);
157 Editor::mouse_mode_toggled (MouseMode m)
159 if (ignore_mouse_mode_toggle) {
165 if (mouse_select_button.get_active()) {
171 if (mouse_move_button.get_active()) {
177 if (mouse_gain_button.get_active()) {
183 if (mouse_zoom_button.get_active()) {
189 if (mouse_timefx_button.get_active()) {
195 if (mouse_audition_button.get_active()) {
206 Editor::which_grabber_cursor ()
208 switch (_edit_point) {
210 return grabber_edit_point_cursor;
213 return grabber_cursor;
219 Editor::set_canvas_cursor ()
221 switch (mouse_mode) {
223 current_canvas_cursor = selector_cursor;
227 current_canvas_cursor = which_grabber_cursor();
231 current_canvas_cursor = cross_hair_cursor;
235 current_canvas_cursor = zoom_cursor;
239 current_canvas_cursor = time_fx_cursor; // just use playhead
243 current_canvas_cursor = speaker_cursor;
248 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
253 Editor::set_mouse_mode (MouseMode m, bool force)
255 if (drag_info.item) {
259 if (!force && m == mouse_mode) {
267 if (mouse_mode != MouseRange) {
269 /* in all modes except range, hide the range selection,
270 show the object (region) selection.
273 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
274 (*i)->set_should_show_selection (true);
276 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
277 (*i)->hide_selection ();
283 in range mode,show the range selection.
286 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
287 if ((*i)->get_selected()) {
288 (*i)->show_selection (selection->time);
293 /* XXX the hack of unsetting all other buttons should go
294 away once GTK2 allows us to use regular radio buttons drawn like
295 normal buttons, rather than my silly GroupedButton hack.
298 ignore_mouse_mode_toggle = true;
300 switch (mouse_mode) {
302 mouse_select_button.set_active (true);
306 mouse_move_button.set_active (true);
310 mouse_gain_button.set_active (true);
314 mouse_zoom_button.set_active (true);
318 mouse_timefx_button.set_active (true);
322 mouse_audition_button.set_active (true);
326 ignore_mouse_mode_toggle = false;
328 set_canvas_cursor ();
332 Editor::step_mouse_mode (bool next)
334 switch (current_mouse_mode()) {
336 if (next) set_mouse_mode (MouseRange);
337 else set_mouse_mode (MouseTimeFX);
341 if (next) set_mouse_mode (MouseZoom);
342 else set_mouse_mode (MouseObject);
346 if (next) set_mouse_mode (MouseGain);
347 else set_mouse_mode (MouseRange);
351 if (next) set_mouse_mode (MouseTimeFX);
352 else set_mouse_mode (MouseZoom);
356 if (next) set_mouse_mode (MouseAudition);
357 else set_mouse_mode (MouseGain);
361 if (next) set_mouse_mode (MouseObject);
362 else set_mouse_mode (MouseTimeFX);
368 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
370 /* in object/audition/timefx mode, any button press sets
371 the selection if the object can be selected. this is a
372 bit of hack, because we want to avoid this if the
373 mouse operation is a region alignment.
375 note: not dbl-click or triple-click
378 if (((mouse_mode != MouseObject) &&
379 (mouse_mode != MouseAudition || item_type != RegionItem) &&
380 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
381 (mouse_mode != MouseRange)) ||
383 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
388 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
390 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
392 /* almost no selection action on modified button-2 or button-3 events */
394 if (item_type != RegionItem && event->button.button != 2) {
400 Selection::Operation op = Keyboard::selection_type (event->button.state);
401 bool press = (event->type == GDK_BUTTON_PRESS);
403 // begin_reversible_command (_("select on click"));
407 if (mouse_mode != MouseRange) {
408 set_selected_regionview_from_click (press, op, true);
409 } else if (event->type == GDK_BUTTON_PRESS) {
410 set_selected_track_as_side_effect ();
414 case RegionViewNameHighlight:
416 if (mouse_mode != MouseRange) {
417 set_selected_regionview_from_click (press, op, true);
418 } else if (event->type == GDK_BUTTON_PRESS) {
419 set_selected_track_as_side_effect ();
423 case FadeInHandleItem:
425 case FadeOutHandleItem:
427 if (mouse_mode != MouseRange) {
428 set_selected_regionview_from_click (press, op, true);
429 } else if (event->type == GDK_BUTTON_PRESS) {
430 set_selected_track_as_side_effect ();
434 case GainAutomationControlPointItem:
435 case PanAutomationControlPointItem:
436 case RedirectAutomationControlPointItem:
437 set_selected_track_as_side_effect ();
438 if (mouse_mode != MouseRange) {
439 set_selected_control_point_from_click (op, false);
444 /* for context click or range selection, select track */
445 if (event->button.button == 3) {
446 set_selected_track_as_side_effect ();
447 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
448 set_selected_track_as_side_effect ();
452 case AutomationTrackItem:
453 set_selected_track_as_side_effect (true);
461 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
464 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
466 track_canvas->grab_focus();
468 if (session && session->actively_recording()) {
472 button_selection (item, event, item_type);
474 if (drag_info.item == 0 &&
475 (Keyboard::is_delete_event (&event->button) ||
476 Keyboard::is_context_menu_event (&event->button) ||
477 Keyboard::is_edit_event (&event->button))) {
479 /* handled by button release */
483 switch (event->button.button) {
486 if (event->type == GDK_BUTTON_PRESS) {
488 if (drag_info.item) {
489 drag_info.item->ungrab (event->button.time);
492 /* single mouse clicks on any of these item types operate
493 independent of mouse mode, mostly because they are
494 not on the main track canvas or because we want
499 case PlayheadCursorItem:
500 start_cursor_grab (item, event);
504 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
505 hide_marker (item, event);
507 start_marker_grab (item, event);
511 case TempoMarkerItem:
512 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
513 start_tempo_marker_copy_grab (item, event);
515 start_tempo_marker_grab (item, event);
519 case MeterMarkerItem:
520 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
521 start_meter_marker_copy_grab (item, event);
523 start_meter_marker_grab (item, event);
533 case RangeMarkerBarItem:
534 start_range_markerbar_op (item, event, CreateRangeMarker);
538 case CdMarkerBarItem:
539 start_range_markerbar_op (item, event, CreateCDMarker);
543 case TransportMarkerBarItem:
544 start_range_markerbar_op (item, event, CreateTransportMarker);
553 switch (mouse_mode) {
556 case StartSelectionTrimItem:
557 start_selection_op (item, event, SelectionStartTrim);
560 case EndSelectionTrimItem:
561 start_selection_op (item, event, SelectionEndTrim);
565 if (Keyboard::modifier_state_contains
566 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
567 // contains and not equals because I can't use alt as a modifier alone.
568 start_selection_grab (item, event);
569 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
570 /* grab selection for moving */
571 start_selection_op (item, event, SelectionMove);
573 /* this was debated, but decided the more common action was to
574 make a new selection */
575 start_selection_op (item, event, CreateSelection);
580 start_selection_op (item, event, CreateSelection);
586 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
587 event->type == GDK_BUTTON_PRESS) {
589 start_rubberband_select (item, event);
591 } else if (event->type == GDK_BUTTON_PRESS) {
594 case FadeInHandleItem:
595 start_fade_in_grab (item, event);
598 case FadeOutHandleItem:
599 start_fade_out_grab (item, event);
603 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
604 start_region_copy_grab (item, event);
605 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
606 start_region_brush_grab (item, event);
608 start_region_grab (item, event);
612 case RegionViewNameHighlight:
613 start_trim (item, event);
618 /* rename happens on edit clicks */
619 start_trim (clicked_regionview->get_name_highlight(), event);
623 case GainAutomationControlPointItem:
624 case PanAutomationControlPointItem:
625 case RedirectAutomationControlPointItem:
626 start_control_point_grab (item, event);
630 case GainAutomationLineItem:
631 case PanAutomationLineItem:
632 case RedirectAutomationLineItem:
633 start_line_grab_from_line (item, event);
638 case AutomationTrackItem:
639 start_rubberband_select (item, event);
642 /* <CMT Additions> */
643 case ImageFrameHandleStartItem:
644 imageframe_start_handle_op(item, event) ;
647 case ImageFrameHandleEndItem:
648 imageframe_end_handle_op(item, event) ;
651 case MarkerViewHandleStartItem:
652 markerview_item_start_handle_op(item, event) ;
655 case MarkerViewHandleEndItem:
656 markerview_item_end_handle_op(item, event) ;
659 /* </CMT Additions> */
661 /* <CMT Additions> */
663 start_markerview_grab(item, event) ;
666 start_imageframe_grab(item, event) ;
668 /* </CMT Additions> */
684 // start_line_grab_from_regionview (item, event);
687 case GainControlPointItem:
688 start_control_point_grab (item, event);
692 start_line_grab_from_line (item, event);
695 case GainAutomationControlPointItem:
696 case PanAutomationControlPointItem:
697 case RedirectAutomationControlPointItem:
698 start_control_point_grab (item, event);
709 case GainAutomationControlPointItem:
710 case PanAutomationControlPointItem:
711 case RedirectAutomationControlPointItem:
712 start_control_point_grab (item, event);
715 case GainAutomationLineItem:
716 case PanAutomationLineItem:
717 case RedirectAutomationLineItem:
718 start_line_grab_from_line (item, event);
722 // XXX need automation mode to identify which
724 // start_line_grab_from_regionview (item, event);
734 if (event->type == GDK_BUTTON_PRESS) {
735 start_mouse_zoom (item, event);
742 if (item_type == RegionItem) {
743 start_time_fx (item, event);
750 scrub_reverse_distance = 0;
751 last_scrub_x = event->button.x;
752 scrubbing_direction = 0;
753 track_canvas->get_window()->set_cursor (*transparent_cursor);
754 /* rest handled in motion & release */
763 switch (mouse_mode) {
765 if (event->type == GDK_BUTTON_PRESS) {
768 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
769 start_region_copy_grab (item, event);
771 start_region_grab (item, event);
776 case GainAutomationControlPointItem:
777 case PanAutomationControlPointItem:
778 case RedirectAutomationControlPointItem:
779 start_control_point_grab (item, event);
790 case RegionViewNameHighlight:
791 start_trim (item, event);
796 start_trim (clicked_regionview->get_name_highlight(), event);
807 if (event->type == GDK_BUTTON_PRESS) {
808 /* relax till release */
815 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
816 temporal_zoom_session();
818 temporal_zoom_to_frame (true, event_frame(event));
841 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
843 nframes_t where = event_frame (event, 0, 0);
844 AutomationTimeAxisView* atv = 0;
846 /* no action if we're recording */
848 if (session && session->actively_recording()) {
852 /* first, see if we're finishing a drag ... */
854 if (drag_info.item) {
855 if (end_grab (item, event)) {
856 /* grab dragged, so do nothing else */
861 button_selection (item, event, item_type);
863 /* edit events get handled here */
865 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
871 case TempoMarkerItem:
872 edit_tempo_marker (item);
875 case MeterMarkerItem:
876 edit_meter_marker (item);
880 if (clicked_regionview->name_active()) {
881 return mouse_rename_region (item, event);
891 /* context menu events get handled here */
893 if (Keyboard::is_context_menu_event (&event->button)) {
895 if (drag_info.item == 0) {
897 /* no matter which button pops up the context menu, tell the menu
898 widget to use button 1 to drive menu selection.
903 case FadeInHandleItem:
905 case FadeOutHandleItem:
906 popup_fade_context_menu (1, event->button.time, item, item_type);
910 popup_track_context_menu (1, event->button.time, item_type, false, where);
914 case RegionViewNameHighlight:
916 popup_track_context_menu (1, event->button.time, item_type, false, where);
920 popup_track_context_menu (1, event->button.time, item_type, true, where);
923 case AutomationTrackItem:
924 popup_track_context_menu (1, event->button.time, item_type, false, where);
928 case RangeMarkerBarItem:
929 case TransportMarkerBarItem:
930 case CdMarkerBarItem:
933 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
937 marker_context_menu (&event->button, item);
940 case TempoMarkerItem:
941 tm_marker_context_menu (&event->button, item);
944 case MeterMarkerItem:
945 tm_marker_context_menu (&event->button, item);
948 case CrossfadeViewItem:
949 popup_track_context_menu (1, event->button.time, item_type, false, where);
952 /* <CMT Additions> */
954 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
956 case ImageFrameTimeAxisItem:
957 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
960 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
962 case MarkerTimeAxisItem:
963 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
965 /* <CMT Additions> */
976 /* delete events get handled here */
978 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
981 case TempoMarkerItem:
982 remove_tempo_marker (item);
985 case MeterMarkerItem:
986 remove_meter_marker (item);
990 remove_marker (*item, event);
994 if (mouse_mode == MouseObject) {
995 remove_clicked_region ();
999 case GainControlPointItem:
1000 if (mouse_mode == MouseGain) {
1001 remove_gain_control_point (item, event);
1005 case GainAutomationControlPointItem:
1006 case PanAutomationControlPointItem:
1007 case RedirectAutomationControlPointItem:
1008 remove_control_point (item, event);
1017 switch (event->button.button) {
1020 switch (item_type) {
1021 /* see comments in button_press_handler */
1022 case PlayheadCursorItem:
1025 case GainAutomationLineItem:
1026 case PanAutomationLineItem:
1027 case RedirectAutomationLineItem:
1028 case StartSelectionTrimItem:
1029 case EndSelectionTrimItem:
1033 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1034 snap_to (where, 0, true);
1036 mouse_add_new_marker (where);
1039 case CdMarkerBarItem:
1040 // if we get here then a dragged range wasn't done
1041 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1042 snap_to (where, 0, true);
1044 mouse_add_new_marker (where, true);
1048 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1051 mouse_add_new_tempo_event (where);
1055 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1063 switch (mouse_mode) {
1065 switch (item_type) {
1066 case AutomationTrackItem:
1067 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_trackview);
1069 atv->add_automation_event (item, event, where, event->button.y);
1081 // Gain only makes sense for audio regions
1083 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1087 switch (item_type) {
1089 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1093 case AutomationTrackItem:
1094 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1095 add_automation_event (item, event, where, event->button.y);
1105 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1106 if (scrubbing_direction == 0) {
1107 /* no drag, just a click */
1108 switch (item_type) {
1110 play_selected_region ();
1116 /* make sure we stop */
1117 session->request_transport_speed (0.0);
1131 switch (mouse_mode) {
1134 switch (item_type) {
1136 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1138 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1141 // Button2 click is unused
1154 // x_style_paste (where, 1.0);
1174 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1180 if (last_item_entered != item) {
1181 last_item_entered = item;
1182 last_item_entered_n = 0;
1185 switch (item_type) {
1186 case GainControlPointItem:
1187 if (mouse_mode == MouseGain) {
1188 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1189 cp->set_visible (true);
1193 at_y = cp->get_y ();
1194 cp->item->i2w (at_x, at_y);
1198 fraction = 1.0 - (cp->get_y() / cp->line.height());
1200 if (is_drawable() && !_scrubbing) {
1201 track_canvas->get_window()->set_cursor (*fader_cursor);
1204 last_item_entered_n++;
1205 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1206 if (last_item_entered_n < 10) {
1207 show_verbose_canvas_cursor ();
1212 case GainAutomationControlPointItem:
1213 case PanAutomationControlPointItem:
1214 case RedirectAutomationControlPointItem:
1215 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1216 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1217 cp->set_visible (true);
1221 at_y = cp->get_y ();
1222 cp->item->i2w (at_x, at_y);
1226 fraction = 1.0 - (cp->get_y() / cp->line.height());
1228 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1229 show_verbose_canvas_cursor ();
1231 if (is_drawable()) {
1232 track_canvas->get_window()->set_cursor (*fader_cursor);
1238 if (mouse_mode == MouseGain) {
1239 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1241 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1242 if (is_drawable()) {
1243 track_canvas->get_window()->set_cursor (*fader_cursor);
1248 case GainAutomationLineItem:
1249 case RedirectAutomationLineItem:
1250 case PanAutomationLineItem:
1251 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1253 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1255 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1257 if (is_drawable()) {
1258 track_canvas->get_window()->set_cursor (*fader_cursor);
1263 case RegionViewNameHighlight:
1264 if (is_drawable() && mouse_mode == MouseObject) {
1265 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1269 case StartSelectionTrimItem:
1270 case EndSelectionTrimItem:
1271 /* <CMT Additions> */
1272 case ImageFrameHandleStartItem:
1273 case ImageFrameHandleEndItem:
1274 case MarkerViewHandleStartItem:
1275 case MarkerViewHandleEndItem:
1276 /* </CMT Additions> */
1278 if (is_drawable()) {
1279 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1283 case PlayheadCursorItem:
1284 if (is_drawable()) {
1285 switch (_edit_point) {
1287 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1290 track_canvas->get_window()->set_cursor (*grabber_cursor);
1296 case RegionViewName:
1298 /* when the name is not an active item, the entire name highlight is for trimming */
1300 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1301 if (mouse_mode == MouseObject && is_drawable()) {
1302 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1308 case AutomationTrackItem:
1309 if (is_drawable()) {
1310 Gdk::Cursor *cursor;
1311 switch (mouse_mode) {
1313 cursor = selector_cursor;
1316 cursor = zoom_cursor;
1319 cursor = cross_hair_cursor;
1323 track_canvas->get_window()->set_cursor (*cursor);
1325 AutomationTimeAxisView* atv;
1326 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1327 clear_entered_track = false;
1328 set_entered_track (atv);
1334 case RangeMarkerBarItem:
1335 case TransportMarkerBarItem:
1336 case CdMarkerBarItem:
1339 if (is_drawable()) {
1340 time_canvas->get_window()->set_cursor (*timebar_cursor);
1345 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1348 entered_marker = marker;
1349 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1351 case MeterMarkerItem:
1352 case TempoMarkerItem:
1353 if (is_drawable()) {
1354 time_canvas->get_window()->set_cursor (*timebar_cursor);
1357 case FadeInHandleItem:
1358 case FadeOutHandleItem:
1359 if (mouse_mode == MouseObject) {
1360 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1362 rect->property_fill_color_rgba() = 0;
1363 rect->property_outline_pixels() = 1;
1372 /* second pass to handle entered track status in a comprehensible way.
1375 switch (item_type) {
1377 case GainAutomationLineItem:
1378 case RedirectAutomationLineItem:
1379 case PanAutomationLineItem:
1380 case GainControlPointItem:
1381 case GainAutomationControlPointItem:
1382 case PanAutomationControlPointItem:
1383 case RedirectAutomationControlPointItem:
1384 /* these do not affect the current entered track state */
1385 clear_entered_track = false;
1388 case AutomationTrackItem:
1389 /* handled above already */
1393 set_entered_track (0);
1401 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1410 switch (item_type) {
1411 case GainControlPointItem:
1412 case GainAutomationControlPointItem:
1413 case PanAutomationControlPointItem:
1414 case RedirectAutomationControlPointItem:
1415 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1416 if (cp->line.npoints() > 1) {
1417 if (!cp->selected) {
1418 cp->set_visible (false);
1422 if (is_drawable()) {
1423 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1426 hide_verbose_canvas_cursor ();
1429 case RegionViewNameHighlight:
1430 case StartSelectionTrimItem:
1431 case EndSelectionTrimItem:
1432 case PlayheadCursorItem:
1433 /* <CMT Additions> */
1434 case ImageFrameHandleStartItem:
1435 case ImageFrameHandleEndItem:
1436 case MarkerViewHandleStartItem:
1437 case MarkerViewHandleEndItem:
1438 /* </CMT Additions> */
1439 if (is_drawable()) {
1440 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1445 case GainAutomationLineItem:
1446 case RedirectAutomationLineItem:
1447 case PanAutomationLineItem:
1448 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1450 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1452 line->property_fill_color_rgba() = al->get_line_color();
1454 if (is_drawable()) {
1455 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1459 case RegionViewName:
1460 /* see enter_handler() for notes */
1461 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1462 if (is_drawable() && mouse_mode == MouseObject) {
1463 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1468 case RangeMarkerBarItem:
1469 case TransportMarkerBarItem:
1470 case CdMarkerBarItem:
1474 if (is_drawable()) {
1475 time_canvas->get_window()->set_cursor (*timebar_cursor);
1480 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1484 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1485 location_flags_changed (loc, this);
1488 case MeterMarkerItem:
1489 case TempoMarkerItem:
1491 if (is_drawable()) {
1492 time_canvas->get_window()->set_cursor (*timebar_cursor);
1497 case FadeInHandleItem:
1498 case FadeOutHandleItem:
1499 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1501 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1503 rect->property_fill_color_rgba() = rv->get_fill_color();
1504 rect->property_outline_pixels() = 0;
1509 case AutomationTrackItem:
1510 if (is_drawable()) {
1511 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1512 clear_entered_track = true;
1513 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1525 Editor::left_automation_track ()
1527 if (clear_entered_track) {
1528 set_entered_track (0);
1529 clear_entered_track = false;
1535 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1537 if (event->motion.is_hint) {
1540 /* We call this so that MOTION_NOTIFY events continue to be
1541 delivered to the canvas. We need to do this because we set
1542 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1543 the density of the events, at the expense of a round-trip
1544 to the server. Given that this will mostly occur on cases
1545 where DISPLAY = :0.0, and given the cost of what the motion
1546 event might do, its a good tradeoff.
1549 track_canvas->get_pointer (x, y);
1552 if (current_stepping_trackview) {
1553 /* don't keep the persistent stepped trackview if the mouse moves */
1554 current_stepping_trackview = 0;
1555 step_timeout.disconnect ();
1558 if (session && session->actively_recording()) {
1559 /* Sorry. no dragging stuff around while we record */
1563 drag_info.item_type = item_type;
1564 drag_info.last_pointer_x = drag_info.current_pointer_x;
1565 drag_info.last_pointer_y = drag_info.current_pointer_y;
1566 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1567 &drag_info.current_pointer_y);
1569 switch (mouse_mode) {
1575 if (scrubbing_direction == 0) {
1577 session->request_locate (drag_info.current_pointer_frame, false);
1578 session->request_transport_speed (0.1);
1579 scrubbing_direction = 1;
1583 if (last_scrub_x > drag_info.current_pointer_x) {
1585 /* pointer moved to the left */
1587 if (scrubbing_direction > 0) {
1589 /* we reversed direction to go backwards */
1592 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1596 /* still moving to the left (backwards) */
1598 scrub_reversals = 0;
1599 scrub_reverse_distance = 0;
1601 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1602 session->request_transport_speed (session->transport_speed() - delta);
1606 /* pointer moved to the right */
1608 if (scrubbing_direction < 0) {
1609 /* we reversed direction to go forward */
1612 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1615 /* still moving to the right */
1617 scrub_reversals = 0;
1618 scrub_reverse_distance = 0;
1620 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1621 session->request_transport_speed (session->transport_speed() + delta);
1625 /* if there have been more than 2 opposite motion moves detected, or one that moves
1626 back more than 10 pixels, reverse direction
1629 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1631 if (scrubbing_direction > 0) {
1632 /* was forwards, go backwards */
1633 session->request_transport_speed (-0.1);
1634 scrubbing_direction = -1;
1636 /* was backwards, go forwards */
1637 session->request_transport_speed (0.1);
1638 scrubbing_direction = 1;
1641 scrub_reverse_distance = 0;
1642 scrub_reversals = 0;
1646 last_scrub_x = drag_info.current_pointer_x;
1653 if (!from_autoscroll && drag_info.item) {
1654 /* item != 0 is the best test i can think of for dragging.
1656 if (!drag_info.move_threshold_passed) {
1658 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1659 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1661 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1663 // and change the initial grab loc/frame if this drag info wants us to
1665 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1666 drag_info.grab_frame = drag_info.current_pointer_frame;
1667 drag_info.grab_x = drag_info.current_pointer_x;
1668 drag_info.grab_y = drag_info.current_pointer_y;
1669 drag_info.last_pointer_frame = drag_info.grab_frame;
1670 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1675 switch (item_type) {
1676 case PlayheadCursorItem:
1678 case GainControlPointItem:
1679 case RedirectAutomationControlPointItem:
1680 case GainAutomationControlPointItem:
1681 case PanAutomationControlPointItem:
1682 case TempoMarkerItem:
1683 case MeterMarkerItem:
1684 case RegionViewNameHighlight:
1685 case StartSelectionTrimItem:
1686 case EndSelectionTrimItem:
1689 case RedirectAutomationLineItem:
1690 case GainAutomationLineItem:
1691 case PanAutomationLineItem:
1692 case FadeInHandleItem:
1693 case FadeOutHandleItem:
1694 /* <CMT Additions> */
1695 case ImageFrameHandleStartItem:
1696 case ImageFrameHandleEndItem:
1697 case MarkerViewHandleStartItem:
1698 case MarkerViewHandleEndItem:
1699 /* </CMT Additions> */
1700 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1701 (event->motion.state & Gdk::BUTTON2_MASK))) {
1702 if (!from_autoscroll) {
1703 maybe_autoscroll (event);
1705 (this->*(drag_info.motion_callback)) (item, event);
1714 switch (mouse_mode) {
1719 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1720 (event->motion.state & GDK_BUTTON2_MASK))) {
1721 if (!from_autoscroll) {
1722 maybe_autoscroll (event);
1724 (this->*(drag_info.motion_callback)) (item, event);
1735 track_canvas_motion (event);
1736 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1744 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1746 if (drag_info.item == 0) {
1747 fatal << _("programming error: start_grab called without drag item") << endmsg;
1753 cursor = which_grabber_cursor ();
1756 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1758 if (event->button.button == 2) {
1759 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1760 drag_info.y_constrained = true;
1761 drag_info.x_constrained = false;
1763 drag_info.y_constrained = false;
1764 drag_info.x_constrained = true;
1767 drag_info.x_constrained = false;
1768 drag_info.y_constrained = false;
1771 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1772 drag_info.last_pointer_frame = drag_info.grab_frame;
1773 drag_info.current_pointer_frame = drag_info.grab_frame;
1774 drag_info.current_pointer_x = drag_info.grab_x;
1775 drag_info.current_pointer_y = drag_info.grab_y;
1776 drag_info.last_pointer_x = drag_info.current_pointer_x;
1777 drag_info.last_pointer_y = drag_info.current_pointer_y;
1778 drag_info.cumulative_x_drag = 0;
1779 drag_info.cumulative_y_drag = 0;
1780 drag_info.first_move = true;
1781 drag_info.move_threshold_passed = false;
1782 drag_info.want_move_threshold = false;
1783 drag_info.pointer_frame_offset = 0;
1784 drag_info.brushing = false;
1785 drag_info.copied_location = 0;
1787 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1789 event->button.time);
1791 if (session && session->transport_rolling()) {
1792 drag_info.was_rolling = true;
1794 drag_info.was_rolling = false;
1797 switch (snap_type) {
1798 case SnapToRegionStart:
1799 case SnapToRegionEnd:
1800 case SnapToRegionSync:
1801 case SnapToRegionBoundary:
1802 build_region_boundary_cache ();
1810 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1812 drag_info.item->ungrab (0);
1813 drag_info.item = new_item;
1816 cursor = which_grabber_cursor ();
1819 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1823 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1825 bool did_drag = false;
1827 stop_canvas_autoscroll ();
1829 if (drag_info.item == 0) {
1833 drag_info.item->ungrab (event->button.time);
1835 if (drag_info.finished_callback) {
1836 drag_info.last_pointer_x = drag_info.current_pointer_x;
1837 drag_info.last_pointer_y = drag_info.current_pointer_y;
1838 (this->*(drag_info.finished_callback)) (item, event);
1841 did_drag = !drag_info.first_move;
1843 hide_verbose_canvas_cursor();
1846 drag_info.copy = false;
1847 drag_info.motion_callback = 0;
1848 drag_info.finished_callback = 0;
1849 drag_info.dest_trackview = 0;
1850 drag_info.source_trackview = 0;
1851 drag_info.last_frame_position = 0;
1852 drag_info.grab_frame = 0;
1853 drag_info.last_pointer_frame = 0;
1854 drag_info.current_pointer_frame = 0;
1855 drag_info.brushing = false;
1857 if (drag_info.copied_location) {
1858 delete drag_info.copied_location;
1859 drag_info.copied_location = 0;
1866 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1868 drag_info.item = item;
1869 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1870 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1874 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1875 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1879 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1881 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1885 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1887 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1889 nframes_t fade_length;
1891 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1892 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1898 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1902 if (pos < (arv->region()->position() + 64)) {
1903 fade_length = 64; // this should be a minimum defined somewhere
1904 } else if (pos > arv->region()->last_frame()) {
1905 fade_length = arv->region()->length();
1907 fade_length = pos - arv->region()->position();
1909 /* mapover the region selection */
1911 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1913 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1919 tmp->reset_fade_in_shape_width (fade_length);
1922 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1924 drag_info.first_move = false;
1928 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1930 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1932 nframes_t fade_length;
1934 if (drag_info.first_move) return;
1936 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1937 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1942 if (pos < (arv->region()->position() + 64)) {
1943 fade_length = 64; // this should be a minimum defined somewhere
1944 } else if (pos > arv->region()->last_frame()) {
1945 fade_length = arv->region()->length();
1947 fade_length = pos - arv->region()->position();
1950 begin_reversible_command (_("change fade in length"));
1952 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1954 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1960 AutomationList& alist = tmp->audio_region()->fade_in();
1961 XMLNode &before = alist.get_state();
1963 tmp->audio_region()->set_fade_in_length (fade_length);
1964 tmp->audio_region()->set_fade_in_active (true);
1966 XMLNode &after = alist.get_state();
1967 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1970 commit_reversible_command ();
1974 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1976 drag_info.item = item;
1977 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1978 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1982 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1983 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1987 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1989 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
1993 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1995 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1997 nframes_t fade_length;
1999 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2000 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2005 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2009 if (pos > (arv->region()->last_frame() - 64)) {
2010 fade_length = 64; // this should really be a minimum fade defined somewhere
2012 else if (pos < arv->region()->position()) {
2013 fade_length = arv->region()->length();
2016 fade_length = arv->region()->last_frame() - pos;
2019 /* mapover the region selection */
2021 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2023 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2029 tmp->reset_fade_out_shape_width (fade_length);
2032 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2034 drag_info.first_move = false;
2038 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2040 if (drag_info.first_move) return;
2042 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2044 nframes_t fade_length;
2046 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2047 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2053 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2057 if (pos > (arv->region()->last_frame() - 64)) {
2058 fade_length = 64; // this should really be a minimum fade defined somewhere
2060 else if (pos < arv->region()->position()) {
2061 fade_length = arv->region()->length();
2064 fade_length = arv->region()->last_frame() - pos;
2067 begin_reversible_command (_("change fade out length"));
2069 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2071 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2077 AutomationList& alist = tmp->audio_region()->fade_out();
2078 XMLNode &before = alist.get_state();
2080 tmp->audio_region()->set_fade_out_length (fade_length);
2081 tmp->audio_region()->set_fade_out_active (true);
2083 XMLNode &after = alist.get_state();
2084 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2087 commit_reversible_command ();
2091 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2093 drag_info.item = item;
2094 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2095 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2099 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2100 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2104 Cursor* cursor = (Cursor *) drag_info.data;
2106 if (cursor == playhead_cursor) {
2107 _dragging_playhead = true;
2109 if (session && drag_info.was_rolling) {
2110 session->request_stop ();
2113 if (session && session->is_auditioning()) {
2114 session->cancel_audition ();
2118 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2120 show_verbose_time_cursor (cursor->current_frame, 10);
2124 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2126 Cursor* cursor = (Cursor *) drag_info.data;
2127 nframes_t adjusted_frame;
2129 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2130 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2136 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2137 if (cursor == playhead_cursor) {
2138 snap_to (adjusted_frame);
2142 if (adjusted_frame == drag_info.last_pointer_frame) return;
2144 cursor->set_position (adjusted_frame);
2146 UpdateAllTransportClocks (cursor->current_frame);
2148 show_verbose_time_cursor (cursor->current_frame, 10);
2150 drag_info.last_pointer_frame = adjusted_frame;
2151 drag_info.first_move = false;
2155 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2157 if (drag_info.first_move) return;
2159 cursor_drag_motion_callback (item, event);
2161 _dragging_playhead = false;
2163 if (item == &playhead_cursor->canvas_item) {
2165 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2171 Editor::update_marker_drag_item (Location *location)
2173 double x1 = frame_to_pixel (location->start());
2174 double x2 = frame_to_pixel (location->end());
2176 if (location->is_mark()) {
2177 marker_drag_line_points.front().set_x(x1);
2178 marker_drag_line_points.back().set_x(x1);
2179 marker_drag_line->property_points() = marker_drag_line_points;
2182 range_marker_drag_rect->property_x1() = x1;
2183 range_marker_drag_rect->property_x2() = x2;
2188 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2192 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2193 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2199 Location *location = find_location_from_marker (marker, is_start);
2201 drag_info.item = item;
2202 drag_info.data = marker;
2203 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2204 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2208 _dragging_edit_point = true;
2210 drag_info.copied_location = new Location (*location);
2211 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2213 update_marker_drag_item (location);
2215 if (location->is_mark()) {
2216 // marker_drag_line->show();
2217 // marker_drag_line->raise_to_top();
2219 range_marker_drag_rect->show();
2220 range_marker_drag_rect->raise_to_top();
2224 show_verbose_time_cursor (location->start(), 10);
2226 show_verbose_time_cursor (location->end(), 10);
2229 Selection::Operation op = Keyboard::selection_type (event->button.state);
2232 case Selection::Toggle:
2233 selection->toggle (marker);
2235 case Selection::Set:
2236 selection->set (marker);
2238 case Selection::Extend:
2239 selection->add (marker);
2241 case Selection::Add:
2242 selection->add (marker);
2248 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2251 Marker* marker = (Marker *) drag_info.data;
2252 Location *real_location;
2253 Location *copy_location;
2255 bool move_both = false;
2258 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2259 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2264 nframes_t next = newframe;
2266 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2267 snap_to (newframe, 0, true);
2270 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2274 /* call this to find out if its the start or end */
2276 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2280 if (real_location->locked()) {
2284 /* use the copy that we're "dragging" around */
2286 copy_location = drag_info.copied_location;
2288 f_delta = copy_location->end() - copy_location->start();
2290 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2294 if (copy_location->is_mark()) {
2297 copy_location->set_start (newframe);
2301 if (is_start) { // start-of-range marker
2304 copy_location->set_start (newframe);
2305 copy_location->set_end (newframe + f_delta);
2306 } else if (newframe < copy_location->end()) {
2307 copy_location->set_start (newframe);
2309 snap_to (next, 1, true);
2310 copy_location->set_end (next);
2311 copy_location->set_start (newframe);
2314 } else { // end marker
2317 copy_location->set_end (newframe);
2318 copy_location->set_start (newframe - f_delta);
2319 } else if (newframe > copy_location->start()) {
2320 copy_location->set_end (newframe);
2322 } else if (newframe > 0) {
2323 snap_to (next, -1, true);
2324 copy_location->set_start (next);
2325 copy_location->set_end (newframe);
2330 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2331 drag_info.first_move = false;
2333 update_marker_drag_item (copy_location);
2335 LocationMarkers* lm = find_location_markers (real_location);
2336 lm->set_position (copy_location->start(), copy_location->end());
2337 edit_point_clock.set (copy_location->start());
2339 show_verbose_time_cursor (newframe, 10);
2343 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2345 if (drag_info.first_move) {
2346 /* just a click, do nothing but whatever selection occured */
2350 _dragging_edit_point = false;
2352 Marker* marker = (Marker *) drag_info.data;
2355 begin_reversible_command ( _("move marker") );
2356 XMLNode &before = session->locations()->get_state();
2358 Location * location = find_location_from_marker (marker, is_start);
2362 if (location->locked()) {
2366 if (location->is_mark()) {
2367 location->set_start (drag_info.copied_location->start());
2369 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2373 XMLNode &after = session->locations()->get_state();
2374 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2375 commit_reversible_command ();
2377 marker_drag_line->hide();
2378 range_marker_drag_rect->hide();
2382 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2385 MeterMarker* meter_marker;
2387 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2388 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2392 meter_marker = dynamic_cast<MeterMarker*> (marker);
2394 MetricSection& section (meter_marker->meter());
2396 if (!section.movable()) {
2400 drag_info.item = item;
2401 drag_info.copy = false;
2402 drag_info.data = marker;
2403 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2404 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2408 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2410 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2414 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2417 MeterMarker* meter_marker;
2419 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2420 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2424 meter_marker = dynamic_cast<MeterMarker*> (marker);
2426 // create a dummy marker for visual representation of moving the copy.
2427 // The actual copying is not done before we reach the finish callback.
2429 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2430 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2431 *new MeterSection(meter_marker->meter()));
2433 drag_info.item = &new_marker->the_item();
2434 drag_info.copy = true;
2435 drag_info.data = new_marker;
2436 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2437 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2441 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2443 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2447 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2449 MeterMarker* marker = (MeterMarker *) drag_info.data;
2450 nframes_t adjusted_frame;
2452 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2453 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2459 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2460 snap_to (adjusted_frame);
2463 if (adjusted_frame == drag_info.last_pointer_frame) return;
2465 marker->set_position (adjusted_frame);
2468 drag_info.last_pointer_frame = adjusted_frame;
2469 drag_info.first_move = false;
2471 show_verbose_time_cursor (adjusted_frame, 10);
2475 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2477 if (drag_info.first_move) return;
2479 meter_marker_drag_motion_callback (drag_info.item, event);
2481 MeterMarker* marker = (MeterMarker *) drag_info.data;
2484 TempoMap& map (session->tempo_map());
2485 map.bbt_time (drag_info.last_pointer_frame, when);
2487 if (drag_info.copy == true) {
2488 begin_reversible_command (_("copy meter mark"));
2489 XMLNode &before = map.get_state();
2490 map.add_meter (marker->meter(), when);
2491 XMLNode &after = map.get_state();
2492 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2493 commit_reversible_command ();
2495 // delete the dummy marker we used for visual representation of copying.
2496 // a new visual marker will show up automatically.
2499 begin_reversible_command (_("move meter mark"));
2500 XMLNode &before = map.get_state();
2501 map.move_meter (marker->meter(), when);
2502 XMLNode &after = map.get_state();
2503 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2504 commit_reversible_command ();
2509 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2512 TempoMarker* tempo_marker;
2514 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2515 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2519 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2520 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2524 MetricSection& section (tempo_marker->tempo());
2526 if (!section.movable()) {
2530 drag_info.item = item;
2531 drag_info.copy = false;
2532 drag_info.data = marker;
2533 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2534 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2538 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2539 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2543 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2546 TempoMarker* tempo_marker;
2548 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2549 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2553 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2554 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2558 // create a dummy marker for visual representation of moving the copy.
2559 // The actual copying is not done before we reach the finish callback.
2561 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2562 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2563 *new TempoSection(tempo_marker->tempo()));
2565 drag_info.item = &new_marker->the_item();
2566 drag_info.copy = true;
2567 drag_info.data = new_marker;
2568 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2569 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2573 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2575 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2579 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2581 TempoMarker* marker = (TempoMarker *) drag_info.data;
2582 nframes_t adjusted_frame;
2584 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2585 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2591 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2592 snap_to (adjusted_frame);
2595 if (adjusted_frame == drag_info.last_pointer_frame) return;
2597 /* OK, we've moved far enough to make it worth actually move the thing. */
2599 marker->set_position (adjusted_frame);
2601 show_verbose_time_cursor (adjusted_frame, 10);
2603 drag_info.last_pointer_frame = adjusted_frame;
2604 drag_info.first_move = false;
2608 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2610 if (drag_info.first_move) return;
2612 tempo_marker_drag_motion_callback (drag_info.item, event);
2614 TempoMarker* marker = (TempoMarker *) drag_info.data;
2617 TempoMap& map (session->tempo_map());
2618 map.bbt_time (drag_info.last_pointer_frame, when);
2620 if (drag_info.copy == true) {
2621 begin_reversible_command (_("copy tempo mark"));
2622 XMLNode &before = map.get_state();
2623 map.add_tempo (marker->tempo(), when);
2624 XMLNode &after = map.get_state();
2625 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2626 commit_reversible_command ();
2628 // delete the dummy marker we used for visual representation of copying.
2629 // a new visual marker will show up automatically.
2632 begin_reversible_command (_("move tempo mark"));
2633 XMLNode &before = map.get_state();
2634 map.move_tempo (marker->tempo(), when);
2635 XMLNode &after = map.get_state();
2636 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2637 commit_reversible_command ();
2642 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2644 ControlPoint* control_point;
2646 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2647 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2651 // We shouldn't remove the first or last gain point
2652 if (control_point->line.is_last_point(*control_point) ||
2653 control_point->line.is_first_point(*control_point)) {
2657 control_point->line.remove_point (*control_point);
2661 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2663 ControlPoint* control_point;
2665 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2666 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2670 control_point->line.remove_point (*control_point);
2674 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2676 ControlPoint* control_point;
2678 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2679 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2683 drag_info.item = item;
2684 drag_info.data = control_point;
2685 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2686 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2688 start_grab (event, fader_cursor);
2690 // start the grab at the center of the control point so
2691 // the point doesn't 'jump' to the mouse after the first drag
2692 drag_info.grab_x = control_point->get_x();
2693 drag_info.grab_y = control_point->get_y();
2694 control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2695 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y,
2696 drag_info.grab_x, drag_info.grab_y);
2698 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2700 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2702 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2703 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2704 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2706 show_verbose_canvas_cursor ();
2710 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2712 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2714 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2715 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2717 if (event->button.state & Keyboard::SecondaryModifier) {
2722 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2723 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2725 // calculate zero crossing point. back off by .01 to stay on the
2726 // positive side of zero
2728 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01;
2729 cp->line.parent_group().i2w(_unused, zero_gain_y);
2731 // make sure we hit zero when passing through
2732 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2733 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2737 if (drag_info.x_constrained) {
2738 cx = drag_info.grab_x;
2740 if (drag_info.y_constrained) {
2741 cy = drag_info.grab_y;
2744 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2745 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2747 cp->line.parent_group().w2i (cx, cy);
2751 cy = min ((double) cp->line.height(), cy);
2753 //translate cx to frames
2754 nframes_t cx_frames = unit_to_frame (cx);
2756 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2757 snap_to (cx_frames);
2760 float fraction = 1.0 - (cy / cp->line.height());
2764 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2770 cp->line.point_drag (*cp, cx_frames , fraction, push);
2772 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2774 drag_info.first_move = false;
2778 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2780 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2782 if (drag_info.first_move) {
2786 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2787 reset_point_selection ();
2791 control_point_drag_motion_callback (item, event);
2793 cp->line.end_drag (cp);
2797 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2799 switch (mouse_mode) {
2801 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2802 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2810 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2814 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2815 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2819 start_line_grab (al, event);
2823 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2827 nframes_t frame_within_region;
2829 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2833 cx = event->button.x;
2834 cy = event->button.y;
2835 line->parent_group().w2i (cx, cy);
2836 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2838 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2839 current_line_drag_info.after)) {
2840 /* no adjacent points */
2844 drag_info.item = &line->grab_item();
2845 drag_info.data = line;
2846 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2847 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2849 start_grab (event, fader_cursor);
2851 double fraction = 1.0 - (cy / line->height());
2853 line->start_drag (0, drag_info.grab_frame, fraction);
2855 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2856 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2857 show_verbose_canvas_cursor ();
2861 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2863 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2865 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2867 if (event->button.state & Keyboard::SecondaryModifier) {
2871 double cx = drag_info.current_pointer_x;
2872 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2874 // calculate zero crossing point. back off by .01 to stay on the
2875 // positive side of zero
2877 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
2878 line->parent_group().i2w(_unused, zero_gain_y);
2880 // make sure we hit zero when passing through
2881 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2882 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2886 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2888 line->parent_group().w2i (cx, cy);
2891 cy = min ((double) line->height(), cy);
2894 fraction = 1.0 - (cy / line->height());
2898 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2904 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2906 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2910 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2912 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2913 line_drag_motion_callback (item, event);
2918 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2920 if (selection->regions.empty() || clicked_regionview == 0) {
2924 drag_info.copy = false;
2925 drag_info.item = item;
2926 drag_info.data = clicked_regionview;
2928 if (Config->get_edit_mode() == Splice) {
2929 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
2930 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
2932 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2933 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2939 TimeAxisView* tvp = clicked_trackview;
2940 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2942 if (tv && tv->is_audio_track()) {
2943 speed = tv->get_diskstream()->speed();
2946 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2947 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2948 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
2949 drag_info.dest_trackview = drag_info.source_trackview;
2950 // we want a move threshold
2951 drag_info.want_move_threshold = true;
2953 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2955 begin_reversible_command (_("move region(s)"));
2959 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2961 if (selection->regions.empty() || clicked_regionview == 0) {
2965 drag_info.copy = true;
2966 drag_info.item = item;
2967 drag_info.data = clicked_regionview;
2971 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2972 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2975 if (atv && atv->is_audio_track()) {
2976 speed = atv->get_diskstream()->speed();
2979 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
2980 drag_info.dest_trackview = drag_info.source_trackview;
2981 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2982 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2983 // we want a move threshold
2984 drag_info.want_move_threshold = true;
2985 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2986 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2987 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2991 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2993 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
2997 drag_info.copy = false;
2998 drag_info.item = item;
2999 drag_info.data = clicked_regionview;
3000 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3001 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3006 TimeAxisView* tvp = clicked_trackview;
3007 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3009 if (tv && tv->is_audio_track()) {
3010 speed = tv->get_diskstream()->speed();
3013 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
3014 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3015 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3016 drag_info.dest_trackview = drag_info.source_trackview;
3017 // we want a move threshold
3018 drag_info.want_move_threshold = true;
3019 drag_info.brushing = true;
3021 begin_reversible_command (_("Drag region brush"));
3025 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3027 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3029 drag_info.want_move_threshold = false; // don't copy again
3031 /* duplicate the regionview(s) and region(s) */
3033 vector<RegionView*> new_regionviews;
3035 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3038 AudioRegionView* arv;
3043 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
3044 /* XXX handle MIDI here */
3048 const boost::shared_ptr<const Region> original = arv->region();
3049 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3050 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (region_copy);
3052 nrv = new AudioRegionView (*arv, ar);
3053 nrv->get_canvas_group()->show ();
3055 new_regionviews.push_back (nrv);
3058 if (new_regionviews.empty()) {
3062 /* reset selection to new regionviews. This will not set selection visual status for
3063 these regionviews since they don't belong to a track, so do that by hand too.
3066 selection->set (new_regionviews);
3068 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3069 (*i)->set_selected (true);
3072 /* reset drag_info data to reflect the fact that we are dragging the copies */
3074 drag_info.data = new_regionviews.front();
3076 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3081 Editor::check_region_drag_possible (AudioTimeAxisView** tv)
3083 /* Which trackview is this ? */
3085 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3086 (*tv) = dynamic_cast<AudioTimeAxisView*>(tvp);
3088 /* The region motion is only processed if the pointer is over
3092 if (!(*tv) || !(*tv)->is_audio_track()) {
3093 /* To make sure we hide the verbose canvas cursor when the mouse is
3094 not held over and audiotrack.
3096 hide_verbose_canvas_cursor ();
3103 struct RegionSelectionByPosition {
3104 bool operator() (RegionView*a, RegionView* b) {
3105 return a->region()->position () < b->region()->position();
3110 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3112 AudioTimeAxisView* tv;
3114 if (!check_region_drag_possible (&tv)) {
3118 if (!drag_info.move_threshold_passed) {
3124 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3130 RegionSelection copy (selection->regions);
3132 RegionSelectionByPosition cmp;
3135 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3137 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(*i)->get_time_axis_view());
3143 boost::shared_ptr<Playlist> playlist;
3145 if ((playlist = atv->playlist()) == 0) {
3149 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3154 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3158 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3164 playlist->shuffle ((*i)->region(), dir);
3166 drag_info.grab_x = drag_info.current_pointer_x;
3171 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3176 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3180 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3181 nframes_t pending_region_position = 0;
3182 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3183 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3184 bool clamp_y_axis = false;
3185 vector<int32_t> height_list(512) ;
3186 vector<int32_t>::iterator j;
3187 AudioTimeAxisView* tv;
3189 possibly_copy_regions_during_grab (event);
3191 if (!check_region_drag_possible (&tv)) {
3195 original_pointer_order = drag_info.dest_trackview->order;
3197 /************************************************************
3199 ************************************************************/
3201 if (drag_info.brushing) {
3202 clamp_y_axis = true;
3207 if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3209 int32_t children = 0, numtracks = 0;
3210 // XXX hard coding track limit, oh my, so very very bad
3211 bitset <1024> tracks (0x00);
3212 /* get a bitmask representing the visible tracks */
3214 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3215 TimeAxisView *tracklist_timeview;
3216 tracklist_timeview = (*i);
3217 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3218 list<TimeAxisView*> children_list;
3220 /* zeroes are audio tracks. ones are other types. */
3222 if (!atv2->hidden()) {
3224 if (visible_y_high < atv2->order) {
3225 visible_y_high = atv2->order;
3227 if (visible_y_low > atv2->order) {
3228 visible_y_low = atv2->order;
3231 if (!atv2->is_audio_track()) {
3232 tracks = tracks |= (0x01 << atv2->order);
3235 height_list[atv2->order] = (*i)->height;
3237 if ((children_list = atv2->get_child_list()).size() > 0) {
3238 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3239 tracks = tracks |= (0x01 << (atv2->order + children));
3240 height_list[atv2->order + children] = (*j)->height;
3248 /* find the actual span according to the canvas */
3250 canvas_pointer_y_span = pointer_y_span;
3251 if (drag_info.dest_trackview->order >= tv->order) {
3253 for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3254 if (height_list[y] == 0 ) {
3255 canvas_pointer_y_span--;
3260 for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3261 if ( height_list[y] == 0 ) {
3262 canvas_pointer_y_span++;
3267 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3268 RegionView* rv2 = (*i);
3269 double ix1, ix2, iy1, iy2;
3272 if (rv2->region()->locked()) {
3276 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3277 rv2->get_canvas_group()->i2w (ix1, iy1);
3278 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3279 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3281 if (atv2->order != original_pointer_order) {
3282 /* this isn't the pointer track */
3284 if (canvas_pointer_y_span > 0) {
3286 /* moving up the canvas */
3287 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3289 int32_t visible_tracks = 0;
3290 while (visible_tracks < canvas_pointer_y_span ) {
3293 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3294 /* we're passing through a hidden track */
3299 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3300 clamp_y_axis = true;
3304 clamp_y_axis = true;
3307 } else if (canvas_pointer_y_span < 0) {
3309 /*moving down the canvas*/
3311 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3314 int32_t visible_tracks = 0;
3316 while (visible_tracks > canvas_pointer_y_span ) {
3319 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3323 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3324 clamp_y_axis = true;
3329 clamp_y_axis = true;
3335 /* this is the pointer's track */
3336 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3337 clamp_y_axis = true;
3338 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3339 clamp_y_axis = true;
3347 } else if (drag_info.dest_trackview == tv) {
3348 clamp_y_axis = true;
3352 if (!clamp_y_axis) {
3353 drag_info.dest_trackview = tv;
3356 /************************************************************
3358 ************************************************************/
3360 /* compute the amount of pointer motion in frames, and where
3361 the region would be if we moved it by that much.
3364 if ( drag_info.move_threshold_passed ) {
3366 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3368 nframes_t sync_frame;
3369 nframes_t sync_offset;
3372 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3374 sync_offset = rv->region()->sync_offset (sync_dir);
3376 /* we don't handle a sync point that lies before zero.
3378 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3379 sync_frame = pending_region_position + (sync_dir*sync_offset);
3381 /* we snap if the snap modifier is not enabled.
3384 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3385 snap_to (sync_frame);
3388 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3391 pending_region_position = drag_info.last_frame_position;
3395 pending_region_position = 0;
3398 if (pending_region_position > max_frames - rv->region()->length()) {
3399 pending_region_position = drag_info.last_frame_position;
3402 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3404 bool x_move_allowed = ( drag_info.copy || !drag_info.x_constrained && (Config->get_edit_mode() != Lock)) || ( drag_info.x_constrained && (Config->get_edit_mode() == Lock)) ;
3406 if ( pending_region_position != drag_info.last_frame_position && x_move_allowed ) {
3408 /* now compute the canvas unit distance we need to move the regionview
3409 to make it appear at the new location.
3412 if (pending_region_position > drag_info.last_frame_position) {
3413 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3415 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3418 drag_info.last_frame_position = pending_region_position;
3425 /* threshold not passed */
3430 /*************************************************************
3432 ************************************************************/
3434 if (x_delta == 0 && (pointer_y_span == 0)) {
3435 /* haven't reached next snap point, and we're not switching
3436 trackviews. nothing to do.
3443 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3445 RegionView* rv2 = (*i);
3447 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3449 double ix1, ix2, iy1, iy2;
3450 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3451 rv2->get_canvas_group()->i2w (ix1, iy1);
3460 /*************************************************************
3462 ************************************************************/
3466 if (drag_info.first_move) {
3467 if (drag_info.move_threshold_passed) {
3478 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3479 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3481 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3483 RegionView* rv = (*i);
3484 double ix1, ix2, iy1, iy2;
3485 int32_t temp_pointer_y_span = pointer_y_span;
3487 if (rv->region()->locked()) {
3491 /* get item BBox, which will be relative to parent. so we have
3492 to query on a child, then convert to world coordinates using
3496 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3497 rv->get_canvas_group()->i2w (ix1, iy1);
3498 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3499 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3500 AudioTimeAxisView* temp_atv;
3502 if ((pointer_y_span != 0) && !clamp_y_axis) {
3505 for (j = height_list.begin(); j!= height_list.end(); j++) {
3506 if (x == canvas_atv->order) {
3507 /* we found the track the region is on */
3508 if (x != original_pointer_order) {
3509 /*this isn't from the same track we're dragging from */
3510 temp_pointer_y_span = canvas_pointer_y_span;
3512 while (temp_pointer_y_span > 0) {
3513 /* we're moving up canvas-wise,
3514 so we need to find the next track height
3516 if (j != height_list.begin()) {
3519 if (x != original_pointer_order) {
3520 /* we're not from the dragged track, so ignore hidden tracks. */
3522 temp_pointer_y_span++;
3526 temp_pointer_y_span--;
3529 while (temp_pointer_y_span < 0) {
3531 if (x != original_pointer_order) {
3533 temp_pointer_y_span--;
3537 if (j != height_list.end()) {
3540 temp_pointer_y_span++;
3542 /* find out where we'll be when we move and set height accordingly */
3544 tvp2 = trackview_by_y_position (iy1 + y_delta);
3545 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3546 rv->set_height (temp_atv->height);
3548 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3549 personally, i think this can confuse things, but never mind.
3552 //const GdkColor& col (temp_atv->view->get_region_color());
3553 //rv->set_color (const_cast<GdkColor&>(col));
3561 /* prevent the regionview from being moved to before
3562 the zero position on the canvas.
3567 if (-x_delta > ix1) {
3570 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3571 x_delta = max_frames - rv->region()->last_frame();
3575 if (drag_info.first_move) {
3577 /* hide any dependent views */
3579 rv->get_time_axis_view().hide_dependent_views (*rv);
3581 /* this is subtle. raising the regionview itself won't help,
3582 because raise_to_top() just puts the item on the top of
3583 its parent's stack. so, we need to put the trackview canvas_display group
3584 on the top, since its parent is the whole canvas.
3587 rv->get_canvas_group()->raise_to_top();
3588 rv->get_time_axis_view().canvas_display->raise_to_top();
3589 cursor_group->raise_to_top();
3590 rv->fake_set_opaque (true);
3593 if (drag_info.brushing) {
3594 mouse_brush_insert_region (rv, pending_region_position);
3596 rv->move (x_delta, y_delta);
3599 } /* foreach region */
3603 if (drag_info.first_move && drag_info.move_threshold_passed) {
3604 cursor_group->raise_to_top();
3605 drag_info.first_move = false;
3608 if (x_delta != 0 && !drag_info.brushing) {
3609 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3614 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3616 bool nocommit = true;
3617 vector<RegionView*> copies;
3618 RouteTimeAxisView* source_tv;
3619 boost::shared_ptr<Diskstream> ds;
3620 boost::shared_ptr<Playlist> from_playlist;
3621 vector<RegionView*> new_selection;
3623 /* first_move is set to false if the regionview has been moved in the
3627 if (drag_info.first_move) {
3634 /* The regionview has been moved at some stage during the grab so we need
3635 to account for any mouse movement between this event and the last one.
3638 region_drag_motion_callback (item, event);
3640 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3641 selection->set (pre_drag_region_selection);
3642 pre_drag_region_selection.clear ();
3645 if (drag_info.brushing) {
3646 /* all changes were made during motion event handlers */
3648 if (drag_info.copy) {
3649 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3650 copies.push_back (*i);
3659 if (drag_info.copy) {
3660 if (drag_info.x_constrained) {
3661 op_string = _("fixed time region copy");
3663 op_string = _("region copy");
3666 if (drag_info.x_constrained) {
3667 op_string = _("fixed time region drag");
3669 op_string = _("region drag");
3673 begin_reversible_command (op_string);
3675 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3677 RegionView* rv = (*i);
3678 double ix1, ix2, iy1, iy2;
3679 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3680 rv->get_canvas_group()->i2w (ix1, iy1);
3681 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
3682 AudioTimeAxisView* dest_atv = dynamic_cast<AudioTimeAxisView*>(dest_tv);
3684 bool changed_tracks;
3685 bool changed_position;
3688 if (rv->region()->locked()) {
3690 selection->regions.remove (rv);
3694 /* adjust for track speed */
3698 if (dest_atv && dest_atv->get_diskstream()) {
3699 speed = dest_atv->get_diskstream()->speed();
3702 changed_position = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3703 changed_tracks = (dest_tv != &rv->get_time_axis_view());
3705 if (changed_position) {
3706 where = (nframes_t) (unit_to_frame (ix1) * speed);
3708 where = rv->region()->position();
3711 /* undo the previous hide_dependent_views so that xfades don't
3712 disappear on copying regions
3715 rv->get_time_axis_view().reveal_dependent_views (*rv);
3717 boost::shared_ptr<Region> new_region;
3719 if (drag_info.copy) {
3720 /* we already made a copy */
3721 new_region = rv->region();
3723 new_region = RegionFactory::create (rv->region());
3726 if (changed_tracks || drag_info.copy) {
3728 boost::shared_ptr<Playlist> to_playlist = dest_atv->playlist();
3730 latest_regionviews.clear ();
3732 sigc::connection c = dest_atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3733 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3734 to_playlist->add_region (new_region, where);
3735 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3738 if (!latest_regionviews.empty()) {
3739 // XXX why just the first one ? we only expect one
3740 dest_atv->reveal_dependent_views (*latest_regionviews.front());
3741 new_selection.push_back (latest_regionviews.front());
3746 /* just change the model */
3748 rv->region()->set_position (where, (void*) this);
3751 if (changed_tracks && !drag_info.copy) {
3753 /* get the playlist where this drag started. we can't use rv->region()->playlist()
3754 because we may have copied the region and it has not been attached to a playlist.
3757 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
3758 assert ((ds = source_tv->get_diskstream()));
3759 assert ((from_playlist = ds->playlist()));
3761 /* moved to a different audio track, without copying */
3763 /* the region that used to be in the old playlist is not
3764 moved to the new one - we use a copy of it. as a result,
3765 any existing editor for the region should no longer be
3769 rv->hide_region_editor();
3770 rv->fake_set_opaque (false);
3772 /* remove the region from the old playlist */
3774 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3775 from_playlist->remove_region ((rv->region()));
3776 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3778 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3779 was selected in all of them, then removing it from a playlist will have removed all
3780 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3781 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3782 corresponding regionview, and the selection is now empty).
3784 this could have invalidated any and all iterators into the region selection.
3786 the heuristic we use here is: if the region selection is empty, break out of the loop
3787 here. if the region selection is not empty, then restart the loop because we know that
3788 we must have removed at least the region(view) we've just been working on as well as any
3789 that we processed on previous iterations.
3791 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3792 we can just iterate.
3795 if (selection->regions.empty()) {
3798 i = selection->regions.by_layer().begin();
3805 if (drag_info.copy) {
3806 copies.push_back (rv);
3810 if (new_selection.empty()) {
3811 if (drag_info.copy) {
3812 /* the region(view)s that are selected and being dragged around
3813 are copies and do not belong to any track. remove them
3814 from the selection right here.
3816 selection->clear_regions();
3819 /* this will clear any existing selection that would have been
3820 cleared in the other clause above
3822 selection->set (new_selection);
3827 commit_reversible_command ();
3830 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3836 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3838 /* Either add to or set the set the region selection, unless
3839 this is an alignment click (control used)
3842 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
3843 TimeAxisView* tv = &rv.get_time_axis_view();
3844 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3846 if (atv && atv->is_audio_track()) {
3847 speed = atv->get_diskstream()->speed();
3850 nframes64_t where = get_preferred_edit_position();
3854 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
3856 align_region (rv.region(), SyncPoint, (nframes_t) (where * speed));
3858 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
3860 align_region (rv.region(), End, (nframes_t) (where * speed));
3864 align_region (rv.region(), Start, (nframes_t) (where * speed));
3871 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3877 nframes_t frame_rate;
3886 if (Profile->get_sae() || Profile->get_small_screen()) {
3887 m = ARDOUR_UI::instance()->primary_clock.mode();
3889 m = ARDOUR_UI::instance()->secondary_clock.mode();
3893 case AudioClock::BBT:
3894 session->bbt_time (frame, bbt);
3895 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3898 case AudioClock::SMPTE:
3899 session->smpte_time (frame, smpte);
3900 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3903 case AudioClock::MinSec:
3904 /* XXX this is copied from show_verbose_duration_cursor() */
3905 frame_rate = session->frame_rate();
3906 hours = frame / (frame_rate * 3600);
3907 frame = frame % (frame_rate * 3600);
3908 mins = frame / (frame_rate * 60);
3909 frame = frame % (frame_rate * 60);
3910 secs = (float) frame / (float) frame_rate;
3911 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3915 snprintf (buf, sizeof(buf), "%u", frame);
3919 if (xpos >= 0 && ypos >=0) {
3920 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3923 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3925 show_verbose_canvas_cursor ();
3929 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3936 nframes_t distance, frame_rate;
3938 Meter meter_at_start(session->tempo_map().meter_at(start));
3946 if (Profile->get_sae() || Profile->get_small_screen()) {
3947 m = ARDOUR_UI::instance()->primary_clock.mode ();
3949 m = ARDOUR_UI::instance()->secondary_clock.mode ();
3953 case AudioClock::BBT:
3954 session->bbt_time (start, sbbt);
3955 session->bbt_time (end, ebbt);
3958 /* XXX this computation won't work well if the
3959 user makes a selection that spans any meter changes.
3962 ebbt.bars -= sbbt.bars;
3963 if (ebbt.beats >= sbbt.beats) {
3964 ebbt.beats -= sbbt.beats;
3967 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3969 if (ebbt.ticks >= sbbt.ticks) {
3970 ebbt.ticks -= sbbt.ticks;
3973 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3976 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3979 case AudioClock::SMPTE:
3980 session->smpte_duration (end - start, smpte);
3981 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3984 case AudioClock::MinSec:
3985 /* XXX this stuff should be elsewhere.. */
3986 distance = end - start;
3987 frame_rate = session->frame_rate();
3988 hours = distance / (frame_rate * 3600);
3989 distance = distance % (frame_rate * 3600);
3990 mins = distance / (frame_rate * 60);
3991 distance = distance % (frame_rate * 60);
3992 secs = (float) distance / (float) frame_rate;
3993 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3997 snprintf (buf, sizeof(buf), "%u", end - start);
4001 if (xpos >= 0 && ypos >=0) {
4002 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4005 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4008 show_verbose_canvas_cursor ();
4012 Editor::collect_new_region_view (RegionView* rv)
4014 latest_regionviews.push_back (rv);
4018 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4020 if (clicked_regionview == 0) {
4024 /* lets try to create new Region for the selection */
4026 vector<boost::shared_ptr<AudioRegion> > new_regions;
4027 create_region_from_selection (new_regions);
4029 if (new_regions.empty()) {
4033 /* XXX fix me one day to use all new regions */
4035 boost::shared_ptr<Region> region (new_regions.front());
4037 /* add it to the current stream/playlist.
4039 tricky: the streamview for the track will add a new regionview. we will
4040 catch the signal it sends when it creates the regionview to
4041 set the regionview we want to then drag.
4044 latest_regionviews.clear();
4045 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4047 /* A selection grab currently creates two undo/redo operations, one for
4048 creating the new region and another for moving it.
4051 begin_reversible_command (_("selection grab"));
4053 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
4055 XMLNode *before = &(playlist->get_state());
4056 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
4057 XMLNode *after = &(playlist->get_state());
4058 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4060 commit_reversible_command ();
4064 if (latest_regionviews.empty()) {
4065 /* something went wrong */
4069 /* we need to deselect all other regionviews, and select this one
4070 i'm ignoring undo stuff, because the region creation will take care of it
4072 selection->set (latest_regionviews);
4074 drag_info.item = latest_regionviews.front()->get_canvas_group();
4075 drag_info.data = latest_regionviews.front();
4076 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4077 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4081 drag_info.source_trackview = clicked_trackview;
4082 drag_info.dest_trackview = drag_info.source_trackview;
4083 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4084 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4086 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4090 Editor::cancel_selection ()
4092 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4093 (*i)->hide_selection ();
4095 selection->clear ();
4096 clicked_selection = 0;
4100 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4102 nframes_t start = 0;
4109 drag_info.item = item;
4110 drag_info.motion_callback = &Editor::drag_selection;
4111 drag_info.finished_callback = &Editor::end_selection_op;
4116 case CreateSelection:
4117 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4118 drag_info.copy = true;
4120 drag_info.copy = false;
4122 start_grab (event, selector_cursor);
4125 case SelectionStartTrim:
4126 if (clicked_trackview) {
4127 clicked_trackview->order_selection_trims (item, true);
4129 start_grab (event, trimmer_cursor);
4130 start = selection->time[clicked_selection].start;
4131 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4134 case SelectionEndTrim:
4135 if (clicked_trackview) {
4136 clicked_trackview->order_selection_trims (item, false);
4138 start_grab (event, trimmer_cursor);
4139 end = selection->time[clicked_selection].end;
4140 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4144 start = selection->time[clicked_selection].start;
4146 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4150 if (selection_op == SelectionMove) {
4151 show_verbose_time_cursor(start, 10);
4153 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4158 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4160 nframes_t start = 0;
4163 nframes_t pending_position;
4165 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4166 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4168 pending_position = 0;
4171 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4172 snap_to (pending_position);
4175 /* only alter selection if the current frame is
4176 different from the last frame position (adjusted)
4179 if (pending_position == drag_info.last_pointer_frame) return;
4181 switch (selection_op) {
4182 case CreateSelection:
4184 if (drag_info.first_move) {
4185 snap_to (drag_info.grab_frame);
4188 if (pending_position < drag_info.grab_frame) {
4189 start = pending_position;
4190 end = drag_info.grab_frame;
4192 end = pending_position;
4193 start = drag_info.grab_frame;
4196 /* first drag: Either add to the selection
4197 or create a new selection->
4200 if (drag_info.first_move) {
4202 begin_reversible_command (_("range selection"));
4204 if (drag_info.copy) {
4205 /* adding to the selection */
4206 clicked_selection = selection->add (start, end);
4207 drag_info.copy = false;
4209 /* new selection-> */
4210 clicked_selection = selection->set (clicked_trackview, start, end);
4215 case SelectionStartTrim:
4217 if (drag_info.first_move) {
4218 begin_reversible_command (_("trim selection start"));
4221 start = selection->time[clicked_selection].start;
4222 end = selection->time[clicked_selection].end;
4224 if (pending_position > end) {
4227 start = pending_position;
4231 case SelectionEndTrim:
4233 if (drag_info.first_move) {
4234 begin_reversible_command (_("trim selection end"));
4237 start = selection->time[clicked_selection].start;
4238 end = selection->time[clicked_selection].end;
4240 if (pending_position < start) {
4243 end = pending_position;
4250 if (drag_info.first_move) {
4251 begin_reversible_command (_("move selection"));
4254 start = selection->time[clicked_selection].start;
4255 end = selection->time[clicked_selection].end;
4257 length = end - start;
4259 start = pending_position;
4262 end = start + length;
4267 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4268 start_canvas_autoscroll (1);
4272 selection->replace (clicked_selection, start, end);
4275 drag_info.last_pointer_frame = pending_position;
4276 drag_info.first_move = false;
4278 if (selection_op == SelectionMove) {
4279 show_verbose_time_cursor(start, 10);
4281 show_verbose_time_cursor(pending_position, 10);
4286 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4288 if (!drag_info.first_move) {
4289 drag_selection (item, event);
4290 /* XXX this is not object-oriented programming at all. ick */
4291 if (selection->time.consolidate()) {
4292 selection->TimeChanged ();
4294 commit_reversible_command ();
4296 /* just a click, no pointer movement.*/
4298 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4300 selection->clear_time();
4305 /* XXX what happens if its a music selection? */
4306 session->set_audio_range (selection->time);
4307 stop_canvas_autoscroll ();
4311 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4314 TimeAxisView* tvp = clicked_trackview;
4315 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4317 if (tv && tv->is_audio_track()) {
4318 speed = tv->get_diskstream()->speed();
4321 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4322 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4323 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4325 //drag_info.item = clicked_regionview->get_name_highlight();
4326 drag_info.item = item;
4327 drag_info.motion_callback = &Editor::trim_motion_callback;
4328 drag_info.finished_callback = &Editor::trim_finished_callback;
4330 start_grab (event, trimmer_cursor);
4332 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4333 trim_op = ContentsTrim;
4335 /* These will get overridden for a point trim.*/
4336 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4337 /* closer to start */
4338 trim_op = StartTrim;
4339 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4347 show_verbose_time_cursor(region_start, 10);
4350 show_verbose_time_cursor(region_end, 10);
4353 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4359 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4361 RegionView* rv = clicked_regionview;
4362 nframes_t frame_delta = 0;
4363 bool left_direction;
4364 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4366 /* snap modifier works differently here..
4367 its' current state has to be passed to the
4368 various trim functions in order to work properly
4372 TimeAxisView* tvp = clicked_trackview;
4373 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4374 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4376 if (tv && tv->is_audio_track()) {
4377 speed = tv->get_diskstream()->speed();
4380 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4381 left_direction = true;
4383 left_direction = false;
4387 snap_to (drag_info.current_pointer_frame);
4390 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4394 if (drag_info.first_move) {
4400 trim_type = "Region start trim";
4403 trim_type = "Region end trim";
4406 trim_type = "Region content trim";
4410 begin_reversible_command (trim_type);
4412 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4413 (*i)->fake_set_opaque(false);
4414 (*i)->region()->freeze ();
4416 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4418 arv->temporarily_hide_envelope ();
4420 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4421 insert_result = motion_frozen_playlists.insert (pl);
4422 if (insert_result.second) {
4423 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4428 if (left_direction) {
4429 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4431 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4436 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4439 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4440 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4446 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4449 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4450 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4457 bool swap_direction = false;
4459 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4460 swap_direction = true;
4463 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4464 i != selection->regions.by_layer().end(); ++i)
4466 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4474 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4477 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4480 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4484 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4485 drag_info.first_move = false;
4489 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4491 boost::shared_ptr<Region> region (rv.region());
4493 if (region->locked()) {
4497 nframes_t new_bound;
4500 TimeAxisView* tvp = clicked_trackview;
4501 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4503 if (tv && tv->is_audio_track()) {
4504 speed = tv->get_diskstream()->speed();
4507 if (left_direction) {
4508 if (swap_direction) {
4509 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4511 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4514 if (swap_direction) {
4515 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4517 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4522 snap_to (new_bound);
4524 region->trim_start ((nframes_t) (new_bound * speed), this);
4525 rv.region_changed (StartChanged);
4529 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4531 boost::shared_ptr<Region> region (rv.region());
4533 if (region->locked()) {
4537 nframes_t new_bound;
4540 TimeAxisView* tvp = clicked_trackview;
4541 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4543 if (tv && tv->is_audio_track()) {
4544 speed = tv->get_diskstream()->speed();
4547 if (left_direction) {
4548 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4550 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4554 snap_to (new_bound, (left_direction ? 0 : 1));
4557 region->trim_front ((nframes_t) (new_bound * speed), this);
4559 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4563 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4565 boost::shared_ptr<Region> region (rv.region());
4567 if (region->locked()) {
4571 nframes_t new_bound;
4574 TimeAxisView* tvp = clicked_trackview;
4575 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4577 if (tv && tv->is_audio_track()) {
4578 speed = tv->get_diskstream()->speed();
4581 if (left_direction) {
4582 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4584 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4588 snap_to (new_bound);
4590 region->trim_end ((nframes_t) (new_bound * speed), this);
4591 rv.region_changed (LengthChanged);
4595 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4597 if (!drag_info.first_move) {
4598 trim_motion_callback (item, event);
4600 if (!selection->selected (clicked_regionview)) {
4601 thaw_region_after_trim (*clicked_regionview);
4604 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4605 i != selection->regions.by_layer().end(); ++i)
4607 thaw_region_after_trim (**i);
4608 (*i)->fake_set_opaque (true);
4612 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4614 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4617 motion_frozen_playlists.clear ();
4619 commit_reversible_command();
4621 /* no mouse movement */
4627 Editor::point_trim (GdkEvent* event)
4629 RegionView* rv = clicked_regionview;
4630 nframes_t new_bound = drag_info.current_pointer_frame;
4632 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4633 snap_to (new_bound);
4636 /* Choose action dependant on which button was pressed */
4637 switch (event->button.button) {
4639 trim_op = StartTrim;
4640 begin_reversible_command (_("Start point trim"));
4642 if (selection->selected (rv)) {
4644 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4645 i != selection->regions.by_layer().end(); ++i)
4647 if (!(*i)->region()->locked()) {
4648 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4649 XMLNode &before = pl->get_state();
4650 (*i)->region()->trim_front (new_bound, this);
4651 XMLNode &after = pl->get_state();
4652 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4658 if (!rv->region()->locked()) {
4659 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4660 XMLNode &before = pl->get_state();
4661 rv->region()->trim_front (new_bound, this);
4662 XMLNode &after = pl->get_state();
4663 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4667 commit_reversible_command();
4672 begin_reversible_command (_("End point trim"));
4674 if (selection->selected (rv)) {
4676 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4678 if (!(*i)->region()->locked()) {
4679 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4680 XMLNode &before = pl->get_state();
4681 (*i)->region()->trim_end (new_bound, this);
4682 XMLNode &after = pl->get_state();
4683 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4689 if (!rv->region()->locked()) {
4690 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4691 XMLNode &before = pl->get_state();
4692 rv->region()->trim_end (new_bound, this);
4693 XMLNode &after = pl->get_state();
4694 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4698 commit_reversible_command();
4707 Editor::thaw_region_after_trim (RegionView& rv)
4709 boost::shared_ptr<Region> region (rv.region());
4711 if (region->locked()) {
4715 region->thaw (_("trimmed region"));
4716 XMLNode &after = region->playlist()->get_state();
4717 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4719 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4721 arv->unhide_envelope ();
4725 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4730 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4731 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4735 Location* location = find_location_from_marker (marker, is_start);
4736 location->set_hidden (true, this);
4741 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4747 drag_info.item = item;
4748 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4749 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4751 range_marker_op = op;
4753 if (!temp_location) {
4754 temp_location = new Location;
4758 case CreateRangeMarker:
4759 case CreateTransportMarker:
4760 case CreateCDMarker:
4762 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4763 drag_info.copy = true;
4765 drag_info.copy = false;
4767 start_grab (event, selector_cursor);
4771 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4776 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4778 nframes_t start = 0;
4780 ArdourCanvas::SimpleRect *crect;
4782 switch (range_marker_op) {
4783 case CreateRangeMarker:
4784 crect = range_bar_drag_rect;
4786 case CreateTransportMarker:
4787 crect = transport_bar_drag_rect;
4789 case CreateCDMarker:
4790 crect = cd_marker_bar_drag_rect;
4793 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
4798 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4799 snap_to (drag_info.current_pointer_frame);
4802 /* only alter selection if the current frame is
4803 different from the last frame position.
4806 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4808 switch (range_marker_op) {
4809 case CreateRangeMarker:
4810 case CreateTransportMarker:
4811 case CreateCDMarker:
4812 if (drag_info.first_move) {
4813 snap_to (drag_info.grab_frame);
4816 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4817 start = drag_info.current_pointer_frame;
4818 end = drag_info.grab_frame;
4820 end = drag_info.current_pointer_frame;
4821 start = drag_info.grab_frame;
4824 /* first drag: Either add to the selection
4825 or create a new selection.
4828 if (drag_info.first_move) {
4830 temp_location->set (start, end);
4834 update_marker_drag_item (temp_location);
4835 range_marker_drag_rect->show();
4836 range_marker_drag_rect->raise_to_top();
4842 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4843 start_canvas_autoscroll (1);
4847 temp_location->set (start, end);
4849 double x1 = frame_to_pixel (start);
4850 double x2 = frame_to_pixel (end);
4851 crect->property_x1() = x1;
4852 crect->property_x2() = x2;
4854 update_marker_drag_item (temp_location);
4857 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4858 drag_info.first_move = false;
4860 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4865 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4867 Location * newloc = 0;
4871 if (!drag_info.first_move) {
4872 drag_range_markerbar_op (item, event);
4874 switch (range_marker_op) {
4875 case CreateRangeMarker:
4876 case CreateCDMarker:
4878 begin_reversible_command (_("new range marker"));
4879 XMLNode &before = session->locations()->get_state();
4880 session->locations()->next_available_name(rangename,"unnamed");
4881 if (range_marker_op == CreateCDMarker) {
4882 flags = Location::IsRangeMarker|Location::IsCDMarker;
4883 cd_marker_bar_drag_rect->hide();
4886 flags = Location::IsRangeMarker;
4887 range_bar_drag_rect->hide();
4889 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
4890 session->locations()->add (newloc, true);
4891 XMLNode &after = session->locations()->get_state();
4892 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4893 commit_reversible_command ();
4895 range_marker_drag_rect->hide();
4899 case CreateTransportMarker:
4900 // popup menu to pick loop or punch
4901 new_transport_marker_context_menu (&event->button, item);
4906 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4908 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
4913 start = session->locations()->first_mark_before (drag_info.grab_frame);
4914 end = session->locations()->first_mark_after (drag_info.grab_frame);
4916 if (end == max_frames) {
4917 end = session->current_end_frame ();
4921 start = session->current_start_frame ();
4924 switch (mouse_mode) {
4926 /* find the two markers on either side and then make the selection from it */
4927 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4931 /* find the two markers on either side of the click and make the range out of it */
4932 selection->set (0, start, end);
4941 stop_canvas_autoscroll ();
4947 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4949 drag_info.item = item;
4950 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4951 drag_info.finished_callback = &Editor::end_mouse_zoom;
4953 start_grab (event, zoom_cursor);
4955 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4959 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4964 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4965 snap_to (drag_info.current_pointer_frame);
4967 if (drag_info.first_move) {
4968 snap_to (drag_info.grab_frame);
4972 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4974 /* base start and end on initial click position */
4975 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4976 start = drag_info.current_pointer_frame;
4977 end = drag_info.grab_frame;
4979 end = drag_info.current_pointer_frame;
4980 start = drag_info.grab_frame;
4985 if (drag_info.first_move) {
4987 zoom_rect->raise_to_top();
4990 reposition_zoom_rect(start, end);
4992 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4993 drag_info.first_move = false;
4995 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5000 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5002 if (!drag_info.first_move) {
5003 drag_mouse_zoom (item, event);
5005 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5006 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5008 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5011 temporal_zoom_to_frame (false, drag_info.grab_frame);
5013 temporal_zoom_step (false);
5014 center_screen (drag_info.grab_frame);
5022 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
5024 double x1 = frame_to_pixel (start);
5025 double x2 = frame_to_pixel (end);
5026 double y2 = full_canvas_height - 1.0;
5028 zoom_rect->property_x1() = x1;
5029 zoom_rect->property_y1() = 1.0;
5030 zoom_rect->property_x2() = x2;
5031 zoom_rect->property_y2() = y2;
5035 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5037 drag_info.item = item;
5038 drag_info.motion_callback = &Editor::drag_rubberband_select;
5039 drag_info.finished_callback = &Editor::end_rubberband_select;
5041 start_grab (event, cross_hair_cursor);
5043 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5047 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5054 /* use a bigger drag threshold than the default */
5056 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5060 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5061 if (drag_info.first_move) {
5062 snap_to (drag_info.grab_frame);
5064 snap_to (drag_info.current_pointer_frame);
5067 /* base start and end on initial click position */
5069 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5070 start = drag_info.current_pointer_frame;
5071 end = drag_info.grab_frame;
5073 end = drag_info.current_pointer_frame;
5074 start = drag_info.grab_frame;
5077 if (drag_info.current_pointer_y < drag_info.grab_y) {
5078 y1 = drag_info.current_pointer_y;
5079 y2 = drag_info.grab_y;
5081 y2 = drag_info.current_pointer_y;
5082 y1 = drag_info.grab_y;
5086 if (start != end || y1 != y2) {
5088 double x1 = frame_to_pixel (start);
5089 double x2 = frame_to_pixel (end);
5091 rubberband_rect->property_x1() = x1;
5092 rubberband_rect->property_y1() = y1;
5093 rubberband_rect->property_x2() = x2;
5094 rubberband_rect->property_y2() = y2;
5096 rubberband_rect->show();
5097 rubberband_rect->raise_to_top();
5099 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5100 drag_info.first_move = false;
5102 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5107 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5109 if (!drag_info.first_move) {
5111 drag_rubberband_select (item, event);
5114 if (drag_info.current_pointer_y < drag_info.grab_y) {
5115 y1 = drag_info.current_pointer_y;
5116 y2 = drag_info.grab_y;
5119 y2 = drag_info.current_pointer_y;
5120 y1 = drag_info.grab_y;
5124 Selection::Operation op = Keyboard::selection_type (event->button.state);
5127 begin_reversible_command (_("rubberband selection"));
5129 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5130 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5132 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5136 commit_reversible_command ();
5140 selection->clear_tracks();
5141 selection->clear_regions();
5142 selection->clear_points ();
5143 selection->clear_lines ();
5146 rubberband_rect->hide();
5151 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5153 using namespace Gtkmm2ext;
5155 ArdourPrompter prompter (false);
5157 prompter.set_prompt (_("Name for region:"));
5158 prompter.set_initial_text (clicked_regionview->region()->name());
5159 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5160 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5161 prompter.show_all ();
5162 switch (prompter.run ()) {
5163 case Gtk::RESPONSE_ACCEPT:
5165 prompter.get_result(str);
5167 clicked_regionview->region()->set_name (str);
5175 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5177 drag_info.item = item;
5178 drag_info.motion_callback = &Editor::time_fx_motion;
5179 drag_info.finished_callback = &Editor::end_time_fx;
5183 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5187 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5189 RegionView* rv = clicked_regionview;
5191 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5192 snap_to (drag_info.current_pointer_frame);
5195 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5199 if (drag_info.current_pointer_frame > rv->region()->position()) {
5200 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5203 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5204 drag_info.first_move = false;
5206 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5210 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5212 clicked_regionview->get_time_axis_view().hide_timestretch ();
5214 if (drag_info.first_move) {
5218 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5219 /* backwards drag of the left edge - not usable */
5223 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5224 #ifdef USE_RUBBERBAND
5225 float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5227 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5230 begin_reversible_command (_("timestretch"));
5232 // XXX how do timeFX on multiple regions ?
5235 rs.add (clicked_regionview);
5237 if (time_stretch (rs, percentage) == 0) {
5238 session->commit_reversible_command ();
5243 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5245 /* no brushing without a useful snap setting */
5248 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5251 switch (snap_mode) {
5253 return; /* can't work because it allows region to be placed anywhere */
5258 switch (snap_type) {
5266 /* don't brush a copy over the original */
5268 if (pos == rv->region()->position()) {
5272 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5274 if (atv == 0 || !atv->is_audio_track()) {
5278 boost::shared_ptr<Playlist> playlist = atv->playlist();
5279 double speed = atv->get_diskstream()->speed();
5281 XMLNode &before = playlist->get_state();
5282 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5283 XMLNode &after = playlist->get_state();
5284 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5286 // playlist is frozen, so we have to update manually
5288 playlist->Modified(); /* EMIT SIGNAL */
5292 Editor::track_height_step_timeout ()
5295 struct timeval delta;
5297 gettimeofday (&now, 0);
5298 timersub (&now, &last_track_height_step_timestamp, &delta);
5300 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5301 current_stepping_trackview = 0;