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;
85 pointer_window = canvas_window->get_pointer (x, y, mask);
87 if (pointer_window == track_canvas->get_bin_window()) {
89 track_canvas->window_to_world (x, y, wx, wy);
90 in_track_canvas = true;
93 in_track_canvas = false;
95 if (pointer_window == time_canvas->get_bin_window()) {
96 time_canvas->window_to_world (x, y, wx, wy);
102 wx += horizontal_adjustment.get_value();
103 wy += vertical_adjustment.get_value();
106 event.type = GDK_BUTTON_RELEASE;
110 where = event_frame (&event, 0, 0);
115 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
129 switch (event->type) {
130 case GDK_BUTTON_RELEASE:
131 case GDK_BUTTON_PRESS:
132 case GDK_2BUTTON_PRESS:
133 case GDK_3BUTTON_PRESS:
134 track_canvas->w2c(event->button.x, event->button.y, *pcx, *pcy);
136 case GDK_MOTION_NOTIFY:
137 track_canvas->w2c(event->motion.x, event->motion.y, *pcx, *pcy);
139 case GDK_ENTER_NOTIFY:
140 case GDK_LEAVE_NOTIFY:
141 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
144 case GDK_KEY_RELEASE:
145 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
148 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
152 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
153 position is negative (as can be the case with motion events in particular),
154 the frame location is always positive.
157 return pixel_to_frame (*pcx);
161 Editor::mouse_mode_toggled (MouseMode m)
163 if (ignore_mouse_mode_toggle) {
169 if (mouse_select_button.get_active()) {
175 if (mouse_move_button.get_active()) {
181 if (mouse_gain_button.get_active()) {
187 if (mouse_zoom_button.get_active()) {
193 if (mouse_timefx_button.get_active()) {
199 if (mouse_audition_button.get_active()) {
210 Editor::which_grabber_cursor ()
212 switch (_edit_point) {
214 return grabber_edit_point_cursor;
217 return grabber_cursor;
223 Editor::set_canvas_cursor ()
225 switch (mouse_mode) {
227 current_canvas_cursor = selector_cursor;
231 current_canvas_cursor = which_grabber_cursor();
235 current_canvas_cursor = cross_hair_cursor;
239 current_canvas_cursor = zoom_cursor;
243 current_canvas_cursor = time_fx_cursor; // just use playhead
247 current_canvas_cursor = speaker_cursor;
252 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
257 Editor::set_mouse_mode (MouseMode m, bool force)
259 if (drag_info.item) {
263 if (!force && m == mouse_mode) {
271 if (mouse_mode != MouseRange) {
273 /* in all modes except range, hide the range selection,
274 show the object (region) selection.
277 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
278 (*i)->set_should_show_selection (true);
280 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
281 (*i)->hide_selection ();
287 in range mode,show the range selection.
290 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
291 if ((*i)->get_selected()) {
292 (*i)->show_selection (selection->time);
297 /* XXX the hack of unsetting all other buttons should go
298 away once GTK2 allows us to use regular radio buttons drawn like
299 normal buttons, rather than my silly GroupedButton hack.
302 ignore_mouse_mode_toggle = true;
304 switch (mouse_mode) {
306 mouse_select_button.set_active (true);
310 mouse_move_button.set_active (true);
314 mouse_gain_button.set_active (true);
318 mouse_zoom_button.set_active (true);
322 mouse_timefx_button.set_active (true);
326 mouse_audition_button.set_active (true);
330 ignore_mouse_mode_toggle = false;
332 set_canvas_cursor ();
336 Editor::step_mouse_mode (bool next)
338 switch (current_mouse_mode()) {
340 if (next) set_mouse_mode (MouseRange);
341 else set_mouse_mode (MouseTimeFX);
345 if (next) set_mouse_mode (MouseZoom);
346 else set_mouse_mode (MouseObject);
350 if (next) set_mouse_mode (MouseGain);
351 else set_mouse_mode (MouseRange);
355 if (next) set_mouse_mode (MouseTimeFX);
356 else set_mouse_mode (MouseZoom);
360 if (next) set_mouse_mode (MouseAudition);
361 else set_mouse_mode (MouseGain);
365 if (next) set_mouse_mode (MouseObject);
366 else set_mouse_mode (MouseTimeFX);
372 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
374 /* in object/audition/timefx mode, any button press sets
375 the selection if the object can be selected. this is a
376 bit of hack, because we want to avoid this if the
377 mouse operation is a region alignment.
379 note: not dbl-click or triple-click
382 if (((mouse_mode != MouseObject) &&
383 (mouse_mode != MouseAudition || item_type != RegionItem) &&
384 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
385 (mouse_mode != MouseRange)) ||
387 (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
392 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
394 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
396 /* almost no selection action on modified button-2 or button-3 events */
398 if (item_type != RegionItem && event->button.button != 2) {
404 Selection::Operation op = Keyboard::selection_type (event->button.state);
405 bool press = (event->type == GDK_BUTTON_PRESS);
407 // begin_reversible_command (_("select on click"));
411 if (mouse_mode != MouseRange) {
412 set_selected_regionview_from_click (press, op, true);
413 } else if (event->type == GDK_BUTTON_PRESS) {
414 set_selected_track_as_side_effect ();
418 case RegionViewNameHighlight:
420 if (mouse_mode != MouseRange) {
421 set_selected_regionview_from_click (press, op, true);
422 } else if (event->type == GDK_BUTTON_PRESS) {
423 set_selected_track_as_side_effect ();
427 case FadeInHandleItem:
429 case FadeOutHandleItem:
431 if (mouse_mode != MouseRange) {
432 set_selected_regionview_from_click (press, op, true);
433 } else if (event->type == GDK_BUTTON_PRESS) {
434 set_selected_track_as_side_effect ();
438 case GainAutomationControlPointItem:
439 case PanAutomationControlPointItem:
440 case RedirectAutomationControlPointItem:
441 set_selected_track_as_side_effect ();
442 if (mouse_mode != MouseRange) {
443 set_selected_control_point_from_click (op, false);
448 /* for context click or range selection, select track */
449 if (event->button.button == 3) {
450 set_selected_track_as_side_effect ();
451 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
452 set_selected_track_as_side_effect ();
456 case AutomationTrackItem:
457 set_selected_track_as_side_effect (true);
465 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
468 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
470 track_canvas->grab_focus();
472 if (session && session->actively_recording()) {
476 button_selection (item, event, item_type);
478 if (drag_info.item == 0 &&
479 (Keyboard::is_delete_event (&event->button) ||
480 Keyboard::is_context_menu_event (&event->button) ||
481 Keyboard::is_edit_event (&event->button))) {
483 /* handled by button release */
487 switch (event->button.button) {
490 if (event->type == GDK_BUTTON_PRESS) {
492 if (drag_info.item) {
493 drag_info.item->ungrab (event->button.time);
496 /* single mouse clicks on any of these item types operate
497 independent of mouse mode, mostly because they are
498 not on the main track canvas or because we want
503 case PlayheadCursorItem:
504 start_cursor_grab (item, event);
508 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
509 hide_marker (item, event);
511 start_marker_grab (item, event);
515 case TempoMarkerItem:
516 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
517 start_tempo_marker_copy_grab (item, event);
519 start_tempo_marker_grab (item, event);
523 case MeterMarkerItem:
524 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
525 start_meter_marker_copy_grab (item, event);
527 start_meter_marker_grab (item, event);
537 case RangeMarkerBarItem:
538 start_range_markerbar_op (item, event, CreateRangeMarker);
542 case CdMarkerBarItem:
543 start_range_markerbar_op (item, event, CreateCDMarker);
547 case TransportMarkerBarItem:
548 start_range_markerbar_op (item, event, CreateTransportMarker);
557 switch (mouse_mode) {
560 case StartSelectionTrimItem:
561 start_selection_op (item, event, SelectionStartTrim);
564 case EndSelectionTrimItem:
565 start_selection_op (item, event, SelectionEndTrim);
569 if (Keyboard::modifier_state_contains
570 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
571 // contains and not equals because I can't use alt as a modifier alone.
572 start_selection_grab (item, event);
573 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
574 /* grab selection for moving */
575 start_selection_op (item, event, SelectionMove);
577 /* this was debated, but decided the more common action was to
578 make a new selection */
579 start_selection_op (item, event, CreateSelection);
584 start_selection_op (item, event, CreateSelection);
590 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
591 event->type == GDK_BUTTON_PRESS) {
593 start_rubberband_select (item, event);
595 } else if (event->type == GDK_BUTTON_PRESS) {
598 case FadeInHandleItem:
599 start_fade_in_grab (item, event);
602 case FadeOutHandleItem:
603 start_fade_out_grab (item, event);
607 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
608 start_region_copy_grab (item, event);
609 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
610 start_region_brush_grab (item, event);
612 start_region_grab (item, event);
616 case RegionViewNameHighlight:
617 start_trim (item, event);
622 /* rename happens on edit clicks */
623 start_trim (clicked_regionview->get_name_highlight(), event);
627 case GainAutomationControlPointItem:
628 case PanAutomationControlPointItem:
629 case RedirectAutomationControlPointItem:
630 start_control_point_grab (item, event);
634 case GainAutomationLineItem:
635 case PanAutomationLineItem:
636 case RedirectAutomationLineItem:
637 start_line_grab_from_line (item, event);
642 case AutomationTrackItem:
643 start_rubberband_select (item, event);
646 /* <CMT Additions> */
647 case ImageFrameHandleStartItem:
648 imageframe_start_handle_op(item, event) ;
651 case ImageFrameHandleEndItem:
652 imageframe_end_handle_op(item, event) ;
655 case MarkerViewHandleStartItem:
656 markerview_item_start_handle_op(item, event) ;
659 case MarkerViewHandleEndItem:
660 markerview_item_end_handle_op(item, event) ;
663 /* </CMT Additions> */
665 /* <CMT Additions> */
667 start_markerview_grab(item, event) ;
670 start_imageframe_grab(item, event) ;
672 /* </CMT Additions> */
688 // start_line_grab_from_regionview (item, event);
691 case GainControlPointItem:
692 start_control_point_grab (item, event);
696 start_line_grab_from_line (item, event);
699 case GainAutomationControlPointItem:
700 case PanAutomationControlPointItem:
701 case RedirectAutomationControlPointItem:
702 start_control_point_grab (item, event);
713 case GainAutomationControlPointItem:
714 case PanAutomationControlPointItem:
715 case RedirectAutomationControlPointItem:
716 start_control_point_grab (item, event);
719 case GainAutomationLineItem:
720 case PanAutomationLineItem:
721 case RedirectAutomationLineItem:
722 start_line_grab_from_line (item, event);
726 // XXX need automation mode to identify which
728 // start_line_grab_from_regionview (item, event);
738 if (event->type == GDK_BUTTON_PRESS) {
739 start_mouse_zoom (item, event);
746 if (item_type == RegionItem) {
747 start_time_fx (item, event);
754 scrub_reverse_distance = 0;
755 last_scrub_x = event->button.x;
756 scrubbing_direction = 0;
757 track_canvas->get_window()->set_cursor (*transparent_cursor);
758 /* rest handled in motion & release */
767 switch (mouse_mode) {
769 if (event->type == GDK_BUTTON_PRESS) {
772 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
773 start_region_copy_grab (item, event);
775 start_region_grab (item, event);
780 case GainAutomationControlPointItem:
781 case PanAutomationControlPointItem:
782 case RedirectAutomationControlPointItem:
783 start_control_point_grab (item, event);
794 case RegionViewNameHighlight:
795 start_trim (item, event);
800 start_trim (clicked_regionview->get_name_highlight(), event);
811 if (event->type == GDK_BUTTON_PRESS) {
812 /* relax till release */
819 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
820 temporal_zoom_session();
822 temporal_zoom_to_frame (true, event_frame(event));
845 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
847 nframes_t where = event_frame (event, 0, 0);
848 AutomationTimeAxisView* atv = 0;
850 /* no action if we're recording */
852 if (session && session->actively_recording()) {
856 /* first, see if we're finishing a drag ... */
858 if (drag_info.item) {
859 if (end_grab (item, event)) {
860 /* grab dragged, so do nothing else */
865 button_selection (item, event, item_type);
867 /* edit events get handled here */
869 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
875 case TempoMarkerItem:
876 edit_tempo_marker (item);
879 case MeterMarkerItem:
880 edit_meter_marker (item);
884 if (clicked_regionview->name_active()) {
885 return mouse_rename_region (item, event);
895 /* context menu events get handled here */
897 if (Keyboard::is_context_menu_event (&event->button)) {
899 if (drag_info.item == 0) {
901 /* no matter which button pops up the context menu, tell the menu
902 widget to use button 1 to drive menu selection.
907 case FadeInHandleItem:
909 case FadeOutHandleItem:
910 popup_fade_context_menu (1, event->button.time, item, item_type);
914 popup_track_context_menu (1, event->button.time, item_type, false, where);
918 case RegionViewNameHighlight:
920 popup_track_context_menu (1, event->button.time, item_type, false, where);
924 popup_track_context_menu (1, event->button.time, item_type, true, where);
927 case AutomationTrackItem:
928 popup_track_context_menu (1, event->button.time, item_type, false, where);
932 case RangeMarkerBarItem:
933 case TransportMarkerBarItem:
934 case CdMarkerBarItem:
937 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
941 marker_context_menu (&event->button, item);
944 case TempoMarkerItem:
945 tm_marker_context_menu (&event->button, item);
948 case MeterMarkerItem:
949 tm_marker_context_menu (&event->button, item);
952 case CrossfadeViewItem:
953 popup_track_context_menu (1, event->button.time, item_type, false, where);
956 /* <CMT Additions> */
958 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
960 case ImageFrameTimeAxisItem:
961 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
964 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
966 case MarkerTimeAxisItem:
967 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
969 /* <CMT Additions> */
980 /* delete events get handled here */
982 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
985 case TempoMarkerItem:
986 remove_tempo_marker (item);
989 case MeterMarkerItem:
990 remove_meter_marker (item);
994 remove_marker (*item, event);
998 if (mouse_mode == MouseObject) {
999 remove_clicked_region ();
1003 case GainControlPointItem:
1004 if (mouse_mode == MouseGain) {
1005 remove_gain_control_point (item, event);
1009 case GainAutomationControlPointItem:
1010 case PanAutomationControlPointItem:
1011 case RedirectAutomationControlPointItem:
1012 remove_control_point (item, event);
1021 switch (event->button.button) {
1024 switch (item_type) {
1025 /* see comments in button_press_handler */
1026 case PlayheadCursorItem:
1029 case GainAutomationLineItem:
1030 case PanAutomationLineItem:
1031 case RedirectAutomationLineItem:
1032 case StartSelectionTrimItem:
1033 case EndSelectionTrimItem:
1037 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1038 snap_to (where, 0, true);
1040 mouse_add_new_marker (where);
1043 case CdMarkerBarItem:
1044 // if we get here then a dragged range wasn't done
1045 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1046 snap_to (where, 0, true);
1048 mouse_add_new_marker (where, true);
1052 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1055 mouse_add_new_tempo_event (where);
1059 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1067 switch (mouse_mode) {
1069 switch (item_type) {
1070 case AutomationTrackItem:
1071 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_trackview);
1073 atv->add_automation_event (item, event, where, event->button.y);
1085 // Gain only makes sense for audio regions
1087 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1091 switch (item_type) {
1093 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1097 case AutomationTrackItem:
1098 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1099 add_automation_event (item, event, where, event->button.y);
1109 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1110 if (scrubbing_direction == 0) {
1111 /* no drag, just a click */
1112 switch (item_type) {
1114 play_selected_region ();
1120 /* make sure we stop */
1121 session->request_transport_speed (0.0);
1135 switch (mouse_mode) {
1138 switch (item_type) {
1140 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1142 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1145 // Button2 click is unused
1158 // x_style_paste (where, 1.0);
1178 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1184 if (last_item_entered != item) {
1185 last_item_entered = item;
1186 last_item_entered_n = 0;
1189 switch (item_type) {
1190 case GainControlPointItem:
1191 if (mouse_mode == MouseGain) {
1192 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1193 cp->set_visible (true);
1197 at_y = cp->get_y ();
1198 cp->item->i2w (at_x, at_y);
1202 fraction = 1.0 - (cp->get_y() / cp->line.height());
1204 if (is_drawable() && !_scrubbing) {
1205 track_canvas->get_window()->set_cursor (*fader_cursor);
1208 last_item_entered_n++;
1209 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1210 if (last_item_entered_n < 10) {
1211 show_verbose_canvas_cursor ();
1216 case GainAutomationControlPointItem:
1217 case PanAutomationControlPointItem:
1218 case RedirectAutomationControlPointItem:
1219 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1220 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1221 cp->set_visible (true);
1225 at_y = cp->get_y ();
1226 cp->item->i2w (at_x, at_y);
1230 fraction = 1.0 - (cp->get_y() / cp->line.height());
1232 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1233 show_verbose_canvas_cursor ();
1235 if (is_drawable()) {
1236 track_canvas->get_window()->set_cursor (*fader_cursor);
1242 if (mouse_mode == MouseGain) {
1243 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1245 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1246 if (is_drawable()) {
1247 track_canvas->get_window()->set_cursor (*fader_cursor);
1252 case GainAutomationLineItem:
1253 case RedirectAutomationLineItem:
1254 case PanAutomationLineItem:
1255 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1257 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1259 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1261 if (is_drawable()) {
1262 track_canvas->get_window()->set_cursor (*fader_cursor);
1267 case RegionViewNameHighlight:
1268 if (is_drawable() && mouse_mode == MouseObject) {
1269 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1273 case StartSelectionTrimItem:
1274 case EndSelectionTrimItem:
1275 /* <CMT Additions> */
1276 case ImageFrameHandleStartItem:
1277 case ImageFrameHandleEndItem:
1278 case MarkerViewHandleStartItem:
1279 case MarkerViewHandleEndItem:
1280 /* </CMT Additions> */
1282 if (is_drawable()) {
1283 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1287 case PlayheadCursorItem:
1288 if (is_drawable()) {
1289 switch (_edit_point) {
1291 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1294 track_canvas->get_window()->set_cursor (*grabber_cursor);
1300 case RegionViewName:
1302 /* when the name is not an active item, the entire name highlight is for trimming */
1304 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1305 if (mouse_mode == MouseObject && is_drawable()) {
1306 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1312 case AutomationTrackItem:
1313 if (is_drawable()) {
1314 Gdk::Cursor *cursor;
1315 switch (mouse_mode) {
1317 cursor = selector_cursor;
1320 cursor = zoom_cursor;
1323 cursor = cross_hair_cursor;
1327 track_canvas->get_window()->set_cursor (*cursor);
1329 AutomationTimeAxisView* atv;
1330 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1331 clear_entered_track = false;
1332 set_entered_track (atv);
1338 case RangeMarkerBarItem:
1339 case TransportMarkerBarItem:
1340 case CdMarkerBarItem:
1343 if (is_drawable()) {
1344 time_canvas->get_window()->set_cursor (*timebar_cursor);
1349 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1352 entered_marker = marker;
1353 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1355 case MeterMarkerItem:
1356 case TempoMarkerItem:
1357 if (is_drawable()) {
1358 time_canvas->get_window()->set_cursor (*timebar_cursor);
1361 case FadeInHandleItem:
1362 case FadeOutHandleItem:
1363 if (mouse_mode == MouseObject) {
1364 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1366 rect->property_fill_color_rgba() = 0;
1367 rect->property_outline_pixels() = 1;
1376 /* second pass to handle entered track status in a comprehensible way.
1379 switch (item_type) {
1381 case GainAutomationLineItem:
1382 case RedirectAutomationLineItem:
1383 case PanAutomationLineItem:
1384 case GainControlPointItem:
1385 case GainAutomationControlPointItem:
1386 case PanAutomationControlPointItem:
1387 case RedirectAutomationControlPointItem:
1388 /* these do not affect the current entered track state */
1389 clear_entered_track = false;
1392 case AutomationTrackItem:
1393 /* handled above already */
1397 set_entered_track (0);
1405 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1414 switch (item_type) {
1415 case GainControlPointItem:
1416 case GainAutomationControlPointItem:
1417 case PanAutomationControlPointItem:
1418 case RedirectAutomationControlPointItem:
1419 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1420 if (cp->line.npoints() > 1) {
1421 if (!cp->selected) {
1422 cp->set_visible (false);
1426 if (is_drawable()) {
1427 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1430 hide_verbose_canvas_cursor ();
1433 case RegionViewNameHighlight:
1434 case StartSelectionTrimItem:
1435 case EndSelectionTrimItem:
1436 case PlayheadCursorItem:
1437 /* <CMT Additions> */
1438 case ImageFrameHandleStartItem:
1439 case ImageFrameHandleEndItem:
1440 case MarkerViewHandleStartItem:
1441 case MarkerViewHandleEndItem:
1442 /* </CMT Additions> */
1443 if (is_drawable()) {
1444 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1449 case GainAutomationLineItem:
1450 case RedirectAutomationLineItem:
1451 case PanAutomationLineItem:
1452 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1454 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1456 line->property_fill_color_rgba() = al->get_line_color();
1458 if (is_drawable()) {
1459 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1463 case RegionViewName:
1464 /* see enter_handler() for notes */
1465 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1466 if (is_drawable() && mouse_mode == MouseObject) {
1467 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1472 case RangeMarkerBarItem:
1473 case TransportMarkerBarItem:
1474 case CdMarkerBarItem:
1478 if (is_drawable()) {
1479 time_canvas->get_window()->set_cursor (*timebar_cursor);
1484 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1488 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1489 location_flags_changed (loc, this);
1492 case MeterMarkerItem:
1493 case TempoMarkerItem:
1495 if (is_drawable()) {
1496 time_canvas->get_window()->set_cursor (*timebar_cursor);
1501 case FadeInHandleItem:
1502 case FadeOutHandleItem:
1503 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1505 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1507 rect->property_fill_color_rgba() = rv->get_fill_color();
1508 rect->property_outline_pixels() = 0;
1513 case AutomationTrackItem:
1514 if (is_drawable()) {
1515 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1516 clear_entered_track = true;
1517 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1529 Editor::left_automation_track ()
1531 if (clear_entered_track) {
1532 set_entered_track (0);
1533 clear_entered_track = false;
1543 if (scrubbing_direction == 0) {
1545 session->request_locate (drag_info.current_pointer_frame, false);
1546 session->request_transport_speed (0.1);
1547 scrubbing_direction = 1;
1551 if (last_scrub_x > drag_info.current_pointer_x) {
1553 /* pointer moved to the left */
1555 if (scrubbing_direction > 0) {
1557 /* we reversed direction to go backwards */
1560 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1564 /* still moving to the left (backwards) */
1566 scrub_reversals = 0;
1567 scrub_reverse_distance = 0;
1569 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1570 session->request_transport_speed (session->transport_speed() - delta);
1574 /* pointer moved to the right */
1576 if (scrubbing_direction < 0) {
1577 /* we reversed direction to go forward */
1580 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1583 /* still moving to the right */
1585 scrub_reversals = 0;
1586 scrub_reverse_distance = 0;
1588 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1589 session->request_transport_speed (session->transport_speed() + delta);
1593 /* if there have been more than 2 opposite motion moves detected, or one that moves
1594 back more than 10 pixels, reverse direction
1597 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1599 if (scrubbing_direction > 0) {
1600 /* was forwards, go backwards */
1601 session->request_transport_speed (-0.1);
1602 scrubbing_direction = -1;
1604 /* was backwards, go forwards */
1605 session->request_transport_speed (0.1);
1606 scrubbing_direction = 1;
1609 scrub_reverse_distance = 0;
1610 scrub_reversals = 0;
1614 last_scrub_x = drag_info.current_pointer_x;
1618 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1620 if (event->motion.is_hint) {
1623 /* We call this so that MOTION_NOTIFY events continue to be
1624 delivered to the canvas. We need to do this because we set
1625 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1626 the density of the events, at the expense of a round-trip
1627 to the server. Given that this will mostly occur on cases
1628 where DISPLAY = :0.0, and given the cost of what the motion
1629 event might do, its a good tradeoff.
1632 track_canvas->get_pointer (x, y);
1635 if (current_stepping_trackview) {
1636 /* don't keep the persistent stepped trackview if the mouse moves */
1637 current_stepping_trackview = 0;
1638 step_timeout.disconnect ();
1641 if (session && session->actively_recording()) {
1642 /* Sorry. no dragging stuff around while we record */
1646 drag_info.item_type = item_type;
1647 drag_info.last_pointer_x = drag_info.current_pointer_x;
1648 drag_info.last_pointer_y = drag_info.current_pointer_y;
1649 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1650 &drag_info.current_pointer_y);
1653 switch (mouse_mode) {
1664 if (!from_autoscroll && drag_info.item) {
1665 /* item != 0 is the best test i can think of for dragging.
1667 if (!drag_info.move_threshold_passed) {
1669 bool x_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1670 bool y_threshold_passed = (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1672 drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1674 // and change the initial grab loc/frame if this drag info wants us to
1676 if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1677 drag_info.grab_frame = drag_info.current_pointer_frame;
1678 drag_info.grab_x = drag_info.current_pointer_x;
1679 drag_info.grab_y = drag_info.current_pointer_y;
1680 drag_info.last_pointer_frame = drag_info.grab_frame;
1681 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1686 switch (item_type) {
1687 case PlayheadCursorItem:
1689 case GainControlPointItem:
1690 case RedirectAutomationControlPointItem:
1691 case GainAutomationControlPointItem:
1692 case PanAutomationControlPointItem:
1693 case TempoMarkerItem:
1694 case MeterMarkerItem:
1695 case RegionViewNameHighlight:
1696 case StartSelectionTrimItem:
1697 case EndSelectionTrimItem:
1700 case RedirectAutomationLineItem:
1701 case GainAutomationLineItem:
1702 case PanAutomationLineItem:
1703 case FadeInHandleItem:
1704 case FadeOutHandleItem:
1705 /* <CMT Additions> */
1706 case ImageFrameHandleStartItem:
1707 case ImageFrameHandleEndItem:
1708 case MarkerViewHandleStartItem:
1709 case MarkerViewHandleEndItem:
1710 /* </CMT Additions> */
1711 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1712 (event->motion.state & Gdk::BUTTON2_MASK))) {
1713 if (!from_autoscroll) {
1714 maybe_autoscroll (&event->motion);
1716 (this->*(drag_info.motion_callback)) (item, event);
1725 switch (mouse_mode) {
1730 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1731 (event->motion.state & GDK_BUTTON2_MASK))) {
1732 if (!from_autoscroll) {
1733 maybe_autoscroll (&event->motion);
1735 (this->*(drag_info.motion_callback)) (item, event);
1746 track_canvas_motion (event);
1747 // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1755 Editor::break_drag ()
1757 stop_canvas_autoscroll ();
1758 hide_verbose_canvas_cursor ();
1760 if (drag_info.item) {
1761 drag_info.item->ungrab (0);
1763 /* put it back where it came from */
1768 drag_info.item->i2w (cxw, cyw);
1769 drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw);
1776 Editor::finalize_drag ()
1779 drag_info.copy = false;
1780 drag_info.motion_callback = 0;
1781 drag_info.finished_callback = 0;
1782 drag_info.dest_trackview = 0;
1783 drag_info.source_trackview = 0;
1784 drag_info.last_frame_position = 0;
1785 drag_info.grab_frame = 0;
1786 drag_info.last_pointer_frame = 0;
1787 drag_info.current_pointer_frame = 0;
1788 drag_info.brushing = false;
1790 if (drag_info.copied_location) {
1791 delete drag_info.copied_location;
1792 drag_info.copied_location = 0;
1797 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1799 if (drag_info.item == 0) {
1800 fatal << _("programming error: start_grab called without drag item") << endmsg;
1806 cursor = which_grabber_cursor ();
1809 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1811 if (event->button.button == 2) {
1812 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1813 drag_info.y_constrained = true;
1814 drag_info.x_constrained = false;
1816 drag_info.y_constrained = false;
1817 drag_info.x_constrained = true;
1820 drag_info.x_constrained = false;
1821 drag_info.y_constrained = false;
1824 drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1825 drag_info.last_pointer_frame = drag_info.grab_frame;
1826 drag_info.current_pointer_frame = drag_info.grab_frame;
1827 drag_info.current_pointer_x = drag_info.grab_x;
1828 drag_info.current_pointer_y = drag_info.grab_y;
1829 drag_info.last_pointer_x = drag_info.current_pointer_x;
1830 drag_info.last_pointer_y = drag_info.current_pointer_y;
1831 drag_info.cumulative_x_drag = 0;
1832 drag_info.cumulative_y_drag = 0;
1833 drag_info.first_move = true;
1834 drag_info.move_threshold_passed = false;
1835 drag_info.want_move_threshold = false;
1836 drag_info.pointer_frame_offset = 0;
1837 drag_info.brushing = false;
1838 drag_info.copied_location = 0;
1840 drag_info.original_x = 0;
1841 drag_info.original_y = 0;
1842 drag_info.item->i2w (drag_info.original_x, drag_info.original_y);
1844 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1846 event->button.time);
1848 if (session && session->transport_rolling()) {
1849 drag_info.was_rolling = true;
1851 drag_info.was_rolling = false;
1854 switch (snap_type) {
1855 case SnapToRegionStart:
1856 case SnapToRegionEnd:
1857 case SnapToRegionSync:
1858 case SnapToRegionBoundary:
1859 build_region_boundary_cache ();
1867 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1869 drag_info.item->ungrab (0);
1870 drag_info.item = new_item;
1873 cursor = which_grabber_cursor ();
1876 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1880 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1882 bool did_drag = false;
1884 stop_canvas_autoscroll ();
1886 if (drag_info.item == 0) {
1890 drag_info.item->ungrab (event->button.time);
1892 if (drag_info.finished_callback) {
1893 drag_info.last_pointer_x = drag_info.current_pointer_x;
1894 drag_info.last_pointer_y = drag_info.current_pointer_y;
1895 (this->*(drag_info.finished_callback)) (item, event);
1898 did_drag = !drag_info.first_move;
1900 hide_verbose_canvas_cursor();
1908 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1910 drag_info.item = item;
1911 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1912 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1916 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1917 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1921 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1923 drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());
1927 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1929 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1931 nframes_t fade_length;
1933 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1934 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1940 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1944 if (pos < (arv->region()->position() + 64)) {
1945 fade_length = 64; // this should be a minimum defined somewhere
1946 } else if (pos > arv->region()->last_frame()) {
1947 fade_length = arv->region()->length();
1949 fade_length = pos - arv->region()->position();
1951 /* mapover the region selection */
1953 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1955 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1961 tmp->reset_fade_in_shape_width (fade_length);
1964 show_verbose_duration_cursor (arv->region()->position(), arv->region()->position() + fade_length, 10);
1966 drag_info.first_move = false;
1970 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1972 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1974 nframes_t fade_length;
1976 if (drag_info.first_move) return;
1978 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1979 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1984 if (pos < (arv->region()->position() + 64)) {
1985 fade_length = 64; // this should be a minimum defined somewhere
1986 } else if (pos > arv->region()->last_frame()) {
1987 fade_length = arv->region()->length();
1989 fade_length = pos - arv->region()->position();
1992 begin_reversible_command (_("change fade in length"));
1994 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1996 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2002 AutomationList& alist = tmp->audio_region()->fade_in();
2003 XMLNode &before = alist.get_state();
2005 tmp->audio_region()->set_fade_in_length (fade_length);
2006 tmp->audio_region()->set_fade_in_active (true);
2008 XMLNode &after = alist.get_state();
2009 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2012 commit_reversible_command ();
2016 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2018 drag_info.item = item;
2019 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2020 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2024 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2025 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2029 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2031 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());
2035 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2037 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2039 nframes_t fade_length;
2041 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2042 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2047 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2051 if (pos > (arv->region()->last_frame() - 64)) {
2052 fade_length = 64; // this should really be a minimum fade defined somewhere
2054 else if (pos < arv->region()->position()) {
2055 fade_length = arv->region()->length();
2058 fade_length = arv->region()->last_frame() - pos;
2061 /* mapover the region selection */
2063 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2065 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2071 tmp->reset_fade_out_shape_width (fade_length);
2074 show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2076 drag_info.first_move = false;
2080 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2082 if (drag_info.first_move) return;
2084 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2086 nframes_t fade_length;
2088 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2089 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2095 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2099 if (pos > (arv->region()->last_frame() - 64)) {
2100 fade_length = 64; // this should really be a minimum fade defined somewhere
2102 else if (pos < arv->region()->position()) {
2103 fade_length = arv->region()->length();
2106 fade_length = arv->region()->last_frame() - pos;
2109 begin_reversible_command (_("change fade out length"));
2111 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2113 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2119 AutomationList& alist = tmp->audio_region()->fade_out();
2120 XMLNode &before = alist.get_state();
2122 tmp->audio_region()->set_fade_out_length (fade_length);
2123 tmp->audio_region()->set_fade_out_active (true);
2125 XMLNode &after = alist.get_state();
2126 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
2129 commit_reversible_command ();
2133 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2135 drag_info.item = item;
2136 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2137 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2141 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2142 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2146 Cursor* cursor = (Cursor *) drag_info.data;
2148 if (cursor == playhead_cursor) {
2149 _dragging_playhead = true;
2151 if (session && drag_info.was_rolling) {
2152 session->request_stop ();
2155 if (session && session->is_auditioning()) {
2156 session->cancel_audition ();
2160 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
2162 show_verbose_time_cursor (cursor->current_frame, 10);
2166 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2168 Cursor* cursor = (Cursor *) drag_info.data;
2169 nframes_t adjusted_frame;
2171 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2172 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2178 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2179 if (cursor == playhead_cursor) {
2180 snap_to (adjusted_frame);
2184 if (adjusted_frame == drag_info.last_pointer_frame) return;
2186 cursor->set_position (adjusted_frame);
2188 UpdateAllTransportClocks (cursor->current_frame);
2190 show_verbose_time_cursor (cursor->current_frame, 10);
2192 drag_info.last_pointer_frame = adjusted_frame;
2193 drag_info.first_move = false;
2197 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2199 if (drag_info.first_move) return;
2201 cursor_drag_motion_callback (item, event);
2203 _dragging_playhead = false;
2205 if (item == &playhead_cursor->canvas_item) {
2207 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2213 Editor::update_marker_drag_item (Location *location)
2215 double x1 = frame_to_pixel (location->start());
2216 double x2 = frame_to_pixel (location->end());
2218 if (location->is_mark()) {
2219 marker_drag_line_points.front().set_x(x1);
2220 marker_drag_line_points.back().set_x(x1);
2221 marker_drag_line->property_points() = marker_drag_line_points;
2224 range_marker_drag_rect->property_x1() = x1;
2225 range_marker_drag_rect->property_x2() = x2;
2230 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2234 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2235 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2241 Location *location = find_location_from_marker (marker, is_start);
2243 drag_info.item = item;
2244 drag_info.data = marker;
2245 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2246 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2250 _dragging_edit_point = true;
2252 drag_info.copied_location = new Location (*location);
2253 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2255 update_marker_drag_item (location);
2257 if (location->is_mark()) {
2258 // marker_drag_line->show();
2259 // marker_drag_line->raise_to_top();
2261 range_marker_drag_rect->show();
2262 range_marker_drag_rect->raise_to_top();
2266 show_verbose_time_cursor (location->start(), 10);
2268 show_verbose_time_cursor (location->end(), 10);
2271 Selection::Operation op = Keyboard::selection_type (event->button.state);
2274 case Selection::Toggle:
2275 selection->toggle (marker);
2277 case Selection::Set:
2278 selection->set (marker);
2280 case Selection::Extend:
2281 selection->add (marker);
2283 case Selection::Add:
2284 selection->add (marker);
2290 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2293 Marker* marker = (Marker *) drag_info.data;
2294 Location *real_location;
2295 Location *copy_location;
2297 bool move_both = false;
2300 if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2301 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2306 nframes_t next = newframe;
2308 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2309 snap_to (newframe, 0, true);
2312 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
2316 /* call this to find out if its the start or end */
2318 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2322 if (real_location->locked()) {
2326 /* use the copy that we're "dragging" around */
2328 copy_location = drag_info.copied_location;
2330 f_delta = copy_location->end() - copy_location->start();
2332 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2336 if (copy_location->is_mark()) {
2339 copy_location->set_start (newframe);
2343 if (is_start) { // start-of-range marker
2346 copy_location->set_start (newframe);
2347 copy_location->set_end (newframe + f_delta);
2348 } else if (newframe < copy_location->end()) {
2349 copy_location->set_start (newframe);
2351 snap_to (next, 1, true);
2352 copy_location->set_end (next);
2353 copy_location->set_start (newframe);
2356 } else { // end marker
2359 copy_location->set_end (newframe);
2360 copy_location->set_start (newframe - f_delta);
2361 } else if (newframe > copy_location->start()) {
2362 copy_location->set_end (newframe);
2364 } else if (newframe > 0) {
2365 snap_to (next, -1, true);
2366 copy_location->set_start (next);
2367 copy_location->set_end (newframe);
2372 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2373 drag_info.first_move = false;
2375 update_marker_drag_item (copy_location);
2377 LocationMarkers* lm = find_location_markers (real_location);
2378 lm->set_position (copy_location->start(), copy_location->end());
2379 edit_point_clock.set (copy_location->start());
2381 show_verbose_time_cursor (newframe, 10);
2385 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2387 if (drag_info.first_move) {
2388 /* just a click, do nothing but whatever selection occured */
2392 _dragging_edit_point = false;
2394 Marker* marker = (Marker *) drag_info.data;
2397 begin_reversible_command ( _("move marker") );
2398 XMLNode &before = session->locations()->get_state();
2400 Location * location = find_location_from_marker (marker, is_start);
2404 if (location->locked()) {
2408 if (location->is_mark()) {
2409 location->set_start (drag_info.copied_location->start());
2411 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2415 XMLNode &after = session->locations()->get_state();
2416 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2417 commit_reversible_command ();
2419 marker_drag_line->hide();
2420 range_marker_drag_rect->hide();
2424 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2427 MeterMarker* meter_marker;
2429 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2430 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2434 meter_marker = dynamic_cast<MeterMarker*> (marker);
2436 MetricSection& section (meter_marker->meter());
2438 if (!section.movable()) {
2442 drag_info.item = item;
2443 drag_info.copy = false;
2444 drag_info.data = marker;
2445 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2446 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2450 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2452 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2456 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2459 MeterMarker* meter_marker;
2461 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2462 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2466 meter_marker = dynamic_cast<MeterMarker*> (marker);
2468 // create a dummy marker for visual representation of moving the copy.
2469 // The actual copying is not done before we reach the finish callback.
2471 snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2472 MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
2473 *new MeterSection(meter_marker->meter()));
2475 drag_info.item = &new_marker->the_item();
2476 drag_info.copy = true;
2477 drag_info.data = new_marker;
2478 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2479 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2483 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2485 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2489 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2491 MeterMarker* marker = (MeterMarker *) drag_info.data;
2492 nframes_t adjusted_frame;
2494 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2495 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2501 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2502 snap_to (adjusted_frame);
2505 if (adjusted_frame == drag_info.last_pointer_frame) return;
2507 marker->set_position (adjusted_frame);
2510 drag_info.last_pointer_frame = adjusted_frame;
2511 drag_info.first_move = false;
2513 show_verbose_time_cursor (adjusted_frame, 10);
2517 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2519 if (drag_info.first_move) return;
2521 meter_marker_drag_motion_callback (drag_info.item, event);
2523 MeterMarker* marker = (MeterMarker *) drag_info.data;
2526 TempoMap& map (session->tempo_map());
2527 map.bbt_time (drag_info.last_pointer_frame, when);
2529 if (drag_info.copy == true) {
2530 begin_reversible_command (_("copy meter mark"));
2531 XMLNode &before = map.get_state();
2532 map.add_meter (marker->meter(), when);
2533 XMLNode &after = map.get_state();
2534 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2535 commit_reversible_command ();
2537 // delete the dummy marker we used for visual representation of copying.
2538 // a new visual marker will show up automatically.
2541 begin_reversible_command (_("move meter mark"));
2542 XMLNode &before = map.get_state();
2543 map.move_meter (marker->meter(), when);
2544 XMLNode &after = map.get_state();
2545 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2546 commit_reversible_command ();
2551 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2554 TempoMarker* tempo_marker;
2556 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2557 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2561 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2562 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2566 MetricSection& section (tempo_marker->tempo());
2568 if (!section.movable()) {
2572 drag_info.item = item;
2573 drag_info.copy = false;
2574 drag_info.data = marker;
2575 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2576 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2580 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2581 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2585 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2588 TempoMarker* tempo_marker;
2590 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2591 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2595 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2596 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2600 // create a dummy marker for visual representation of moving the copy.
2601 // The actual copying is not done before we reach the finish callback.
2603 snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2604 TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2605 *new TempoSection(tempo_marker->tempo()));
2607 drag_info.item = &new_marker->the_item();
2608 drag_info.copy = true;
2609 drag_info.data = new_marker;
2610 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2611 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2615 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2617 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2621 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2623 TempoMarker* marker = (TempoMarker *) drag_info.data;
2624 nframes_t adjusted_frame;
2626 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2627 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2633 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2634 snap_to (adjusted_frame);
2637 if (adjusted_frame == drag_info.last_pointer_frame) return;
2639 /* OK, we've moved far enough to make it worth actually move the thing. */
2641 marker->set_position (adjusted_frame);
2643 show_verbose_time_cursor (adjusted_frame, 10);
2645 drag_info.last_pointer_frame = adjusted_frame;
2646 drag_info.first_move = false;
2650 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2652 if (drag_info.first_move) return;
2654 tempo_marker_drag_motion_callback (drag_info.item, event);
2656 TempoMarker* marker = (TempoMarker *) drag_info.data;
2659 TempoMap& map (session->tempo_map());
2660 map.bbt_time (drag_info.last_pointer_frame, when);
2662 if (drag_info.copy == true) {
2663 begin_reversible_command (_("copy tempo mark"));
2664 XMLNode &before = map.get_state();
2665 map.add_tempo (marker->tempo(), when);
2666 XMLNode &after = map.get_state();
2667 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2668 commit_reversible_command ();
2670 // delete the dummy marker we used for visual representation of copying.
2671 // a new visual marker will show up automatically.
2674 begin_reversible_command (_("move tempo mark"));
2675 XMLNode &before = map.get_state();
2676 map.move_tempo (marker->tempo(), when);
2677 XMLNode &after = map.get_state();
2678 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2679 commit_reversible_command ();
2684 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2686 ControlPoint* control_point;
2688 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2689 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2693 // We shouldn't remove the first or last gain point
2694 if (control_point->line.is_last_point(*control_point) ||
2695 control_point->line.is_first_point(*control_point)) {
2699 control_point->line.remove_point (*control_point);
2703 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2705 ControlPoint* control_point;
2707 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2708 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2712 control_point->line.remove_point (*control_point);
2716 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2718 ControlPoint* control_point;
2720 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2721 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2725 drag_info.item = item;
2726 drag_info.data = control_point;
2727 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2728 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2730 start_grab (event, fader_cursor);
2732 // start the grab at the center of the control point so
2733 // the point doesn't 'jump' to the mouse after the first drag
2734 drag_info.grab_x = control_point->get_x();
2735 drag_info.grab_y = control_point->get_y();
2736 control_point->line.parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2737 track_canvas->w2c(drag_info.grab_x, drag_info.grab_y,
2738 drag_info.grab_x, drag_info.grab_y);
2740 drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2742 control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2744 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2745 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2746 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2748 show_verbose_canvas_cursor ();
2752 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2754 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2756 double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2757 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2759 if (event->button.state & Keyboard::SecondaryModifier) {
2764 double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2765 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2767 // calculate zero crossing point. back off by .01 to stay on the
2768 // positive side of zero
2770 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line.height() - .01;
2771 cp->line.parent_group().i2w(_unused, zero_gain_y);
2773 // make sure we hit zero when passing through
2774 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2775 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2779 if (drag_info.x_constrained) {
2780 cx = drag_info.grab_x;
2782 if (drag_info.y_constrained) {
2783 cy = drag_info.grab_y;
2786 drag_info.cumulative_x_drag = cx - drag_info.grab_x;
2787 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2789 cp->line.parent_group().w2i (cx, cy);
2793 cy = min ((double) cp->line.height(), cy);
2795 //translate cx to frames
2796 nframes_t cx_frames = unit_to_frame (cx);
2798 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2799 snap_to (cx_frames);
2802 float fraction = 1.0 - (cy / cp->line.height());
2806 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2812 cp->line.point_drag (*cp, cx_frames , fraction, push);
2814 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2816 drag_info.first_move = false;
2820 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2822 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2824 if (drag_info.first_move) {
2828 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2829 reset_point_selection ();
2833 control_point_drag_motion_callback (item, event);
2835 cp->line.end_drag (cp);
2839 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2841 switch (mouse_mode) {
2843 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2844 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2852 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2856 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2857 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2861 start_line_grab (al, event);
2865 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2869 nframes_t frame_within_region;
2871 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2875 cx = event->button.x;
2876 cy = event->button.y;
2877 line->parent_group().w2i (cx, cy);
2878 frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2880 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2881 current_line_drag_info.after)) {
2882 /* no adjacent points */
2886 drag_info.item = &line->grab_item();
2887 drag_info.data = line;
2888 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2889 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2891 start_grab (event, fader_cursor);
2893 double fraction = 1.0 - (cy / line->height());
2895 line->start_drag (0, drag_info.grab_frame, fraction);
2897 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2898 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2899 show_verbose_canvas_cursor ();
2903 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2905 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2907 double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2909 if (event->button.state & Keyboard::SecondaryModifier) {
2913 double cx = drag_info.current_pointer_x;
2914 double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2916 // calculate zero crossing point. back off by .01 to stay on the
2917 // positive side of zero
2919 double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
2920 line->parent_group().i2w(_unused, zero_gain_y);
2922 // make sure we hit zero when passing through
2923 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2924 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2928 drag_info.cumulative_y_drag = cy - drag_info.grab_y;
2930 line->parent_group().w2i (cx, cy);
2933 cy = min ((double) line->height(), cy);
2936 fraction = 1.0 - (cy / line->height());
2940 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2946 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2948 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2952 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2954 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2955 line_drag_motion_callback (item, event);
2960 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2962 if (selection->regions.empty() || clicked_regionview == 0) {
2966 drag_info.copy = false;
2967 drag_info.item = item;
2968 drag_info.data = clicked_regionview;
2970 if (Config->get_edit_mode() == Splice) {
2971 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
2972 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
2974 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2975 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2981 TimeAxisView* tvp = clicked_trackview;
2982 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2984 if (tv && tv->is_audio_track()) {
2985 speed = tv->get_diskstream()->speed();
2988 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2989 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2990 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
2991 drag_info.dest_trackview = drag_info.source_trackview;
2992 // we want a move threshold
2993 drag_info.want_move_threshold = true;
2995 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2997 begin_reversible_command (_("move region(s)"));
3001 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3003 if (selection->regions.empty() || clicked_regionview == 0) {
3007 drag_info.copy = true;
3008 drag_info.item = item;
3009 drag_info.data = clicked_regionview;
3013 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3014 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
3017 if (atv && atv->is_audio_track()) {
3018 speed = atv->get_diskstream()->speed();
3021 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3022 drag_info.dest_trackview = drag_info.source_trackview;
3023 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
3024 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3025 // we want a move threshold
3026 drag_info.want_move_threshold = true;
3027 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3028 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3029 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3033 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3035 if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3039 drag_info.copy = false;
3040 drag_info.item = item;
3041 drag_info.data = clicked_regionview;
3042 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3043 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3048 TimeAxisView* tvp = clicked_trackview;
3049 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3051 if (tv && tv->is_audio_track()) {
3052 speed = tv->get_diskstream()->speed();
3055 drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
3056 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3057 drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3058 drag_info.dest_trackview = drag_info.source_trackview;
3059 // we want a move threshold
3060 drag_info.want_move_threshold = true;
3061 drag_info.brushing = true;
3063 begin_reversible_command (_("Drag region brush"));
3067 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3069 if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3071 drag_info.want_move_threshold = false; // don't copy again
3073 /* duplicate the regionview(s) and region(s) */
3075 vector<RegionView*> new_regionviews;
3077 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3080 AudioRegionView* arv;
3085 if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
3086 /* XXX handle MIDI here */
3090 const boost::shared_ptr<const Region> original = arv->region();
3091 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3092 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (region_copy);
3094 nrv = new AudioRegionView (*arv, ar);
3095 nrv->get_canvas_group()->show ();
3097 new_regionviews.push_back (nrv);
3100 if (new_regionviews.empty()) {
3104 /* reset selection to new regionviews. This will not set selection visual status for
3105 these regionviews since they don't belong to a track, so do that by hand too.
3108 selection->set (new_regionviews);
3110 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3111 (*i)->set_selected (true);
3114 /* reset drag_info data to reflect the fact that we are dragging the copies */
3116 drag_info.data = new_regionviews.front();
3118 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3123 Editor::check_region_drag_possible (AudioTimeAxisView** tv)
3125 /* Which trackview is this ? */
3127 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3128 (*tv) = dynamic_cast<AudioTimeAxisView*>(tvp);
3130 /* The region motion is only processed if the pointer is over
3134 if (!(*tv) || !(*tv)->is_audio_track()) {
3135 /* To make sure we hide the verbose canvas cursor when the mouse is
3136 not held over and audiotrack.
3138 hide_verbose_canvas_cursor ();
3145 struct RegionSelectionByPosition {
3146 bool operator() (RegionView*a, RegionView* b) {
3147 return a->region()->position () < b->region()->position();
3152 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3154 AudioTimeAxisView* tv;
3156 if (!check_region_drag_possible (&tv)) {
3160 if (!drag_info.move_threshold_passed) {
3166 if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3172 RegionSelection copy (selection->regions);
3174 RegionSelectionByPosition cmp;
3177 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3179 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(*i)->get_time_axis_view());
3185 boost::shared_ptr<Playlist> playlist;
3187 if ((playlist = atv->playlist()) == 0) {
3191 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3196 if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3200 if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3206 playlist->shuffle ((*i)->region(), dir);
3208 drag_info.grab_x = drag_info.current_pointer_x;
3213 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3218 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3222 RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data);
3223 nframes_t pending_region_position = 0;
3224 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3225 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
3226 bool clamp_y_axis = false;
3227 vector<int32_t> height_list(512) ;
3228 vector<int32_t>::iterator j;
3229 AudioTimeAxisView* tv;
3231 possibly_copy_regions_during_grab (event);
3233 if (!check_region_drag_possible (&tv)) {
3237 original_pointer_order = drag_info.dest_trackview->order;
3239 /************************************************************
3241 ************************************************************/
3243 if (drag_info.brushing) {
3244 clamp_y_axis = true;
3249 if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3251 int32_t children = 0, numtracks = 0;
3252 // XXX hard coding track limit, oh my, so very very bad
3253 bitset <1024> tracks (0x00);
3254 /* get a bitmask representing the visible tracks */
3256 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3257 TimeAxisView *tracklist_timeview;
3258 tracklist_timeview = (*i);
3259 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
3260 list<TimeAxisView*> children_list;
3262 /* zeroes are audio tracks. ones are other types. */
3264 if (!atv2->hidden()) {
3266 if (visible_y_high < atv2->order) {
3267 visible_y_high = atv2->order;
3269 if (visible_y_low > atv2->order) {
3270 visible_y_low = atv2->order;
3273 if (!atv2->is_audio_track()) {
3274 tracks = tracks |= (0x01 << atv2->order);
3277 height_list[atv2->order] = (*i)->height;
3279 if ((children_list = atv2->get_child_list()).size() > 0) {
3280 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
3281 tracks = tracks |= (0x01 << (atv2->order + children));
3282 height_list[atv2->order + children] = (*j)->height;
3290 /* find the actual span according to the canvas */
3292 canvas_pointer_y_span = pointer_y_span;
3293 if (drag_info.dest_trackview->order >= tv->order) {
3295 for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3296 if (height_list[y] == 0 ) {
3297 canvas_pointer_y_span--;
3302 for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3303 if ( height_list[y] == 0 ) {
3304 canvas_pointer_y_span++;
3309 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3310 RegionView* rv2 = (*i);
3311 double ix1, ix2, iy1, iy2;
3314 if (rv2->region()->locked()) {
3318 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3319 rv2->get_canvas_group()->i2w (ix1, iy1);
3320 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3321 RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3323 if (atv2->order != original_pointer_order) {
3324 /* this isn't the pointer track */
3326 if (canvas_pointer_y_span > 0) {
3328 /* moving up the canvas */
3329 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
3331 int32_t visible_tracks = 0;
3332 while (visible_tracks < canvas_pointer_y_span ) {
3335 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3336 /* we're passing through a hidden track */
3341 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
3342 clamp_y_axis = true;
3346 clamp_y_axis = true;
3349 } else if (canvas_pointer_y_span < 0) {
3351 /*moving down the canvas*/
3353 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3356 int32_t visible_tracks = 0;
3358 while (visible_tracks > canvas_pointer_y_span ) {
3361 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
3365 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3366 clamp_y_axis = true;
3371 clamp_y_axis = true;
3377 /* this is the pointer's track */
3378 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
3379 clamp_y_axis = true;
3380 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3381 clamp_y_axis = true;
3389 } else if (drag_info.dest_trackview == tv) {
3390 clamp_y_axis = true;
3394 if (!clamp_y_axis) {
3395 drag_info.dest_trackview = tv;
3398 /************************************************************
3400 ************************************************************/
3402 /* compute the amount of pointer motion in frames, and where
3403 the region would be if we moved it by that much.
3406 if ( drag_info.move_threshold_passed ) {
3408 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3410 nframes_t sync_frame;
3411 nframes_t sync_offset;
3414 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3416 sync_offset = rv->region()->sync_offset (sync_dir);
3418 /* we don't handle a sync point that lies before zero.
3420 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3421 sync_frame = pending_region_position + (sync_dir*sync_offset);
3423 /* we snap if the snap modifier is not enabled.
3426 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3427 snap_to (sync_frame);
3430 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3433 pending_region_position = drag_info.last_frame_position;
3437 pending_region_position = 0;
3440 if (pending_region_position > max_frames - rv->region()->length()) {
3441 pending_region_position = drag_info.last_frame_position;
3444 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
3446 bool x_move_allowed;
3448 if (Config->get_edit_mode() == Lock) {
3449 if (drag_info.copy) {
3450 x_move_allowed = !drag_info.x_constrained;
3452 /* in locked edit mode, reverse the usual meaning of x_constrained */
3453 x_move_allowed = drag_info.x_constrained;
3456 x_move_allowed = !drag_info.x_constrained;
3459 if ( pending_region_position != drag_info.last_frame_position && x_move_allowed ) {
3461 /* now compute the canvas unit distance we need to move the regionview
3462 to make it appear at the new location.
3465 if (pending_region_position > drag_info.last_frame_position) {
3466 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3468 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3471 drag_info.last_frame_position = pending_region_position;
3478 /* threshold not passed */
3483 /*************************************************************
3485 ************************************************************/
3487 if (x_delta == 0 && (pointer_y_span == 0)) {
3488 /* haven't reached next snap point, and we're not switching
3489 trackviews. nothing to do.
3496 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3498 RegionView* rv2 = (*i);
3500 // If any regionview is at zero, we need to know so we can stop further leftward motion.
3502 double ix1, ix2, iy1, iy2;
3503 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3504 rv2->get_canvas_group()->i2w (ix1, iy1);
3513 /*************************************************************
3515 ************************************************************/
3519 if (drag_info.first_move) {
3520 if (drag_info.move_threshold_passed) {
3531 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3532 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3534 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3536 RegionView* rv = (*i);
3537 double ix1, ix2, iy1, iy2;
3538 int32_t temp_pointer_y_span = pointer_y_span;
3540 if (rv->region()->locked()) {
3544 /* get item BBox, which will be relative to parent. so we have
3545 to query on a child, then convert to world coordinates using
3549 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3550 rv->get_canvas_group()->i2w (ix1, iy1);
3551 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3552 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3553 AudioTimeAxisView* temp_atv;
3555 if ((pointer_y_span != 0) && !clamp_y_axis) {
3558 for (j = height_list.begin(); j!= height_list.end(); j++) {
3559 if (x == canvas_atv->order) {
3560 /* we found the track the region is on */
3561 if (x != original_pointer_order) {
3562 /*this isn't from the same track we're dragging from */
3563 temp_pointer_y_span = canvas_pointer_y_span;
3565 while (temp_pointer_y_span > 0) {
3566 /* we're moving up canvas-wise,
3567 so we need to find the next track height
3569 if (j != height_list.begin()) {
3572 if (x != original_pointer_order) {
3573 /* we're not from the dragged track, so ignore hidden tracks. */
3575 temp_pointer_y_span++;
3579 temp_pointer_y_span--;
3582 while (temp_pointer_y_span < 0) {
3584 if (x != original_pointer_order) {
3586 temp_pointer_y_span--;
3590 if (j != height_list.end()) {
3593 temp_pointer_y_span++;
3595 /* find out where we'll be when we move and set height accordingly */
3597 tvp2 = trackview_by_y_position (iy1 + y_delta);
3598 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3599 rv->set_height (temp_atv->height);
3601 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3602 personally, i think this can confuse things, but never mind.
3605 //const GdkColor& col (temp_atv->view->get_region_color());
3606 //rv->set_color (const_cast<GdkColor&>(col));
3614 /* prevent the regionview from being moved to before
3615 the zero position on the canvas.
3620 if (-x_delta > ix1) {
3623 } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3624 x_delta = max_frames - rv->region()->last_frame();
3628 if (drag_info.first_move) {
3630 /* hide any dependent views */
3632 rv->get_time_axis_view().hide_dependent_views (*rv);
3634 /* this is subtle. raising the regionview itself won't help,
3635 because raise_to_top() just puts the item on the top of
3636 its parent's stack. so, we need to put the trackview canvas_display group
3637 on the top, since its parent is the whole canvas.
3640 rv->get_canvas_group()->raise_to_top();
3641 rv->get_time_axis_view().canvas_display->raise_to_top();
3642 cursor_group->raise_to_top();
3643 rv->fake_set_opaque (true);
3646 if (drag_info.brushing) {
3647 mouse_brush_insert_region (rv, pending_region_position);
3649 rv->move (x_delta, y_delta);
3652 } /* foreach region */
3656 if (drag_info.first_move && drag_info.move_threshold_passed) {
3657 cursor_group->raise_to_top();
3658 drag_info.first_move = false;
3661 if (x_delta != 0 && !drag_info.brushing) {
3662 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3667 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3669 bool nocommit = true;
3670 vector<RegionView*> copies;
3671 RouteTimeAxisView* source_tv;
3672 boost::shared_ptr<Diskstream> ds;
3673 boost::shared_ptr<Playlist> from_playlist;
3674 vector<RegionView*> new_selection;
3676 /* first_move is set to false if the regionview has been moved in the
3680 if (drag_info.first_move) {
3687 /* The regionview has been moved at some stage during the grab so we need
3688 to account for any mouse movement between this event and the last one.
3691 region_drag_motion_callback (item, event);
3693 if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3694 selection->set (pre_drag_region_selection);
3695 pre_drag_region_selection.clear ();
3698 if (drag_info.brushing) {
3699 /* all changes were made during motion event handlers */
3701 if (drag_info.copy) {
3702 for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3703 copies.push_back (*i);
3712 if (drag_info.copy) {
3713 if (drag_info.x_constrained) {
3714 op_string = _("fixed time region copy");
3716 op_string = _("region copy");
3719 if (drag_info.x_constrained) {
3720 op_string = _("fixed time region drag");
3722 op_string = _("region drag");
3726 begin_reversible_command (op_string);
3728 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3730 RegionView* rv = (*i);
3731 double ix1, ix2, iy1, iy2;
3732 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3733 rv->get_canvas_group()->i2w (ix1, iy1);
3734 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
3735 AudioTimeAxisView* dest_atv = dynamic_cast<AudioTimeAxisView*>(dest_tv);
3737 bool changed_tracks;
3738 bool changed_position;
3741 if (rv->region()->locked()) {
3746 /* adjust for track speed */
3750 if (dest_atv && dest_atv->get_diskstream()) {
3751 speed = dest_atv->get_diskstream()->speed();
3754 changed_position = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3755 changed_tracks = (dest_tv != &rv->get_time_axis_view());
3757 if (changed_position && !drag_info.x_constrained) {
3758 where = (nframes_t) (unit_to_frame (ix1) * speed);
3760 where = rv->region()->position();
3763 /* undo the previous hide_dependent_views so that xfades don't
3764 disappear on copying regions
3767 rv->get_time_axis_view().reveal_dependent_views (*rv);
3769 boost::shared_ptr<Region> new_region;
3771 if (drag_info.copy) {
3772 /* we already made a copy */
3773 new_region = rv->region();
3775 new_region = RegionFactory::create (rv->region());
3778 if (changed_tracks || drag_info.copy) {
3780 boost::shared_ptr<Playlist> to_playlist = dest_atv->playlist();
3782 latest_regionviews.clear ();
3784 sigc::connection c = dest_atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3785 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3786 to_playlist->add_region (new_region, where);
3787 session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3790 if (!latest_regionviews.empty()) {
3791 // XXX why just the first one ? we only expect one
3792 dest_atv->reveal_dependent_views (*latest_regionviews.front());
3793 new_selection.push_back (latest_regionviews.front());
3798 /* just change the model */
3800 rv->region()->set_position (where, (void*) this);
3803 if (changed_tracks && !drag_info.copy) {
3805 /* get the playlist where this drag started. we can't use rv->region()->playlist()
3806 because we may have copied the region and it has not been attached to a playlist.
3809 assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
3810 assert ((ds = source_tv->get_diskstream()));
3811 assert ((from_playlist = ds->playlist()));
3813 /* moved to a different audio track, without copying */
3815 /* the region that used to be in the old playlist is not
3816 moved to the new one - we use a copy of it. as a result,
3817 any existing editor for the region should no longer be
3821 rv->hide_region_editor();
3822 rv->fake_set_opaque (false);
3824 /* remove the region from the old playlist */
3826 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
3827 from_playlist->remove_region ((rv->region()));
3828 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
3830 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3831 was selected in all of them, then removing it from a playlist will have removed all
3832 trace of it from the selection (i.e. there were N regions selected, we removed 1,
3833 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3834 corresponding regionview, and the selection is now empty).
3836 this could have invalidated any and all iterators into the region selection.
3838 the heuristic we use here is: if the region selection is empty, break out of the loop
3839 here. if the region selection is not empty, then restart the loop because we know that
3840 we must have removed at least the region(view) we've just been working on as well as any
3841 that we processed on previous iterations.
3843 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
3844 we can just iterate.
3847 if (selection->regions.empty()) {
3850 i = selection->regions.by_layer().begin();
3857 if (drag_info.copy) {
3858 copies.push_back (rv);
3862 if (new_selection.empty()) {
3863 if (drag_info.copy) {
3864 /* the region(view)s that are selected and being dragged around
3865 are copies and do not belong to any track. remove them
3866 from the selection right here.
3868 selection->clear_regions();
3871 /* this will clear any existing selection that would have been
3872 cleared in the other clause above
3874 selection->set (new_selection);
3879 commit_reversible_command ();
3882 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
3888 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3890 /* Either add to or set the set the region selection, unless
3891 this is an alignment click (control used)
3894 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
3895 TimeAxisView* tv = &rv.get_time_axis_view();
3896 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3898 if (atv && atv->is_audio_track()) {
3899 speed = atv->get_diskstream()->speed();
3902 nframes64_t where = get_preferred_edit_position();
3906 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
3908 align_region (rv.region(), SyncPoint, (nframes_t) (where * speed));
3910 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
3912 align_region (rv.region(), End, (nframes_t) (where * speed));
3916 align_region (rv.region(), Start, (nframes_t) (where * speed));
3923 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos)
3929 nframes_t frame_rate;
3938 if (Profile->get_sae() || Profile->get_small_screen()) {
3939 m = ARDOUR_UI::instance()->primary_clock.mode();
3941 m = ARDOUR_UI::instance()->secondary_clock.mode();
3945 case AudioClock::BBT:
3946 session->bbt_time (frame, bbt);
3947 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3950 case AudioClock::SMPTE:
3951 session->smpte_time (frame, smpte);
3952 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3955 case AudioClock::MinSec:
3956 /* XXX this is copied from show_verbose_duration_cursor() */
3957 frame_rate = session->frame_rate();
3958 hours = frame / (frame_rate * 3600);
3959 frame = frame % (frame_rate * 3600);
3960 mins = frame / (frame_rate * 60);
3961 frame = frame % (frame_rate * 60);
3962 secs = (float) frame / (float) frame_rate;
3963 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3967 snprintf (buf, sizeof(buf), "%u", frame);
3971 if (xpos >= 0 && ypos >=0) {
3972 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3975 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3977 show_verbose_canvas_cursor ();
3981 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos)
3988 nframes_t distance, frame_rate;
3990 Meter meter_at_start(session->tempo_map().meter_at(start));
3998 if (Profile->get_sae() || Profile->get_small_screen()) {
3999 m = ARDOUR_UI::instance()->primary_clock.mode ();
4001 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4005 case AudioClock::BBT:
4006 session->bbt_time (start, sbbt);
4007 session->bbt_time (end, ebbt);
4010 /* XXX this computation won't work well if the
4011 user makes a selection that spans any meter changes.
4014 ebbt.bars -= sbbt.bars;
4015 if (ebbt.beats >= sbbt.beats) {
4016 ebbt.beats -= sbbt.beats;
4019 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4021 if (ebbt.ticks >= sbbt.ticks) {
4022 ebbt.ticks -= sbbt.ticks;
4025 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4028 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4031 case AudioClock::SMPTE:
4032 session->smpte_duration (end - start, smpte);
4033 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4036 case AudioClock::MinSec:
4037 /* XXX this stuff should be elsewhere.. */
4038 distance = end - start;
4039 frame_rate = session->frame_rate();
4040 hours = distance / (frame_rate * 3600);
4041 distance = distance % (frame_rate * 3600);
4042 mins = distance / (frame_rate * 60);
4043 distance = distance % (frame_rate * 60);
4044 secs = (float) distance / (float) frame_rate;
4045 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4049 snprintf (buf, sizeof(buf), "%u", end - start);
4053 if (xpos >= 0 && ypos >=0) {
4054 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4057 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4060 show_verbose_canvas_cursor ();
4064 Editor::collect_new_region_view (RegionView* rv)
4066 latest_regionviews.push_back (rv);
4070 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4072 if (clicked_regionview == 0) {
4076 /* lets try to create new Region for the selection */
4078 vector<boost::shared_ptr<AudioRegion> > new_regions;
4079 create_region_from_selection (new_regions);
4081 if (new_regions.empty()) {
4085 /* XXX fix me one day to use all new regions */
4087 boost::shared_ptr<Region> region (new_regions.front());
4089 /* add it to the current stream/playlist.
4091 tricky: the streamview for the track will add a new regionview. we will
4092 catch the signal it sends when it creates the regionview to
4093 set the regionview we want to then drag.
4096 latest_regionviews.clear();
4097 sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4099 /* A selection grab currently creates two undo/redo operations, one for
4100 creating the new region and another for moving it.
4103 begin_reversible_command (_("selection grab"));
4105 boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
4107 XMLNode *before = &(playlist->get_state());
4108 clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
4109 XMLNode *after = &(playlist->get_state());
4110 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4112 commit_reversible_command ();
4116 if (latest_regionviews.empty()) {
4117 /* something went wrong */
4121 /* we need to deselect all other regionviews, and select this one
4122 i'm ignoring undo stuff, because the region creation will take care of it
4124 selection->set (latest_regionviews);
4126 drag_info.item = latest_regionviews.front()->get_canvas_group();
4127 drag_info.data = latest_regionviews.front();
4128 drag_info.motion_callback = &Editor::region_drag_motion_callback;
4129 drag_info.finished_callback = &Editor::region_drag_finished_callback;
4133 drag_info.source_trackview = clicked_trackview;
4134 drag_info.dest_trackview = drag_info.source_trackview;
4135 drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4136 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4138 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4142 Editor::cancel_selection ()
4144 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4145 (*i)->hide_selection ();
4147 selection->clear ();
4148 clicked_selection = 0;
4152 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4154 nframes_t start = 0;
4161 drag_info.item = item;
4162 drag_info.motion_callback = &Editor::drag_selection;
4163 drag_info.finished_callback = &Editor::end_selection_op;
4168 case CreateSelection:
4169 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4170 drag_info.copy = true;
4172 drag_info.copy = false;
4174 start_grab (event, selector_cursor);
4177 case SelectionStartTrim:
4178 if (clicked_trackview) {
4179 clicked_trackview->order_selection_trims (item, true);
4181 start_grab (event, trimmer_cursor);
4182 start = selection->time[clicked_selection].start;
4183 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4186 case SelectionEndTrim:
4187 if (clicked_trackview) {
4188 clicked_trackview->order_selection_trims (item, false);
4190 start_grab (event, trimmer_cursor);
4191 end = selection->time[clicked_selection].end;
4192 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
4196 start = selection->time[clicked_selection].start;
4198 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
4202 if (selection_op == SelectionMove) {
4203 show_verbose_time_cursor(start, 10);
4205 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4210 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4212 nframes_t start = 0;
4215 nframes_t pending_position;
4217 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4218 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4220 pending_position = 0;
4223 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4224 snap_to (pending_position);
4227 /* only alter selection if the current frame is
4228 different from the last frame position (adjusted)
4231 if (pending_position == drag_info.last_pointer_frame) return;
4233 switch (selection_op) {
4234 case CreateSelection:
4236 if (drag_info.first_move) {
4237 snap_to (drag_info.grab_frame);
4240 if (pending_position < drag_info.grab_frame) {
4241 start = pending_position;
4242 end = drag_info.grab_frame;
4244 end = pending_position;
4245 start = drag_info.grab_frame;
4248 /* first drag: Either add to the selection
4249 or create a new selection->
4252 if (drag_info.first_move) {
4254 begin_reversible_command (_("range selection"));
4256 if (drag_info.copy) {
4257 /* adding to the selection */
4258 clicked_selection = selection->add (start, end);
4259 drag_info.copy = false;
4261 /* new selection-> */
4262 clicked_selection = selection->set (clicked_trackview, start, end);
4267 case SelectionStartTrim:
4269 if (drag_info.first_move) {
4270 begin_reversible_command (_("trim selection start"));
4273 start = selection->time[clicked_selection].start;
4274 end = selection->time[clicked_selection].end;
4276 if (pending_position > end) {
4279 start = pending_position;
4283 case SelectionEndTrim:
4285 if (drag_info.first_move) {
4286 begin_reversible_command (_("trim selection end"));
4289 start = selection->time[clicked_selection].start;
4290 end = selection->time[clicked_selection].end;
4292 if (pending_position < start) {
4295 end = pending_position;
4302 if (drag_info.first_move) {
4303 begin_reversible_command (_("move selection"));
4306 start = selection->time[clicked_selection].start;
4307 end = selection->time[clicked_selection].end;
4309 length = end - start;
4311 start = pending_position;
4314 end = start + length;
4319 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4320 start_canvas_autoscroll (1, 0);
4324 selection->replace (clicked_selection, start, end);
4327 drag_info.last_pointer_frame = pending_position;
4328 drag_info.first_move = false;
4330 if (selection_op == SelectionMove) {
4331 show_verbose_time_cursor(start, 10);
4333 show_verbose_time_cursor(pending_position, 10);
4338 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4340 if (!drag_info.first_move) {
4341 drag_selection (item, event);
4342 /* XXX this is not object-oriented programming at all. ick */
4343 if (selection->time.consolidate()) {
4344 selection->TimeChanged ();
4346 commit_reversible_command ();
4348 /* just a click, no pointer movement.*/
4350 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4352 selection->clear_time();
4357 /* XXX what happens if its a music selection? */
4358 session->set_audio_range (selection->time);
4359 stop_canvas_autoscroll ();
4363 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4366 TimeAxisView* tvp = clicked_trackview;
4367 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4369 if (tv && tv->is_audio_track()) {
4370 speed = tv->get_diskstream()->speed();
4373 nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
4374 nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
4375 nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
4377 //drag_info.item = clicked_regionview->get_name_highlight();
4378 drag_info.item = item;
4379 drag_info.motion_callback = &Editor::trim_motion_callback;
4380 drag_info.finished_callback = &Editor::trim_finished_callback;
4382 start_grab (event, trimmer_cursor);
4384 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4385 trim_op = ContentsTrim;
4387 /* These will get overridden for a point trim.*/
4388 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4389 /* closer to start */
4390 trim_op = StartTrim;
4391 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4399 show_verbose_time_cursor(region_start, 10);
4402 show_verbose_time_cursor(region_end, 10);
4405 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4411 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4413 RegionView* rv = clicked_regionview;
4414 nframes_t frame_delta = 0;
4415 bool left_direction;
4416 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4418 /* snap modifier works differently here..
4419 its' current state has to be passed to the
4420 various trim functions in order to work properly
4424 TimeAxisView* tvp = clicked_trackview;
4425 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4426 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4428 if (tv && tv->is_audio_track()) {
4429 speed = tv->get_diskstream()->speed();
4432 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4433 left_direction = true;
4435 left_direction = false;
4439 snap_to (drag_info.current_pointer_frame);
4442 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4446 if (drag_info.first_move) {
4452 trim_type = "Region start trim";
4455 trim_type = "Region end trim";
4458 trim_type = "Region content trim";
4462 begin_reversible_command (trim_type);
4464 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4465 (*i)->fake_set_opaque(false);
4466 (*i)->region()->freeze ();
4468 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4470 arv->temporarily_hide_envelope ();
4472 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4473 insert_result = motion_frozen_playlists.insert (pl);
4474 if (insert_result.second) {
4475 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4480 if (left_direction) {
4481 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4483 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4488 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4491 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4492 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4498 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4501 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4502 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4509 bool swap_direction = false;
4511 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4512 swap_direction = true;
4515 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4516 i != selection->regions.by_layer().end(); ++i)
4518 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4526 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);
4529 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);
4532 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4536 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4537 drag_info.first_move = false;
4541 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4543 boost::shared_ptr<Region> region (rv.region());
4545 if (region->locked()) {
4549 nframes_t new_bound;
4552 TimeAxisView* tvp = clicked_trackview;
4553 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4555 if (tv && tv->is_audio_track()) {
4556 speed = tv->get_diskstream()->speed();
4559 if (left_direction) {
4560 if (swap_direction) {
4561 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4563 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4566 if (swap_direction) {
4567 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4569 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4574 snap_to (new_bound);
4576 region->trim_start ((nframes_t) (new_bound * speed), this);
4577 rv.region_changed (StartChanged);
4581 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4583 boost::shared_ptr<Region> region (rv.region());
4585 if (region->locked()) {
4589 nframes_t new_bound;
4592 TimeAxisView* tvp = clicked_trackview;
4593 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4595 if (tv && tv->is_audio_track()) {
4596 speed = tv->get_diskstream()->speed();
4599 if (left_direction) {
4600 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4602 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4606 snap_to (new_bound, (left_direction ? 0 : 1));
4609 region->trim_front ((nframes_t) (new_bound * speed), this);
4611 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4615 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4617 boost::shared_ptr<Region> region (rv.region());
4619 if (region->locked()) {
4623 nframes_t new_bound;
4626 TimeAxisView* tvp = clicked_trackview;
4627 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4629 if (tv && tv->is_audio_track()) {
4630 speed = tv->get_diskstream()->speed();
4633 if (left_direction) {
4634 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4636 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4640 snap_to (new_bound);
4642 region->trim_end ((nframes_t) (new_bound * speed), this);
4643 rv.region_changed (LengthChanged);
4647 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4649 if (!drag_info.first_move) {
4650 trim_motion_callback (item, event);
4652 if (!selection->selected (clicked_regionview)) {
4653 thaw_region_after_trim (*clicked_regionview);
4656 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4657 i != selection->regions.by_layer().end(); ++i)
4659 thaw_region_after_trim (**i);
4660 (*i)->fake_set_opaque (true);
4664 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4666 session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4669 motion_frozen_playlists.clear ();
4671 commit_reversible_command();
4673 /* no mouse movement */
4679 Editor::point_trim (GdkEvent* event)
4681 RegionView* rv = clicked_regionview;
4682 nframes_t new_bound = drag_info.current_pointer_frame;
4684 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4685 snap_to (new_bound);
4688 /* Choose action dependant on which button was pressed */
4689 switch (event->button.button) {
4691 trim_op = StartTrim;
4692 begin_reversible_command (_("Start point trim"));
4694 if (selection->selected (rv)) {
4696 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4697 i != selection->regions.by_layer().end(); ++i)
4699 if (!(*i)->region()->locked()) {
4700 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4701 XMLNode &before = pl->get_state();
4702 (*i)->region()->trim_front (new_bound, this);
4703 XMLNode &after = pl->get_state();
4704 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4710 if (!rv->region()->locked()) {
4711 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4712 XMLNode &before = pl->get_state();
4713 rv->region()->trim_front (new_bound, this);
4714 XMLNode &after = pl->get_state();
4715 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4719 commit_reversible_command();
4724 begin_reversible_command (_("End point trim"));
4726 if (selection->selected (rv)) {
4728 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4730 if (!(*i)->region()->locked()) {
4731 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4732 XMLNode &before = pl->get_state();
4733 (*i)->region()->trim_end (new_bound, this);
4734 XMLNode &after = pl->get_state();
4735 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4741 if (!rv->region()->locked()) {
4742 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4743 XMLNode &before = pl->get_state();
4744 rv->region()->trim_end (new_bound, this);
4745 XMLNode &after = pl->get_state();
4746 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4750 commit_reversible_command();
4759 Editor::thaw_region_after_trim (RegionView& rv)
4761 boost::shared_ptr<Region> region (rv.region());
4763 if (region->locked()) {
4767 region->thaw (_("trimmed region"));
4768 XMLNode &after = region->playlist()->get_state();
4769 session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4771 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4773 arv->unhide_envelope ();
4777 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4782 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4783 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4787 Location* location = find_location_from_marker (marker, is_start);
4788 location->set_hidden (true, this);
4793 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4799 drag_info.item = item;
4800 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4801 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4803 range_marker_op = op;
4805 if (!temp_location) {
4806 temp_location = new Location;
4810 case CreateRangeMarker:
4811 case CreateTransportMarker:
4812 case CreateCDMarker:
4814 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4815 drag_info.copy = true;
4817 drag_info.copy = false;
4819 start_grab (event, selector_cursor);
4823 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4828 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4830 nframes_t start = 0;
4832 ArdourCanvas::SimpleRect *crect;
4834 switch (range_marker_op) {
4835 case CreateRangeMarker:
4836 crect = range_bar_drag_rect;
4838 case CreateTransportMarker:
4839 crect = transport_bar_drag_rect;
4841 case CreateCDMarker:
4842 crect = cd_marker_bar_drag_rect;
4845 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
4850 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4851 snap_to (drag_info.current_pointer_frame);
4854 /* only alter selection if the current frame is
4855 different from the last frame position.
4858 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4860 switch (range_marker_op) {
4861 case CreateRangeMarker:
4862 case CreateTransportMarker:
4863 case CreateCDMarker:
4864 if (drag_info.first_move) {
4865 snap_to (drag_info.grab_frame);
4868 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4869 start = drag_info.current_pointer_frame;
4870 end = drag_info.grab_frame;
4872 end = drag_info.current_pointer_frame;
4873 start = drag_info.grab_frame;
4876 /* first drag: Either add to the selection
4877 or create a new selection.
4880 if (drag_info.first_move) {
4882 temp_location->set (start, end);
4886 update_marker_drag_item (temp_location);
4887 range_marker_drag_rect->show();
4888 range_marker_drag_rect->raise_to_top();
4894 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4895 start_canvas_autoscroll (1, 0);
4899 temp_location->set (start, end);
4901 double x1 = frame_to_pixel (start);
4902 double x2 = frame_to_pixel (end);
4903 crect->property_x1() = x1;
4904 crect->property_x2() = x2;
4906 update_marker_drag_item (temp_location);
4909 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4910 drag_info.first_move = false;
4912 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4917 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4919 Location * newloc = 0;
4923 if (!drag_info.first_move) {
4924 drag_range_markerbar_op (item, event);
4926 switch (range_marker_op) {
4927 case CreateRangeMarker:
4928 case CreateCDMarker:
4930 begin_reversible_command (_("new range marker"));
4931 XMLNode &before = session->locations()->get_state();
4932 session->locations()->next_available_name(rangename,"unnamed");
4933 if (range_marker_op == CreateCDMarker) {
4934 flags = Location::IsRangeMarker|Location::IsCDMarker;
4935 cd_marker_bar_drag_rect->hide();
4938 flags = Location::IsRangeMarker;
4939 range_bar_drag_rect->hide();
4941 newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
4942 session->locations()->add (newloc, true);
4943 XMLNode &after = session->locations()->get_state();
4944 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4945 commit_reversible_command ();
4947 range_marker_drag_rect->hide();
4951 case CreateTransportMarker:
4952 // popup menu to pick loop or punch
4953 new_transport_marker_context_menu (&event->button, item);
4958 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4960 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
4965 start = session->locations()->first_mark_before (drag_info.grab_frame);
4966 end = session->locations()->first_mark_after (drag_info.grab_frame);
4968 if (end == max_frames) {
4969 end = session->current_end_frame ();
4973 start = session->current_start_frame ();
4976 switch (mouse_mode) {
4978 /* find the two markers on either side and then make the selection from it */
4979 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4983 /* find the two markers on either side of the click and make the range out of it */
4984 selection->set (0, start, end);
4993 stop_canvas_autoscroll ();
4999 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5001 drag_info.item = item;
5002 drag_info.motion_callback = &Editor::drag_mouse_zoom;
5003 drag_info.finished_callback = &Editor::end_mouse_zoom;
5005 start_grab (event, zoom_cursor);
5007 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5011 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5016 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5017 snap_to (drag_info.current_pointer_frame);
5019 if (drag_info.first_move) {
5020 snap_to (drag_info.grab_frame);
5024 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5026 /* base start and end on initial click position */
5027 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5028 start = drag_info.current_pointer_frame;
5029 end = drag_info.grab_frame;
5031 end = drag_info.current_pointer_frame;
5032 start = drag_info.grab_frame;
5037 if (drag_info.first_move) {
5039 zoom_rect->raise_to_top();
5042 reposition_zoom_rect(start, end);
5044 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5045 drag_info.first_move = false;
5047 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5052 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5054 if (!drag_info.first_move) {
5055 drag_mouse_zoom (item, event);
5057 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5058 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5060 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5063 temporal_zoom_to_frame (false, drag_info.grab_frame);
5065 temporal_zoom_step (false);
5066 center_screen (drag_info.grab_frame);
5074 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
5076 double x1 = frame_to_pixel (start);
5077 double x2 = frame_to_pixel (end);
5078 double y2 = full_canvas_height - 1.0;
5080 zoom_rect->property_x1() = x1;
5081 zoom_rect->property_y1() = 1.0;
5082 zoom_rect->property_x2() = x2;
5083 zoom_rect->property_y2() = y2;
5087 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5089 drag_info.item = item;
5090 drag_info.motion_callback = &Editor::drag_rubberband_select;
5091 drag_info.finished_callback = &Editor::end_rubberband_select;
5093 start_grab (event, cross_hair_cursor);
5095 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5099 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5106 /* use a bigger drag threshold than the default */
5108 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5112 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5113 if (drag_info.first_move) {
5114 snap_to (drag_info.grab_frame);
5116 snap_to (drag_info.current_pointer_frame);
5119 /* base start and end on initial click position */
5121 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5122 start = drag_info.current_pointer_frame;
5123 end = drag_info.grab_frame;
5125 end = drag_info.current_pointer_frame;
5126 start = drag_info.grab_frame;
5129 if (drag_info.current_pointer_y < drag_info.grab_y) {
5130 y1 = drag_info.current_pointer_y;
5131 y2 = drag_info.grab_y;
5133 y2 = drag_info.current_pointer_y;
5134 y1 = drag_info.grab_y;
5138 if (start != end || y1 != y2) {
5140 double x1 = frame_to_pixel (start);
5141 double x2 = frame_to_pixel (end);
5143 rubberband_rect->property_x1() = x1;
5144 rubberband_rect->property_y1() = y1;
5145 rubberband_rect->property_x2() = x2;
5146 rubberband_rect->property_y2() = y2;
5148 rubberband_rect->show();
5149 rubberband_rect->raise_to_top();
5151 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5152 drag_info.first_move = false;
5154 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5159 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5161 if (!drag_info.first_move) {
5163 drag_rubberband_select (item, event);
5166 if (drag_info.current_pointer_y < drag_info.grab_y) {
5167 y1 = drag_info.current_pointer_y;
5168 y2 = drag_info.grab_y;
5171 y2 = drag_info.current_pointer_y;
5172 y1 = drag_info.grab_y;
5176 Selection::Operation op = Keyboard::selection_type (event->button.state);
5179 begin_reversible_command (_("rubberband selection"));
5181 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5182 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5184 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5188 commit_reversible_command ();
5192 selection->clear_tracks();
5193 selection->clear_regions();
5194 selection->clear_points ();
5195 selection->clear_lines ();
5198 rubberband_rect->hide();
5203 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5205 using namespace Gtkmm2ext;
5207 ArdourPrompter prompter (false);
5209 prompter.set_prompt (_("Name for region:"));
5210 prompter.set_initial_text (clicked_regionview->region()->name());
5211 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5212 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5213 prompter.show_all ();
5214 switch (prompter.run ()) {
5215 case Gtk::RESPONSE_ACCEPT:
5217 prompter.get_result(str);
5219 clicked_regionview->region()->set_name (str);
5227 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5229 drag_info.item = item;
5230 drag_info.motion_callback = &Editor::time_fx_motion;
5231 drag_info.finished_callback = &Editor::end_time_fx;
5235 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5239 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5241 RegionView* rv = clicked_regionview;
5243 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5244 snap_to (drag_info.current_pointer_frame);
5247 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5251 if (drag_info.current_pointer_frame > rv->region()->position()) {
5252 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5255 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5256 drag_info.first_move = false;
5258 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5262 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5264 clicked_regionview->get_time_axis_view().hide_timestretch ();
5266 if (drag_info.first_move) {
5270 if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5271 /* backwards drag of the left edge - not usable */
5275 nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5276 #ifdef USE_RUBBERBAND
5277 float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
5279 float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5282 begin_reversible_command (_("timestretch"));
5284 // XXX how do timeFX on multiple regions ?
5287 rs.add (clicked_regionview);
5289 if (time_stretch (rs, percentage) == 0) {
5290 session->commit_reversible_command ();
5295 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
5297 /* no brushing without a useful snap setting */
5300 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
5303 switch (snap_mode) {
5305 return; /* can't work because it allows region to be placed anywhere */
5310 switch (snap_type) {
5318 /* don't brush a copy over the original */
5320 if (pos == rv->region()->position()) {
5324 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
5326 if (atv == 0 || !atv->is_audio_track()) {
5330 boost::shared_ptr<Playlist> playlist = atv->playlist();
5331 double speed = atv->get_diskstream()->speed();
5333 XMLNode &before = playlist->get_state();
5334 playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
5335 XMLNode &after = playlist->get_state();
5336 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5338 // playlist is frozen, so we have to update manually
5340 playlist->Modified(); /* EMIT SIGNAL */
5344 Editor::track_height_step_timeout ()
5347 struct timeval delta;
5349 gettimeofday (&now, 0);
5350 timersub (&now, &last_track_height_step_timestamp, &delta);
5352 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
5353 current_stepping_trackview = 0;