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.
28 #include <pbd/error.h>
29 #include <gtkmm2ext/utils.h>
31 #include "ardour_ui.h"
33 #include "time_axis_view.h"
34 #include "audio_time_axis.h"
35 #include "regionview.h"
37 #include "streamview.h"
38 #include "region_gain_line.h"
39 #include "automation_time_axis.h"
42 #include "selection.h"
45 #include "rgb_macros.h"
47 #include <ardour/types.h>
48 #include <ardour/route.h>
49 #include <ardour/audio_track.h>
50 #include <ardour/diskstream.h>
51 #include <ardour/playlist.h>
52 #include <ardour/audioplaylist.h>
53 #include <ardour/audioregion.h>
54 #include <ardour/dB.h>
55 #include <ardour/utils.h>
56 #include <ardour/region_factory.h>
63 using namespace ARDOUR;
66 using namespace Editing;
69 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
83 switch (event->type) {
84 case GDK_BUTTON_RELEASE:
85 case GDK_BUTTON_PRESS:
86 case GDK_2BUTTON_PRESS:
87 case GDK_3BUTTON_PRESS:
88 track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
90 case GDK_MOTION_NOTIFY:
91 track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
93 case GDK_ENTER_NOTIFY:
94 case GDK_LEAVE_NOTIFY:
95 track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
98 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
102 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
103 position is negative (as can be the case with motion events in particular),
104 the frame location is always positive.
107 return pixel_to_frame (*pcx);
111 Editor::mouse_mode_toggled (MouseMode m)
113 if (ignore_mouse_mode_toggle) {
119 if (mouse_select_button.get_active()) {
125 if (mouse_move_button.get_active()) {
131 if (mouse_gain_button.get_active()) {
137 if (mouse_zoom_button.get_active()) {
143 if (mouse_timefx_button.get_active()) {
149 if (mouse_audition_button.get_active()) {
160 Editor::set_mouse_mode (MouseMode m, bool force)
162 if (drag_info.item) {
166 if (m == mouse_mode && !force) {
174 if (mouse_mode != MouseRange) {
176 /* in all modes except range, hide the range selection,
177 show the object (region) selection.
180 for (AudioRegionSelection::iterator i = selection->audio_regions.begin(); i != selection->audio_regions.end(); ++i) {
181 (*i)->set_should_show_selection (true);
183 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
184 (*i)->hide_selection ();
189 /* in range mode, hide object (region) selection, and show the
193 for (AudioRegionSelection::iterator i = selection->audio_regions.begin(); i != selection->audio_regions.end(); ++i) {
194 (*i)->set_should_show_selection (false);
196 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
197 if ((*i)->selected()) {
198 (*i)->show_selection (selection->time);
203 /* XXX the hack of unsetting all other buttongs should go
204 away once GTK2 allows us to use regular radio buttons drawn like
205 normal buttons, rather than my silly GroupedButton hack.
208 ignore_mouse_mode_toggle = true;
210 switch (mouse_mode) {
212 mouse_select_button.set_active (true);
213 current_canvas_cursor = selector_cursor;
217 mouse_move_button.set_active (true);
218 current_canvas_cursor = grabber_cursor;
222 mouse_gain_button.set_active (true);
223 current_canvas_cursor = cross_hair_cursor;
227 mouse_zoom_button.set_active (true);
228 current_canvas_cursor = zoom_cursor;
232 mouse_timefx_button.set_active (true);
233 current_canvas_cursor = time_fx_cursor; // just use playhead
237 mouse_audition_button.set_active (true);
238 current_canvas_cursor = speaker_cursor;
242 ignore_mouse_mode_toggle = false;
245 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
250 Editor::step_mouse_mode (bool next)
252 switch (current_mouse_mode()) {
254 if (next) set_mouse_mode (MouseRange);
255 else set_mouse_mode (MouseTimeFX);
259 if (next) set_mouse_mode (MouseZoom);
260 else set_mouse_mode (MouseObject);
264 if (next) set_mouse_mode (MouseGain);
265 else set_mouse_mode (MouseRange);
269 if (next) set_mouse_mode (MouseTimeFX);
270 else set_mouse_mode (MouseZoom);
274 if (next) set_mouse_mode (MouseAudition);
275 else set_mouse_mode (MouseGain);
279 if (next) set_mouse_mode (MouseObject);
280 else set_mouse_mode (MouseTimeFX);
286 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
288 jack_nframes_t where = event_frame (event, 0, 0);
290 track_canvas.grab_focus();
292 if (session && session->actively_recording()) {
296 /* in object/audition/timefx mode, any button press sets
297 the selection if the object can be selected. this is a
298 bit of hack, because we want to avoid this if the
299 mouse operation is a region alignment.
302 if (((mouse_mode == MouseObject) ||
303 (mouse_mode == MouseAudition && item_type == RegionItem) ||
304 (mouse_mode == MouseTimeFX && item_type == RegionItem)) &&
305 event->type == GDK_BUTTON_PRESS &&
306 event->button.button <= 3) {
311 /* not dbl-click or triple-click */
315 set_selected_regionview_from_click (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift), true);
318 case AudioRegionViewNameHighlight:
319 case AudioRegionViewName:
320 if ((rv = reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))) != 0) {
321 set_selected_regionview_from_click (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift), true);
325 case GainAutomationControlPointItem:
326 case PanAutomationControlPointItem:
327 case RedirectAutomationControlPointItem:
328 if ((cp = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) != 0) {
329 set_selected_control_point_from_click (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift), true);
336 case AutomationTrackItem:
344 #define SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
345 #ifdef SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
346 /* in range mode, button 1/2/3 press potentially selects a track */
348 if (mouse_mode == MouseRange &&
349 event->type == GDK_BUTTON_PRESS &&
350 event->button.button <= 3) {
357 case AutomationTrackItem:
358 set_selected_track_from_click (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift), true, true);
361 case AudioRegionViewNameHighlight:
362 case AudioRegionViewName:
363 rv = reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"));
370 if (drag_info.item == 0 &&
371 (Keyboard::is_delete_event (&event->button) ||
372 Keyboard::is_context_menu_event (&event->button) ||
373 Keyboard::is_edit_event (&event->button))) {
375 /* handled by button release */
379 switch (event->button.button) {
382 if (event->type == GDK_BUTTON_PRESS) {
384 if (drag_info.item) {
385 drag_info.item->ungrab (event->button.time);
388 /* single mouse clicks on any of these item types operate
389 independent of mouse mode, mostly because they are
390 not on the main track canvas or because we want
396 case PlayheadCursorItem:
397 start_cursor_grab (item, event);
401 if (Keyboard::modifier_state_equals (event->button.state,
402 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
403 hide_marker (item, event);
405 start_marker_grab (item, event);
409 case TempoMarkerItem:
410 start_tempo_marker_grab (item, event);
413 case MeterMarkerItem:
414 start_meter_marker_grab (item, event);
423 case RangeMarkerBarItem:
424 start_range_markerbar_op (item, event, CreateRangeMarker);
427 case TransportMarkerBarItem:
428 start_range_markerbar_op (item, event, CreateTransportMarker);
437 switch (mouse_mode) {
440 case StartSelectionTrimItem:
441 start_selection_op (item, event, SelectionStartTrim);
444 case EndSelectionTrimItem:
445 start_selection_op (item, event, SelectionEndTrim);
449 if (Keyboard::modifier_state_contains
450 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
451 // contains and not equals because I can't use alt as a modifier alone.
452 start_selection_grab (item, event);
453 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
454 /* grab selection for moving */
455 start_selection_op (item, event, SelectionMove);
458 /* this was debated, but decided the more common action was to
459 make a new selection */
460 start_selection_op (item, event, CreateSelection);
465 start_selection_op (item, event, CreateSelection);
471 if (Keyboard::modifier_state_contains (event->button.state,
472 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt))
473 && event->type == GDK_BUTTON_PRESS) {
475 start_rubberband_select (item, event);
477 } else if (event->type == GDK_BUTTON_PRESS) {
480 case FadeInHandleItem:
481 start_fade_in_grab (item, event);
484 case FadeOutHandleItem:
485 start_fade_out_grab (item, event);
489 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
490 start_region_copy_grab (item, event);
491 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
492 start_region_brush_grab (item, event);
494 start_region_grab (item, event);
498 case AudioRegionViewNameHighlight:
499 start_trim (item, event);
503 case AudioRegionViewName:
504 /* rename happens on edit clicks */
505 start_trim (clicked_regionview->get_name_highlight(), event);
509 case GainAutomationControlPointItem:
510 case PanAutomationControlPointItem:
511 case RedirectAutomationControlPointItem:
512 start_control_point_grab (item, event);
516 case GainAutomationLineItem:
517 case PanAutomationLineItem:
518 case RedirectAutomationLineItem:
519 start_line_grab_from_line (item, event);
524 case AutomationTrackItem:
525 start_rubberband_select (item, event);
528 /* <CMT Additions> */
529 case ImageFrameHandleStartItem:
530 imageframe_start_handle_op(item, event) ;
533 case ImageFrameHandleEndItem:
534 imageframe_end_handle_op(item, event) ;
537 case MarkerViewHandleStartItem:
538 markerview_item_start_handle_op(item, event) ;
541 case MarkerViewHandleEndItem:
542 markerview_item_end_handle_op(item, event) ;
545 /* </CMT Additions> */
547 /* <CMT Additions> */
549 start_markerview_grab(item, event) ;
552 start_imageframe_grab(item, event) ;
554 /* </CMT Additions> */
566 // start_line_grab_from_regionview (item, event);
569 case GainControlPointItem:
570 start_control_point_grab (item, event);
574 start_line_grab_from_line (item, event);
577 case GainAutomationControlPointItem:
578 case PanAutomationControlPointItem:
579 case RedirectAutomationControlPointItem:
580 start_control_point_grab (item, event);
591 case GainAutomationControlPointItem:
592 case PanAutomationControlPointItem:
593 case RedirectAutomationControlPointItem:
594 start_control_point_grab (item, event);
597 case GainAutomationLineItem:
598 case PanAutomationLineItem:
599 case RedirectAutomationLineItem:
600 start_line_grab_from_line (item, event);
604 // XXX need automation mode to identify which
606 // start_line_grab_from_regionview (item, event);
616 if (event->type == GDK_BUTTON_PRESS) {
617 start_mouse_zoom (item, event);
624 if (item_type == RegionItem) {
625 start_time_fx (item, event);
630 /* handled in release */
639 switch (mouse_mode) {
641 if (event->type == GDK_BUTTON_PRESS) {
644 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
645 start_region_copy_grab (item, event);
647 start_region_grab (item, event);
651 case GainAutomationControlPointItem:
652 case PanAutomationControlPointItem:
653 case RedirectAutomationControlPointItem:
654 start_control_point_grab (item, event);
665 case AudioRegionViewNameHighlight:
666 start_trim (item, event);
670 case AudioRegionViewName:
671 start_trim (clicked_regionview->get_name_highlight(), event);
682 if (event->type == GDK_BUTTON_PRESS) {
683 /* relax till release */
690 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
691 temporal_zoom_session();
693 temporal_zoom_to_frame (true, event_frame(event));
708 switch (mouse_mode) {
710 //temporal_zoom_to_frame (true, where);
711 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
712 temporal_zoom_to_frame (true, where);
715 temporal_zoom_step (true);
720 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
721 scroll_backward (0.6f);
723 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
724 scroll_tracks_up_line ();
726 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
727 if (clicked_trackview) {
728 if (!current_stepping_trackview) {
729 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
730 current_stepping_trackview = clicked_trackview;
732 gettimeofday (&last_track_height_step_timestamp, 0);
733 current_stepping_trackview->step_height (true);
736 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
737 temporal_zoom_to_frame (true, where);
744 switch (mouse_mode) {
746 // temporal_zoom_to_frame (false, where);
747 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
748 temporal_zoom_to_frame (false, where);
751 temporal_zoom_step (false);
756 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
757 scroll_forward (0.6f);
759 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
760 scroll_tracks_down_line ();
762 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
763 if (clicked_trackview) {
764 if (!current_stepping_trackview) {
765 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
766 current_stepping_trackview = clicked_trackview;
768 gettimeofday (&last_track_height_step_timestamp, 0);
769 current_stepping_trackview->step_height (false);
771 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
772 temporal_zoom_to_frame (false, where);
786 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
788 jack_nframes_t where = event_frame (event, 0, 0);
790 /* no action if we're recording */
792 if (session && session->actively_recording()) {
796 /* first, see if we're finishing a drag ... */
798 if (drag_info.item) {
799 if (end_grab (item, event)) {
800 /* grab dragged, so do nothing else */
805 /* edit events get handled here */
807 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
813 case TempoMarkerItem:
814 edit_tempo_marker (item);
817 case MeterMarkerItem:
818 edit_meter_marker (item);
821 case AudioRegionViewName:
822 if (clicked_regionview->name_active()) {
823 return mouse_rename_region (item, event);
833 /* context menu events get handled here */
835 if (Keyboard::is_context_menu_event (&event->button)) {
837 if (drag_info.item == 0) {
839 /* no matter which button pops up the context menu, tell the menu
840 widget to use button 1 to drive menu selection.
845 case FadeInHandleItem:
847 case FadeOutHandleItem:
848 popup_fade_context_menu (1, event->button.time, item, item_type);
852 popup_track_context_menu (1, event->button.time, item_type, false, where);
856 case AudioRegionViewNameHighlight:
857 case AudioRegionViewName:
858 popup_track_context_menu (1, event->button.time, item_type, false, where);
862 popup_track_context_menu (1, event->button.time, item_type, true, where);
865 case AutomationTrackItem:
866 popup_track_context_menu (1, event->button.time, item_type, false, where);
870 case RangeMarkerBarItem:
871 case TransportMarkerBarItem:
874 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
878 marker_context_menu (&event->button, item);
881 case TempoMarkerItem:
882 tm_marker_context_menu (&event->button, item);
885 case MeterMarkerItem:
886 tm_marker_context_menu (&event->button, item);
889 case CrossfadeViewItem:
890 popup_track_context_menu (1, event->button.time, item_type, false, where);
893 /* <CMT Additions> */
895 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
897 case ImageFrameTimeAxisItem:
898 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
901 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
903 case MarkerTimeAxisItem:
904 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
906 /* <CMT Additions> */
917 /* delete events get handled here */
919 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
922 case TempoMarkerItem:
923 remove_tempo_marker (item);
926 case MeterMarkerItem:
927 remove_meter_marker (item);
932 remove_marker (*item, event);
937 if (mouse_mode == MouseObject) {
938 remove_clicked_region ();
942 case GainControlPointItem:
943 if (mouse_mode == MouseGain) {
944 remove_gain_control_point (item, event);
948 case GainAutomationControlPointItem:
949 case PanAutomationControlPointItem:
950 case RedirectAutomationControlPointItem:
951 remove_control_point (item, event);
960 switch (event->button.button) {
964 /* see comments in button_press_handler */
966 case PlayheadCursorItem:
969 case GainAutomationLineItem:
970 case PanAutomationLineItem:
971 case RedirectAutomationLineItem:
972 case StartSelectionTrimItem:
973 case EndSelectionTrimItem:
977 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
978 snap_to (where, 0, true);
980 mouse_add_new_marker (where);
984 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
987 mouse_add_new_tempo_event (where);
991 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
999 switch (mouse_mode) {
1001 switch (item_type) {
1002 case AutomationTrackItem:
1003 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1017 switch (item_type) {
1019 clicked_regionview->add_gain_point_event (item, event);
1023 case AutomationTrackItem:
1024 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1025 add_automation_event (item, event, where, event->button.y);
1034 switch (item_type) {
1036 audition_selected_region ();
1053 switch (mouse_mode) {
1056 switch (item_type) {
1058 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1060 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1063 // Button2 click is unused
1076 // x_style_paste (where, 1.0);
1096 Editor::maybe_autoscroll (GdkEvent* event)
1098 jack_nframes_t one_page = (jack_nframes_t) rint (canvas_width * frames_per_unit);
1099 jack_nframes_t rightmost_frame = leftmost_frame + one_page;
1101 jack_nframes_t frame = drag_info.current_pointer_frame;
1103 if (autoscroll_timeout_tag < 0) {
1104 if (frame > rightmost_frame) {
1105 if (rightmost_frame < max_frames) {
1106 start_canvas_autoscroll (1);
1108 } else if (frame < leftmost_frame) {
1109 if (leftmost_frame > 0) {
1110 start_canvas_autoscroll (-1);
1114 if (frame >= leftmost_frame && frame < rightmost_frame) {
1115 stop_canvas_autoscroll ();
1121 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1127 switch (item_type) {
1128 case GainControlPointItem:
1129 if (mouse_mode == MouseGain) {
1130 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1131 cp->set_visible (true);
1135 at_y = cp->get_y ();
1136 cp->item->i2w (at_x, at_y);
1140 fraction = 1.0 - (cp->get_y() / cp->line.height());
1142 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1143 show_verbose_canvas_cursor ();
1145 if (is_drawable()) {
1146 track_canvas.get_window()->set_cursor (*fader_cursor);
1151 case GainAutomationControlPointItem:
1152 case PanAutomationControlPointItem:
1153 case RedirectAutomationControlPointItem:
1154 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1155 cp->set_visible (true);
1159 at_y = cp->get_y ();
1160 cp->item->i2w (at_x, at_y);
1164 fraction = 1.0 - (cp->get_y() / cp->line.height());
1166 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1167 show_verbose_canvas_cursor ();
1169 if (is_drawable()) {
1170 track_canvas.get_window()->set_cursor (*fader_cursor);
1175 if (mouse_mode == MouseGain) {
1176 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1178 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1179 if (is_drawable()) {
1180 track_canvas.get_window()->set_cursor (*fader_cursor);
1185 case GainAutomationLineItem:
1186 case RedirectAutomationLineItem:
1187 case PanAutomationLineItem:
1189 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1191 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1193 if (is_drawable()) {
1194 track_canvas.get_window()->set_cursor (*fader_cursor);
1198 case AudioRegionViewNameHighlight:
1199 if (is_drawable() && mouse_mode == MouseObject) {
1200 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1204 case StartSelectionTrimItem:
1205 case EndSelectionTrimItem:
1206 /* <CMT Additions> */
1207 case ImageFrameHandleStartItem:
1208 case ImageFrameHandleEndItem:
1209 case MarkerViewHandleStartItem:
1210 case MarkerViewHandleEndItem:
1211 /* </CMT Additions> */
1213 if (is_drawable()) {
1214 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1218 case EditCursorItem:
1219 case PlayheadCursorItem:
1220 if (is_drawable()) {
1221 track_canvas.get_window()->set_cursor (*grabber_cursor);
1225 case AudioRegionViewName:
1227 /* when the name is not an active item, the entire name highlight is for trimming */
1229 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1230 if (mouse_mode == MouseObject && is_drawable()) {
1231 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1237 case AutomationTrackItem:
1238 if (is_drawable()) {
1239 Gdk::Cursor *cursor;
1240 switch (mouse_mode) {
1242 cursor = selector_cursor;
1245 cursor = zoom_cursor;
1248 cursor = cross_hair_cursor;
1252 track_canvas.get_window()->set_cursor (*cursor);
1254 AutomationTimeAxisView* atv;
1255 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1256 clear_entered_track = false;
1257 set_entered_track (atv);
1263 case RangeMarkerBarItem:
1264 case TransportMarkerBarItem:
1267 if (is_drawable()) {
1268 time_canvas.get_window()->set_cursor (*timebar_cursor);
1273 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1276 marker->set_color_rgba (color_map[cEnteredMarker]);
1278 case MeterMarkerItem:
1279 case TempoMarkerItem:
1280 if (is_drawable()) {
1281 time_canvas.get_window()->set_cursor (*timebar_cursor);
1284 case FadeInHandleItem:
1285 case FadeOutHandleItem:
1286 if (mouse_mode == MouseObject) {
1287 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1289 rect->property_fill_color_rgba() = 0;
1290 rect->property_outline_pixels() = 1;
1299 /* second pass to handle entered track status in a comprehensible way.
1302 switch (item_type) {
1304 case GainAutomationLineItem:
1305 case RedirectAutomationLineItem:
1306 case PanAutomationLineItem:
1307 case GainControlPointItem:
1308 case GainAutomationControlPointItem:
1309 case PanAutomationControlPointItem:
1310 case RedirectAutomationControlPointItem:
1311 /* these do not affect the current entered track state */
1312 clear_entered_track = false;
1315 case AutomationTrackItem:
1316 /* handled above already */
1320 set_entered_track (0);
1328 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1334 AudioRegionView* rv;
1337 switch (item_type) {
1338 case GainControlPointItem:
1339 case GainAutomationControlPointItem:
1340 case PanAutomationControlPointItem:
1341 case RedirectAutomationControlPointItem:
1342 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1343 if (cp->line.npoints() > 1) {
1344 if (!cp->selected) {
1345 cp->set_visible (false);
1349 if (is_drawable()) {
1350 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1353 hide_verbose_canvas_cursor ();
1356 case AudioRegionViewNameHighlight:
1357 case StartSelectionTrimItem:
1358 case EndSelectionTrimItem:
1359 case EditCursorItem:
1360 case PlayheadCursorItem:
1361 /* <CMT Additions> */
1362 case ImageFrameHandleStartItem:
1363 case ImageFrameHandleEndItem:
1364 case MarkerViewHandleStartItem:
1365 case MarkerViewHandleEndItem:
1366 /* </CMT Additions> */
1367 if (is_drawable()) {
1368 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1373 case GainAutomationLineItem:
1374 case RedirectAutomationLineItem:
1375 case PanAutomationLineItem:
1376 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1378 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1380 line->property_fill_color_rgba() = al->get_line_color();
1382 if (is_drawable()) {
1383 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1387 case AudioRegionViewName:
1388 /* see enter_handler() for notes */
1389 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1390 if (is_drawable() && mouse_mode == MouseObject) {
1391 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1396 case RangeMarkerBarItem:
1397 case TransportMarkerBarItem:
1401 if (is_drawable()) {
1402 time_canvas.get_window()->set_cursor (*timebar_cursor);
1407 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1410 loc = find_location_from_marker (marker, is_start);
1411 if (loc) location_flags_changed (loc, this);
1413 case MeterMarkerItem:
1414 case TempoMarkerItem:
1416 if (is_drawable()) {
1417 time_canvas.get_window()->set_cursor (*timebar_cursor);
1422 case FadeInHandleItem:
1423 case FadeOutHandleItem:
1424 rv = static_cast<AudioRegionView*>(item->get_data ("regionview"));
1426 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1428 rect->property_fill_color_rgba() = rv->get_fill_color();
1429 rect->property_outline_pixels() = 0;
1434 case AutomationTrackItem:
1435 if (is_drawable()) {
1436 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1437 clear_entered_track = true;
1438 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1450 Editor::left_automation_track ()
1452 if (clear_entered_track) {
1453 set_entered_track (0);
1454 clear_entered_track = false;
1460 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1464 /* We call this so that MOTION_NOTIFY events continue to be
1465 delivered to the canvas. We need to do this because we set
1466 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1467 the density of the events, at the expense of a round-trip
1468 to the server. Given that this will mostly occur on cases
1469 where DISPLAY = :0.0, and given the cost of what the motion
1470 event might do, its a good tradeoff.
1473 track_canvas.get_pointer (x, y);
1475 if (current_stepping_trackview) {
1476 /* don't keep the persistent stepped trackview if the mouse moves */
1477 current_stepping_trackview = 0;
1478 step_timeout.disconnect ();
1481 if (session && session->actively_recording()) {
1482 /* Sorry. no dragging stuff around while we record */
1486 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1487 &drag_info.current_pointer_y);
1489 if (drag_info.item) {
1490 /* item != 0 is the best test i can think of for
1493 if (!drag_info.move_threshold_passsed)
1495 drag_info.move_threshold_passsed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1497 // and change the initial grab loc/frame if this drag info wants us to
1498 if (drag_info.want_move_threshold && drag_info.move_threshold_passsed) {
1499 drag_info.grab_frame = drag_info.current_pointer_frame;
1500 drag_info.grab_x = drag_info.current_pointer_x;
1501 drag_info.grab_y = drag_info.current_pointer_y;
1502 drag_info.last_pointer_frame = drag_info.grab_frame;
1503 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1508 switch (item_type) {
1509 case PlayheadCursorItem:
1510 case EditCursorItem:
1512 case GainControlPointItem:
1513 case RedirectAutomationControlPointItem:
1514 case GainAutomationControlPointItem:
1515 case PanAutomationControlPointItem:
1516 case TempoMarkerItem:
1517 case MeterMarkerItem:
1518 case AudioRegionViewNameHighlight:
1519 case StartSelectionTrimItem:
1520 case EndSelectionTrimItem:
1523 case RedirectAutomationLineItem:
1524 case GainAutomationLineItem:
1525 case PanAutomationLineItem:
1526 case FadeInHandleItem:
1527 case FadeOutHandleItem:
1528 /* <CMT Additions> */
1529 case ImageFrameHandleStartItem:
1530 case ImageFrameHandleEndItem:
1531 case MarkerViewHandleStartItem:
1532 case MarkerViewHandleEndItem:
1533 /* </CMT Additions> */
1534 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1535 (event->motion.state & Gdk::BUTTON2_MASK))) {
1536 maybe_autoscroll (event);
1537 (this->*(drag_info.motion_callback)) (item, event);
1546 switch (mouse_mode) {
1551 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1552 (event->motion.state & GDK_BUTTON2_MASK))) {
1553 maybe_autoscroll (event);
1554 (this->*(drag_info.motion_callback)) (item, event);
1565 track_canvas_motion (event);
1573 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1575 if (drag_info.item == 0) {
1576 fatal << _("programming error: start_grab called without drag item") << endmsg;
1582 cursor = grabber_cursor;
1585 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1587 if (event->button.button == 2) {
1588 drag_info.x_constrained = true;
1590 drag_info.x_constrained = false;
1593 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1594 drag_info.last_pointer_frame = drag_info.grab_frame;
1595 drag_info.current_pointer_frame = drag_info.grab_frame;
1596 drag_info.current_pointer_x = drag_info.grab_x;
1597 drag_info.current_pointer_y = drag_info.grab_y;
1598 drag_info.cumulative_x_drag = 0;
1599 drag_info.cumulative_y_drag = 0;
1600 drag_info.first_move = true;
1601 drag_info.move_threshold_passsed = false;
1602 drag_info.want_move_threshold = false;
1603 drag_info.pointer_frame_offset = 0;
1604 drag_info.brushing = false;
1605 drag_info.copied_location = 0;
1607 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1609 event->button.time);
1611 if (session && session->transport_rolling()) {
1612 drag_info.was_rolling = true;
1614 drag_info.was_rolling = false;
1617 switch (snap_type) {
1618 case SnapToRegionStart:
1619 case SnapToRegionEnd:
1620 case SnapToRegionSync:
1621 case SnapToRegionBoundary:
1622 build_region_boundary_cache ();
1630 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1632 bool did_drag = false;
1634 stop_canvas_autoscroll ();
1636 if (drag_info.item == 0) {
1640 drag_info.item->ungrab (event->button.time);
1642 if (drag_info.finished_callback) {
1643 (this->*(drag_info.finished_callback)) (item, event);
1646 did_drag = !drag_info.first_move;
1648 hide_verbose_canvas_cursor();
1651 drag_info.copy = false;
1652 drag_info.motion_callback = 0;
1653 drag_info.finished_callback = 0;
1654 drag_info.last_trackview = 0;
1655 drag_info.last_frame_position = 0;
1656 drag_info.grab_frame = 0;
1657 drag_info.last_pointer_frame = 0;
1658 drag_info.current_pointer_frame = 0;
1659 drag_info.brushing = false;
1661 if (drag_info.copied_location) {
1662 delete drag_info.copied_location;
1663 drag_info.copied_location = 0;
1670 Editor::set_edit_cursor (GdkEvent* event)
1672 jack_nframes_t pointer_frame = event_frame (event);
1674 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1675 if (snap_type != SnapToEditCursor) {
1676 snap_to (pointer_frame);
1680 edit_cursor->set_position (pointer_frame);
1681 edit_cursor_clock.set (pointer_frame);
1685 Editor::set_playhead_cursor (GdkEvent* event)
1687 jack_nframes_t pointer_frame = event_frame (event);
1689 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1690 snap_to (pointer_frame);
1694 session->request_locate (pointer_frame, session->transport_rolling());
1699 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1701 drag_info.item = item;
1702 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1703 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1707 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1708 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1712 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1714 drag_info.pointer_frame_offset = drag_info.grab_frame - ((jack_nframes_t) arv->region.fade_in().back()->when + arv->region.position());
1718 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1720 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1722 jack_nframes_t fade_length;
1724 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1725 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1731 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1735 if (pos < (arv->region.position() + 64)) {
1736 fade_length = 64; // this should be a minimum defined somewhere
1737 } else if (pos > arv->region.last_frame()) {
1738 fade_length = arv->region.length();
1740 fade_length = pos - arv->region.position();
1743 arv->reset_fade_in_shape_width (fade_length);
1745 show_verbose_duration_cursor (arv->region.position(), arv->region.position() + fade_length, 10);
1747 drag_info.first_move = false;
1751 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1753 if (drag_info.first_move) return;
1755 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1757 jack_nframes_t fade_length;
1759 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1760 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1766 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1770 if (pos < (arv->region.position() + 64)) {
1771 fade_length = 64; // this should be a minimum defined somewhere
1773 else if (pos > arv->region.last_frame()) {
1774 fade_length = arv->region.length();
1777 fade_length = pos - arv->region.position();
1780 begin_reversible_command (_("change fade in length"));
1781 session->add_undo (arv->region.get_memento());
1782 arv->region.set_fade_in_length (fade_length);
1783 session->add_redo_no_execute (arv->region.get_memento());
1784 commit_reversible_command ();
1785 fade_in_drag_motion_callback (item, event);
1789 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1791 drag_info.item = item;
1792 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1793 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1797 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1798 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1802 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1804 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region.length() - (jack_nframes_t) arv->region.fade_out().back()->when + arv->region.position());
1808 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1810 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1812 jack_nframes_t fade_length;
1814 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1815 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1821 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1825 if (pos > (arv->region.last_frame() - 64)) {
1826 fade_length = 64; // this should really be a minimum fade defined somewhere
1828 else if (pos < arv->region.position()) {
1829 fade_length = arv->region.length();
1832 fade_length = arv->region.last_frame() - pos;
1835 arv->reset_fade_out_shape_width (fade_length);
1837 show_verbose_duration_cursor (arv->region.last_frame() - fade_length, arv->region.last_frame(), 10);
1839 drag_info.first_move = false;
1843 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1845 if (drag_info.first_move) return;
1847 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1849 jack_nframes_t fade_length;
1851 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1852 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1858 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1862 if (pos > (arv->region.last_frame() - 64)) {
1863 fade_length = 64; // this should really be a minimum fade defined somewhere
1865 else if (pos < arv->region.position()) {
1866 fade_length = arv->region.length();
1869 fade_length = arv->region.last_frame() - pos;
1872 begin_reversible_command (_("change fade out length"));
1873 session->add_undo (arv->region.get_memento());
1874 arv->region.set_fade_out_length (fade_length);
1875 session->add_redo_no_execute (arv->region.get_memento());
1876 commit_reversible_command ();
1878 fade_out_drag_motion_callback (item, event);
1882 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1884 drag_info.item = item;
1885 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1886 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1890 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1891 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1895 Cursor* cursor = (Cursor *) drag_info.data;
1897 if (session && cursor == playhead_cursor) {
1898 if (drag_info.was_rolling) {
1899 session->request_stop ();
1903 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1905 show_verbose_time_cursor (cursor->current_frame, 10);
1909 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1911 Cursor* cursor = (Cursor *) drag_info.data;
1912 jack_nframes_t adjusted_frame;
1914 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1915 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1921 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1922 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1923 snap_to (adjusted_frame);
1927 if (adjusted_frame == drag_info.last_pointer_frame) return;
1929 cursor->set_position (adjusted_frame);
1931 if (cursor == edit_cursor) {
1932 edit_cursor_clock.set (cursor->current_frame);
1935 show_verbose_time_cursor (cursor->current_frame, 10);
1937 drag_info.last_pointer_frame = adjusted_frame;
1938 drag_info.first_move = false;
1942 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1944 if (drag_info.first_move) return;
1946 cursor_drag_motion_callback (item, event);
1948 if (item == &playhead_cursor->canvas_item) {
1950 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1952 } else if (item == &edit_cursor->canvas_item) {
1953 edit_cursor->set_position (edit_cursor->current_frame);
1954 edit_cursor_clock.set (edit_cursor->current_frame);
1959 Editor::update_marker_drag_item (Location *location)
1961 double x1 = frame_to_pixel (location->start());
1962 double x2 = frame_to_pixel (location->end());
1964 if (location->is_mark()) {
1965 marker_drag_line_points.front().set_x(x1);
1966 marker_drag_line_points.back().set_x(x1);
1967 marker_drag_line->property_points() = marker_drag_line_points;
1970 range_marker_drag_rect->property_x1() = x1;
1971 range_marker_drag_rect->property_x2() = x2;
1976 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
1980 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1981 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1987 Location *location = find_location_from_marker (marker, is_start);
1989 drag_info.item = item;
1990 drag_info.data = marker;
1991 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
1992 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
1996 drag_info.copied_location = new Location (*location);
1997 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
1999 update_marker_drag_item (location);
2001 if (location->is_mark()) {
2002 marker_drag_line->show();
2003 marker_drag_line->raise_to_top();
2006 range_marker_drag_rect->show();
2007 range_marker_drag_rect->raise_to_top();
2010 if (is_start) show_verbose_time_cursor (location->start(), 10);
2011 else show_verbose_time_cursor (location->end(), 10);
2015 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2017 jack_nframes_t f_delta;
2018 Marker* marker = (Marker *) drag_info.data;
2019 Location *real_location;
2020 Location *copy_location;
2022 bool move_both = false;
2024 jack_nframes_t newframe;
2025 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2026 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2032 jack_nframes_t next = newframe;
2034 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2035 snap_to (newframe, 0, true);
2038 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
2040 /* call this to find out if its the start or end */
2042 real_location = find_location_from_marker (marker, is_start);
2044 /* use the copy that we're "dragging" around */
2046 copy_location = drag_info.copied_location;
2048 f_delta = copy_location->end() - copy_location->start();
2050 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2054 if (is_start) { // start marker
2057 copy_location->set_start (newframe);
2058 copy_location->set_end (newframe + f_delta);
2059 } else if (newframe < copy_location->end()) {
2060 copy_location->set_start (newframe);
2062 snap_to (next, 1, true);
2063 copy_location->set_end (next);
2064 copy_location->set_start (newframe);
2067 } else { // end marker
2070 copy_location->set_end (newframe);
2071 copy_location->set_start (newframe - f_delta);
2072 } else if (newframe > copy_location->start()) {
2073 copy_location->set_end (newframe);
2075 } else if (newframe > 0) {
2076 snap_to (next, -1, true);
2077 copy_location->set_start (next);
2078 copy_location->set_end (newframe);
2082 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2083 drag_info.first_move = false;
2085 update_marker_drag_item (copy_location);
2087 LocationMarkers* lm = find_location_markers (real_location);
2088 lm->set_position (copy_location->start(), copy_location->end());
2090 show_verbose_time_cursor (newframe, 10);
2094 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2096 if (drag_info.first_move) {
2097 marker_drag_motion_callback (item, event);
2101 Marker* marker = (Marker *) drag_info.data;
2103 Location * location = find_location_from_marker (marker, is_start);
2105 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2108 marker_drag_line->hide();
2109 range_marker_drag_rect->hide();
2113 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2116 MeterMarker* meter_marker;
2118 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2119 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2123 meter_marker = dynamic_cast<MeterMarker*> (marker);
2125 MetricSection& section (meter_marker->meter());
2127 if (!section.movable()) {
2131 drag_info.item = item;
2132 drag_info.data = marker;
2133 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2134 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2138 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2140 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2144 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2146 MeterMarker* marker = (MeterMarker *) drag_info.data;
2147 jack_nframes_t adjusted_frame;
2149 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2150 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2156 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2157 snap_to (adjusted_frame);
2160 if (adjusted_frame == drag_info.last_pointer_frame) return;
2162 marker->set_position (adjusted_frame);
2165 drag_info.last_pointer_frame = adjusted_frame;
2166 drag_info.first_move = false;
2168 show_verbose_time_cursor (adjusted_frame, 10);
2172 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2174 if (drag_info.first_move) return;
2176 meter_marker_drag_motion_callback (item, event);
2178 MeterMarker* marker = (MeterMarker *) drag_info.data;
2181 TempoMap& map (session->tempo_map());
2182 map.bbt_time (drag_info.last_pointer_frame, when);
2184 begin_reversible_command (_("move meter mark"));
2185 session->add_undo (map.get_memento());
2186 map.move_meter (marker->meter(), when);
2187 session->add_redo_no_execute (map.get_memento());
2188 commit_reversible_command ();
2192 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2195 TempoMarker* tempo_marker;
2197 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2198 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2202 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2203 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2207 MetricSection& section (tempo_marker->tempo());
2209 if (!section.movable()) {
2213 drag_info.item = item;
2214 drag_info.data = marker;
2215 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2216 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2220 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2221 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2225 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2227 TempoMarker* marker = (TempoMarker *) drag_info.data;
2228 jack_nframes_t adjusted_frame;
2230 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2231 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2237 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2238 snap_to (adjusted_frame);
2241 if (adjusted_frame == drag_info.last_pointer_frame) return;
2243 /* OK, we've moved far enough to make it worth actually move the thing. */
2245 marker->set_position (adjusted_frame);
2247 show_verbose_time_cursor (adjusted_frame, 10);
2249 drag_info.last_pointer_frame = adjusted_frame;
2250 drag_info.first_move = false;
2254 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2256 if (drag_info.first_move) return;
2258 tempo_marker_drag_motion_callback (item, event);
2260 TempoMarker* marker = (TempoMarker *) drag_info.data;
2263 TempoMap& map (session->tempo_map());
2264 map.bbt_time (drag_info.last_pointer_frame, when);
2266 begin_reversible_command (_("move tempo mark"));
2267 session->add_undo (map.get_memento());
2268 map.move_tempo (marker->tempo(), when);
2269 session->add_redo_no_execute (map.get_memento());
2270 commit_reversible_command ();
2274 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2276 ControlPoint* control_point;
2278 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2279 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2283 // We shouldn't remove the first or last gain point
2284 if (control_point->line.is_last_point(*control_point) ||
2285 control_point->line.is_first_point(*control_point)) {
2289 control_point->line.remove_point (*control_point);
2293 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2295 ControlPoint* control_point;
2297 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2298 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2302 control_point->line.remove_point (*control_point);
2306 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2308 ControlPoint* control_point;
2310 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2311 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2315 drag_info.item = item;
2316 drag_info.data = control_point;
2317 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2318 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2320 start_grab (event, fader_cursor);
2322 control_point->line.start_drag (control_point, 0);
2324 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2325 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2326 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2328 show_verbose_canvas_cursor ();
2332 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2334 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2336 double cx = drag_info.current_pointer_x;
2337 double cy = drag_info.current_pointer_y;
2339 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2340 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2342 bool x_constrained = false;
2344 if (drag_info.x_constrained) {
2345 if (fabs(drag_info.cumulative_x_drag) < fabs(drag_info.cumulative_y_drag)) {
2346 cx = drag_info.grab_x;
2347 x_constrained = true;
2350 cy = drag_info.grab_y;
2355 cp->line.parent_group().w2i (cx, cy);
2359 cy = min ((double) cp->line.height(), cy);
2361 //translate cx to frames
2362 jack_nframes_t cx_frames = (jack_nframes_t) floor (cx * frames_per_unit);
2364 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !x_constrained) {
2365 snap_to (cx_frames);
2368 float fraction = 1.0 - (cy / cp->line.height());
2372 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2378 cp->line.point_drag (*cp, cx_frames , fraction, push);
2380 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2384 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2386 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2387 control_point_drag_motion_callback (item, event);
2388 cp->line.end_drag (cp);
2392 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2394 switch (mouse_mode) {
2396 start_line_grab (clicked_regionview->get_gain_line(), event);
2404 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2408 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2409 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2413 start_line_grab (al, event);
2417 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2421 jack_nframes_t frame_within_region;
2423 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2427 cx = event->button.x;
2428 cy = event->button.y;
2429 line->parent_group().w2i (cx, cy);
2430 frame_within_region = (jack_nframes_t) floor (cx * frames_per_unit);
2432 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2433 current_line_drag_info.after)) {
2434 /* no adjacent points */
2438 drag_info.item = &line->grab_item();
2439 drag_info.data = line;
2440 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2441 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2443 start_grab (event, fader_cursor);
2445 double fraction = 1.0 - (cy / line->height());
2447 line->start_drag (0, fraction);
2449 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2450 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2451 show_verbose_canvas_cursor ();
2455 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2457 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2458 double cx = drag_info.current_pointer_x;
2459 double cy = drag_info.current_pointer_y;
2461 line->parent_group().w2i (cx, cy);
2464 fraction = 1.0 - (cy / line->height());
2468 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2474 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2476 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2480 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2482 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2483 line_drag_motion_callback (item, event);
2488 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2490 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2494 drag_info.copy = false;
2495 drag_info.item = item;
2496 drag_info.data = clicked_regionview;
2497 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2498 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2503 TimeAxisView* tvp = clicked_trackview;
2504 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2506 if (tv && tv->is_audio_track()) {
2507 speed = tv->get_diskstream()->speed();
2510 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2511 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2512 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2513 // we want a move threshold
2514 drag_info.want_move_threshold = true;
2516 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2518 begin_reversible_command (_("move region(s)"));
2522 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2524 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2528 /* this is committed in the grab finished callback. */
2530 begin_reversible_command (_("Drag region copy"));
2532 /* duplicate the region(s) */
2534 vector<AudioRegionView*> new_regionviews;
2536 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2537 AudioRegionView* rv;
2541 Playlist* to_playlist = rv->region.playlist();
2542 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
2544 session->add_undo (to_playlist->get_memento ());
2545 latest_regionview = 0;
2547 sigc::connection c = atv->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2549 /* create a new region with the same name.
2552 AudioRegion* newregion = new AudioRegion (rv->region);
2554 /* if the original region was locked, we don't care */
2556 newregion->set_locked (false);
2558 to_playlist->add_region (*newregion, (jack_nframes_t) (rv->region.position() * atv->get_diskstream()->speed()));
2562 if (latest_regionview) {
2563 new_regionviews.push_back (latest_regionview);
2568 if (new_regionviews.empty()) {
2572 /* reset selection to new regionviews */
2574 selection->set (new_regionviews);
2576 drag_info.item = new_regionviews.front()->get_canvas_group ();
2577 drag_info.copy = true;
2578 drag_info.data = new_regionviews.front();
2579 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2580 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2584 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2585 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
2588 if (atv && atv->is_audio_track()) {
2589 speed = atv->get_diskstream()->speed();
2592 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2593 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2594 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2595 // we want a move threshold
2596 drag_info.want_move_threshold = true;
2598 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2600 begin_reversible_command (_("copy region(s)"));
2604 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2606 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2610 drag_info.copy = false;
2611 drag_info.item = item;
2612 drag_info.data = clicked_regionview;
2613 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2614 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2619 TimeAxisView* tvp = clicked_trackview;
2620 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2622 if (tv && tv->is_audio_track()) {
2623 speed = tv->get_diskstream()->speed();
2626 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2627 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2628 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2629 // we want a move threshold
2630 drag_info.want_move_threshold = true;
2631 drag_info.brushing = true;
2633 begin_reversible_command (_("Drag region brush"));
2637 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2641 AudioRegionView *rv = reinterpret_cast<AudioRegionView*> (drag_info.data);
2642 jack_nframes_t pending_region_position = 0;
2643 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2644 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2645 bool clamp_y_axis = false;
2646 vector<int32_t> height_list(512) ;
2647 vector<int32_t>::iterator j;
2649 /* Which trackview is this ? */
2651 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2652 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2654 /* The region motion is only processed if the pointer is over
2658 if (!tv || !tv->is_audio_track()) {
2659 /* To make sure we hide the verbose canvas cursor when the mouse is
2660 not held over and audiotrack.
2662 hide_verbose_canvas_cursor ();
2666 original_pointer_order = drag_info.last_trackview->order;
2668 /************************************************************
2670 ************************************************************/
2672 if (drag_info.brushing) {
2673 clamp_y_axis = true;
2678 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2680 int32_t children = 0, numtracks = 0;
2681 // XXX hard coding track limit, oh my, so very very bad
2682 bitset <1024> tracks (0x00);
2683 /* get a bitmask representing the visible tracks */
2685 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2686 TimeAxisView *tracklist_timeview;
2687 tracklist_timeview = (*i);
2688 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2689 list<TimeAxisView*> children_list;
2691 /* zeroes are audio tracks. ones are other types. */
2693 if (!atv2->hidden()) {
2695 if (visible_y_high < atv2->order) {
2696 visible_y_high = atv2->order;
2698 if (visible_y_low > atv2->order) {
2699 visible_y_low = atv2->order;
2702 if (!atv2->is_audio_track()) {
2703 tracks = tracks |= (0x01 << atv2->order);
2706 height_list[atv2->order] = (*i)->height;
2708 if ((children_list = atv2->get_child_list()).size() > 0) {
2709 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2710 tracks = tracks |= (0x01 << (atv2->order + children));
2711 height_list[atv2->order + children] = (*j)->height;
2719 /* find the actual span according to the canvas */
2721 canvas_pointer_y_span = pointer_y_span;
2722 if (drag_info.last_trackview->order >= tv->order) {
2724 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2725 if (height_list[y] == 0 ) {
2726 canvas_pointer_y_span--;
2731 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2732 if ( height_list[y] == 0 ) {
2733 canvas_pointer_y_span++;
2738 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2739 AudioRegionView* rv2;
2741 double ix1, ix2, iy1, iy2;
2744 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2745 rv2->get_canvas_group()->i2w (ix1, iy1);
2746 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2747 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
2749 if (atv2->order != original_pointer_order) {
2750 /* this isn't the pointer track */
2752 if (canvas_pointer_y_span > 0) {
2754 /* moving up the canvas */
2755 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2757 int32_t visible_tracks = 0;
2758 while (visible_tracks < canvas_pointer_y_span ) {
2761 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2762 /* we're passing through a hidden track */
2767 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2768 clamp_y_axis = true;
2772 clamp_y_axis = true;
2775 } else if (canvas_pointer_y_span < 0) {
2777 /*moving down the canvas*/
2779 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2782 int32_t visible_tracks = 0;
2784 while (visible_tracks > canvas_pointer_y_span ) {
2787 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2791 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2792 clamp_y_axis = true;
2797 clamp_y_axis = true;
2803 /* this is the pointer's track */
2804 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2805 clamp_y_axis = true;
2806 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2807 clamp_y_axis = true;
2815 } else if (drag_info.last_trackview == tv) {
2816 clamp_y_axis = true;
2820 if (!clamp_y_axis) {
2821 drag_info.last_trackview = tv;
2824 /************************************************************
2826 ************************************************************/
2828 /* compute the amount of pointer motion in frames, and where
2829 the region would be if we moved it by that much.
2832 if (drag_info.move_threshold_passsed) {
2834 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2836 jack_nframes_t sync_frame;
2837 jack_nframes_t sync_offset;
2840 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2842 sync_offset = rv->region.sync_offset (sync_dir);
2843 sync_frame = rv->region.adjust_to_sync (pending_region_position);
2845 /* we snap if the snap modifier is not enabled.
2848 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2849 snap_to (sync_frame);
2852 if (sync_frame - sync_offset <= sync_frame) {
2853 pending_region_position = sync_frame - (sync_dir*sync_offset);
2855 pending_region_position = 0;
2859 pending_region_position = 0;
2862 if (pending_region_position > max_frames - rv->region.length()) {
2863 pending_region_position = drag_info.last_frame_position;
2866 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
2868 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
2870 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
2871 to make it appear at the new location.
2874 if (pending_region_position > drag_info.last_frame_position) {
2875 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
2877 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
2880 drag_info.last_frame_position = pending_region_position;
2887 /* threshold not passed */
2892 /*************************************************************
2894 ************************************************************/
2896 if (x_delta == 0 && (pointer_y_span == 0)) {
2897 /* haven't reached next snap point, and we're not switching
2898 trackviews. nothing to do.
2904 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2906 AudioRegionView* rv2;
2909 /* if any regionview is at zero, we need to know so we can
2910 stop further leftward motion.
2913 double ix1, ix2, iy1, iy2;
2914 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2915 rv2->get_canvas_group()->i2w (ix1, iy1);
2924 /*************************************************************
2926 ************************************************************/
2928 pair<set<Playlist*>::iterator,bool> insert_result;
2930 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2932 AudioRegionView* rv;
2934 double ix1, ix2, iy1, iy2;
2935 int32_t temp_pointer_y_span = pointer_y_span;
2937 /* get item BBox, which will be relative to parent. so we have
2938 to query on a child, then convert to world coordinates using
2942 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2943 rv->get_canvas_group()->i2w (ix1, iy1);
2944 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2945 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
2946 AudioTimeAxisView* temp_atv;
2948 if ((pointer_y_span != 0) && !clamp_y_axis) {
2951 for (j = height_list.begin(); j!= height_list.end(); j++) {
2952 if (x == canvas_atv->order) {
2953 /* we found the track the region is on */
2954 if (x != original_pointer_order) {
2955 /*this isn't from the same track we're dragging from */
2956 temp_pointer_y_span = canvas_pointer_y_span;
2958 while (temp_pointer_y_span > 0) {
2959 /* we're moving up canvas-wise,
2960 so we need to find the next track height
2962 if (j != height_list.begin()) {
2965 if (x != original_pointer_order) {
2966 /* we're not from the dragged track, so ignore hidden tracks. */
2968 temp_pointer_y_span++;
2972 temp_pointer_y_span--;
2974 while (temp_pointer_y_span < 0) {
2976 if (x != original_pointer_order) {
2978 temp_pointer_y_span--;
2982 if (j != height_list.end()) {
2985 temp_pointer_y_span++;
2987 /* find out where we'll be when we move and set height accordingly */
2989 tvp2 = trackview_by_y_position (iy1 + y_delta);
2990 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
2991 rv->set_height (temp_atv->height);
2993 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
2994 personally, i think this can confuse things, but never mind.
2997 //const GdkColor& col (temp_atv->view->get_region_color());
2998 //rv->set_color (const_cast<GdkColor&>(col));
3005 /* prevent the regionview from being moved to before
3006 the zero position on the canvas.
3011 if (-x_delta > ix1) {
3014 } else if ((x_delta > 0) &&(rv->region.last_frame() > max_frames - x_delta)) {
3015 x_delta = max_frames - rv->region.last_frame();
3018 if (drag_info.first_move) {
3020 /* hide any dependent views */
3022 // rv->get_time_axis_view().hide_dependent_views (*rv);
3024 /* this is subtle. raising the regionview itself won't help,
3025 because raise_to_top() just puts the item on the top of
3026 its parent's stack. so, we need to put the trackview canvas_display group
3027 on the top, since its parent is the whole canvas.
3030 rv->get_canvas_group()->raise_to_top();
3031 rv->get_time_axis_view().canvas_display->raise_to_top();
3032 cursor_group->raise_to_top();
3034 /* freeze the playlists from notifying till
3038 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3039 if (atv && atv->is_audio_track()) {
3040 AudioPlaylist* pl = atv->get_diskstream()->playlist();
3042 /* only freeze and capture state once */
3044 insert_result = motion_frozen_playlists.insert (pl);
3045 if (insert_result.second) {
3047 session->add_undo(pl->get_memento());
3053 if (drag_info.brushing) {
3054 mouse_brush_insert_region (rv, pending_region_position);
3056 rv->move (x_delta, y_delta);
3060 if (drag_info.first_move) {
3061 cursor_group->raise_to_top();
3064 drag_info.first_move = false;
3066 if (x_delta != 0 && !drag_info.brushing) {
3067 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3073 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3075 jack_nframes_t where;
3076 AudioRegionView* rv = reinterpret_cast<AudioRegionView *> (drag_info.data);
3077 pair<set<Playlist*>::iterator,bool> insert_result;
3078 bool nocommit = true;
3080 AudioTimeAxisView* atv;
3081 bool regionview_y_movement;
3082 bool regionview_x_movement;
3084 /* first_move is set to false if the regionview has been moved in the
3088 if (drag_info.first_move) {
3095 /* The regionview has been moved at some stage during the grab so we need
3096 to account for any mouse movement between this event and the last one.
3099 region_drag_motion_callback (item, event);
3101 if (drag_info.brushing) {
3102 /* all changes were made during motion event handlers */
3106 /* adjust for track speed */
3109 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3110 if (atv && atv->get_diskstream()) {
3111 speed = atv->get_diskstream()->speed();
3114 regionview_x_movement = (drag_info.last_frame_position != (jack_nframes_t) (rv->region.position()/speed));
3115 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3117 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3118 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3120 if (regionview_y_movement) {
3122 /* motion between tracks */
3124 list<AudioRegionView*> new_selection;
3126 /* moved to a different audio track. */
3128 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ) {
3130 AudioRegionView* rv2 = (*i);
3132 /* the region that used to be in the old playlist is not
3133 moved to the new one - we make a copy of it. as a result,
3134 any existing editor for the region should no longer be
3138 if (!drag_info.copy) {
3139 rv2->hide_region_editor();
3141 new_selection.push_back (rv2);
3145 /* first, freeze the target tracks */
3147 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3149 Playlist* from_playlist;
3150 Playlist* to_playlist;
3152 double ix1, ix2, iy1, iy2;
3154 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3155 (*i)->get_canvas_group()->i2w (ix1, iy1);
3156 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3157 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3159 from_playlist = (*i)->region.playlist();
3160 to_playlist = atv2->playlist();
3162 /* the from_playlist was frozen in the "first_move" case
3163 of the motion handler. the insert can fail,
3164 but that doesn't matter. it just means
3165 we already have the playlist in the list.
3168 motion_frozen_playlists.insert (from_playlist);
3170 /* only freeze the to_playlist once */
3172 insert_result = motion_frozen_playlists.insert(to_playlist);
3173 if (insert_result.second) {
3174 to_playlist->freeze();
3175 session->add_undo(to_playlist->get_memento());
3180 /* now do it again with the actual operations */
3182 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3184 Playlist* from_playlist;
3185 Playlist* to_playlist;
3187 double ix1, ix2, iy1, iy2;
3189 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3190 (*i)->get_canvas_group()->i2w (ix1, iy1);
3191 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3192 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3194 from_playlist = (*i)->region.playlist();
3195 to_playlist = atv2->playlist();
3197 latest_regionview = 0;
3199 where = (jack_nframes_t) (unit_to_frame (ix1) * speed);
3200 Region* new_region = createRegion ((*i)->region);
3202 from_playlist->remove_region (&((*i)->region));
3204 sigc::connection c = atv2->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3205 to_playlist->add_region (*new_region, where);
3208 if (latest_regionview) {
3209 selection->add (latest_regionview);
3215 /* motion within a single track */
3217 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3221 if (rv->region.locked()) {
3225 if (regionview_x_movement) {
3226 double ownspeed = 1.0;
3227 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3229 if (atv && atv->get_diskstream()) {
3230 ownspeed = atv->get_diskstream()->speed();
3233 /* base the new region position on the current position of the regionview.*/
3235 double ix1, ix2, iy1, iy2;
3237 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3238 rv->get_canvas_group()->i2w (ix1, iy1);
3239 where = (jack_nframes_t) (unit_to_frame (ix1) * ownspeed);
3243 where = rv->region.position();
3246 rv->get_time_axis_view().reveal_dependent_views (*rv);
3248 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3250 rv->region.set_position (where, (void *) this);
3255 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3257 session->add_redo_no_execute ((*p)->get_memento());
3260 motion_frozen_playlists.clear ();
3263 commit_reversible_command ();
3268 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3270 /* Either add to or set the set the region selection, unless
3271 this is an alignment click (control used)
3274 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3275 TimeAxisView* tv = &rv.get_time_axis_view();
3276 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3278 if (atv && atv->is_audio_track()) {
3279 speed = atv->get_diskstream()->speed();
3282 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3284 align_region (rv.region, SyncPoint, (jack_nframes_t) (edit_cursor->current_frame * speed));
3286 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3288 align_region (rv.region, End, (jack_nframes_t) (edit_cursor->current_frame * speed));
3292 align_region (rv.region, Start, (jack_nframes_t) (edit_cursor->current_frame * speed));
3298 Editor::show_verbose_time_cursor (jack_nframes_t frame, double offset, double xpos, double ypos)
3309 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3310 case AudioClock::BBT:
3311 session->bbt_time (frame, bbt);
3312 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3315 case AudioClock::SMPTE:
3316 session->smpte_time (frame, smpte);
3317 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3320 case AudioClock::MinSec:
3321 /* XXX fix this to compute min/sec properly */
3322 session->smpte_time (frame, smpte);
3323 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3324 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3328 snprintf (buf, sizeof(buf), "%u", frame);
3332 if (xpos >= 0 && ypos >=0) {
3333 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3336 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3338 show_verbose_canvas_cursor ();
3342 Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_nframes_t end, double offset, double xpos, double ypos)
3349 Meter meter_at_start(session->tempo_map().meter_at(start));
3355 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3356 case AudioClock::BBT:
3357 session->bbt_time (start, sbbt);
3358 session->bbt_time (end, ebbt);
3361 /* XXX this computation won't work well if the
3362 user makes a selection that spans any meter changes.
3365 ebbt.bars -= sbbt.bars;
3366 if (ebbt.beats >= sbbt.beats) {
3367 ebbt.beats -= sbbt.beats;
3370 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3372 if (ebbt.ticks >= sbbt.ticks) {
3373 ebbt.ticks -= sbbt.ticks;
3376 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3379 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3382 case AudioClock::SMPTE:
3383 session->smpte_duration (end - start, smpte);
3384 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3387 case AudioClock::MinSec:
3388 /* XXX fix this to compute min/sec properly */
3389 session->smpte_duration (end - start, smpte);
3390 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3391 snprintf (buf, sizeof (buf), "%02ld:%02ld:%.4f", smpte.hours, smpte.minutes, secs);
3395 snprintf (buf, sizeof(buf), "%u", end - start);
3399 if (xpos >= 0 && ypos >=0) {
3400 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3403 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3405 show_verbose_canvas_cursor ();
3409 Editor::collect_new_region_view (AudioRegionView* rv)
3411 latest_regionview = rv;
3415 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3417 if (clicked_regionview == 0) {
3421 /* lets try to create new Region for the selection */
3423 vector<AudioRegion*> new_regions;
3424 create_region_from_selection (new_regions);
3426 if (new_regions.empty()) {
3430 /* XXX fix me one day to use all new regions */
3432 Region* region = new_regions.front();
3434 /* add it to the current stream/playlist.
3436 tricky: the streamview for the track will add a new regionview. we will
3437 catch the signal it sends when it creates the regionview to
3438 set the regionview we want to then drag.
3441 latest_regionview = 0;
3442 sigc::connection c = clicked_audio_trackview->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3444 /* A selection grab currently creates two undo/redo operations, one for
3445 creating the new region and another for moving it.
3448 begin_reversible_command (_("selection grab"));
3450 Playlist* playlist = clicked_trackview->playlist();
3452 session->add_undo (playlist->get_memento ());
3453 clicked_trackview->playlist()->add_region (*region, selection->time[clicked_selection].start);
3454 session->add_redo_no_execute (playlist->get_memento ());
3456 commit_reversible_command ();
3460 if (latest_regionview == 0) {
3461 /* something went wrong */
3465 /* we need to deselect all other regionviews, and select this one
3466 i'm ignoring undo stuff, because the region creation will take care of it */
3467 selection->set (latest_regionview);
3469 drag_info.item = latest_regionview->get_canvas_group();
3470 drag_info.data = latest_regionview;
3471 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3472 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3476 drag_info.last_trackview = clicked_trackview;
3477 drag_info.last_frame_position = latest_regionview->region.position();
3478 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3480 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3484 Editor::cancel_selection ()
3486 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3487 (*i)->hide_selection ();
3489 selection->clear ();
3490 clicked_selection = 0;
3494 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3496 jack_nframes_t start = 0;
3497 jack_nframes_t end = 0;
3503 drag_info.item = item;
3504 drag_info.motion_callback = &Editor::drag_selection;
3505 drag_info.finished_callback = &Editor::end_selection_op;
3510 case CreateSelection:
3512 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3513 drag_info.copy = true;
3515 drag_info.copy = false;
3517 start_grab (event, selector_cursor);
3520 case SelectionStartTrim:
3521 clicked_trackview->order_selection_trims (item, true);
3522 start_grab (event, trimmer_cursor);
3523 start = selection->time[clicked_selection].start;
3524 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3527 case SelectionEndTrim:
3528 clicked_trackview->order_selection_trims (item, false);
3529 start_grab (event, trimmer_cursor);
3530 end = selection->time[clicked_selection].end;
3531 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3535 start = selection->time[clicked_selection].start;
3537 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3541 if (selection_op == SelectionMove) {
3542 show_verbose_time_cursor(start, 10);
3544 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3549 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3551 jack_nframes_t start = 0;
3552 jack_nframes_t end = 0;
3553 jack_nframes_t length;
3554 jack_nframes_t pending_position;
3556 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3557 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3560 pending_position = 0;
3563 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3564 snap_to (pending_position);
3567 /* only alter selection if the current frame is
3568 different from the last frame position (adjusted)
3571 if (pending_position == drag_info.last_pointer_frame) return;
3573 switch (selection_op) {
3574 case CreateSelection:
3576 if (drag_info.first_move) {
3577 snap_to (drag_info.grab_frame);
3580 if (pending_position < drag_info.grab_frame) {
3581 start = pending_position;
3582 end = drag_info.grab_frame;
3584 end = pending_position;
3585 start = drag_info.grab_frame;
3588 /* first drag: Either add to the selection
3589 or create a new selection->
3592 if (drag_info.first_move) {
3594 begin_reversible_command (_("range selection"));
3596 if (drag_info.copy) {
3597 /* adding to the selection */
3598 clicked_selection = selection->add (start, end);
3599 drag_info.copy = false;
3601 /* new selection-> */
3602 clicked_selection = selection->set (clicked_trackview, start, end);
3607 case SelectionStartTrim:
3609 if (drag_info.first_move) {
3610 begin_reversible_command (_("trim selection start"));
3613 start = selection->time[clicked_selection].start;
3614 end = selection->time[clicked_selection].end;
3616 if (pending_position > end) {
3619 start = pending_position;
3623 case SelectionEndTrim:
3625 if (drag_info.first_move) {
3626 begin_reversible_command (_("trim selection end"));
3629 start = selection->time[clicked_selection].start;
3630 end = selection->time[clicked_selection].end;
3632 if (pending_position < start) {
3635 end = pending_position;
3642 if (drag_info.first_move) {
3643 begin_reversible_command (_("move selection"));
3646 start = selection->time[clicked_selection].start;
3647 end = selection->time[clicked_selection].end;
3649 length = end - start;
3651 start = pending_position;
3654 end = start + length;
3659 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3660 start_canvas_autoscroll (1);
3664 selection->replace (clicked_selection, start, end);
3667 drag_info.last_pointer_frame = pending_position;
3668 drag_info.first_move = false;
3670 if (selection_op == SelectionMove) {
3671 show_verbose_time_cursor(start, 10);
3673 show_verbose_time_cursor(pending_position, 10);
3678 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3680 if (!drag_info.first_move) {
3681 drag_selection (item, event);
3682 /* XXX this is not object-oriented programming at all. ick */
3683 if (selection->time.consolidate()) {
3684 selection->TimeChanged ();
3686 commit_reversible_command ();
3688 /* just a click, no pointer movement.*/
3690 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3692 selection->clear_time();
3697 /* XXX what happens if its a music selection? */
3698 session->set_audio_range (selection->time);
3699 stop_canvas_autoscroll ();
3703 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3706 TimeAxisView* tvp = clicked_trackview;
3707 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3709 if (tv && tv->is_audio_track()) {
3710 speed = tv->get_diskstream()->speed();
3713 jack_nframes_t region_start = (jack_nframes_t) (clicked_regionview->region.position() / speed);
3714 jack_nframes_t region_end = (jack_nframes_t) (clicked_regionview->region.last_frame() / speed);
3715 jack_nframes_t region_length = (jack_nframes_t) (clicked_regionview->region.length() / speed);
3717 //drag_info.item = clicked_regionview->get_name_highlight();
3718 drag_info.item = item;
3719 drag_info.motion_callback = &Editor::trim_motion_callback;
3720 drag_info.finished_callback = &Editor::trim_finished_callback;
3722 start_grab (event, trimmer_cursor);
3724 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3725 trim_op = ContentsTrim;
3727 /* These will get overridden for a point trim.*/
3728 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3729 /* closer to start */
3730 trim_op = StartTrim;
3731 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3739 show_verbose_time_cursor(region_start, 10);
3742 show_verbose_time_cursor(region_end, 10);
3745 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3751 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3753 AudioRegionView* rv = clicked_regionview;
3754 jack_nframes_t frame_delta = 0;
3755 bool left_direction;
3756 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3758 /* snap modifier works differently here..
3759 its' current state has to be passed to the
3760 various trim functions in order to work properly
3764 TimeAxisView* tvp = clicked_trackview;
3765 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3767 if (tv && tv->is_audio_track()) {
3768 speed = tv->get_diskstream()->speed();
3771 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3772 left_direction = true;
3774 left_direction = false;
3778 snap_to (drag_info.current_pointer_frame);
3781 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3785 if (drag_info.first_move) {
3791 trim_type = "Region start trim";
3794 trim_type = "Region end trim";
3797 trim_type = "Region content trim";
3801 begin_reversible_command (trim_type);
3803 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3804 (*i)->region.freeze ();
3805 (*i)->temporarily_hide_envelope ();
3806 session->add_undo ((*i)->region.playlist()->get_memento());
3810 if (left_direction) {
3811 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
3813 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
3818 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region.first_frame()/speed)) {
3821 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3822 single_start_trim (**i, frame_delta, left_direction, obey_snap);
3828 if ((left_direction == true) && (drag_info.current_pointer_frame > (jack_nframes_t) (rv->region.last_frame()/speed))) {
3831 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3832 single_end_trim (**i, frame_delta, left_direction, obey_snap);
3839 bool swap_direction = false;
3841 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3842 swap_direction = true;
3845 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
3846 i != selection->audio_regions.by_layer().end(); ++i)
3848 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
3856 show_verbose_time_cursor((jack_nframes_t) (rv->region.position()/speed), 10);
3859 show_verbose_time_cursor((jack_nframes_t) (rv->region.last_frame()/speed), 10);
3862 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3866 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
3867 drag_info.first_move = false;
3871 Editor::single_contents_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
3873 Region& region (rv.region);
3875 if (region.locked()) {
3879 jack_nframes_t new_bound;
3882 TimeAxisView* tvp = clicked_trackview;
3883 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3885 if (tv && tv->is_audio_track()) {
3886 speed = tv->get_diskstream()->speed();
3889 if (left_direction) {
3890 if (swap_direction) {
3891 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
3893 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
3896 if (swap_direction) {
3897 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
3899 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
3904 snap_to (new_bound);
3906 region.trim_start ((jack_nframes_t) (new_bound * speed), this);
3907 rv.region_changed (StartChanged);
3911 Editor::single_start_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
3913 Region& region (rv.region);
3915 if (region.locked()) {
3919 jack_nframes_t new_bound;
3922 TimeAxisView* tvp = clicked_trackview;
3923 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3925 if (tv && tv->is_audio_track()) {
3926 speed = tv->get_diskstream()->speed();
3929 if (left_direction) {
3930 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
3932 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
3936 snap_to (new_bound, (left_direction ? 0 : 1));
3939 region.trim_front ((jack_nframes_t) (new_bound * speed), this);
3941 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
3945 Editor::single_end_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
3947 Region& region (rv.region);
3949 if (region.locked()) {
3953 jack_nframes_t new_bound;
3956 TimeAxisView* tvp = clicked_trackview;
3957 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3959 if (tv && tv->is_audio_track()) {
3960 speed = tv->get_diskstream()->speed();
3963 if (left_direction) {
3964 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) - frame_delta;
3966 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) + frame_delta;
3970 snap_to (new_bound);
3972 region.trim_end ((jack_nframes_t) (new_bound * speed), this);
3973 rv.region_changed (LengthChanged);
3977 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3979 if (!drag_info.first_move) {
3980 trim_motion_callback (item, event);
3982 if (!clicked_regionview->get_selected()) {
3983 thaw_region_after_trim (*clicked_regionview);
3986 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
3987 i != selection->audio_regions.by_layer().end(); ++i)
3989 thaw_region_after_trim (**i);
3992 commit_reversible_command();
3994 /* no mouse movement */
4000 Editor::point_trim (GdkEvent* event)
4002 AudioRegionView* rv = clicked_regionview;
4003 jack_nframes_t new_bound = drag_info.current_pointer_frame;
4005 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4006 snap_to (new_bound);
4009 /* Choose action dependant on which button was pressed */
4010 switch (event->button.button) {
4012 trim_op = StartTrim;
4013 begin_reversible_command (_("Start point trim"));
4015 if (rv->get_selected()) {
4017 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4018 i != selection->audio_regions.by_layer().end(); ++i)
4020 if (!(*i)->region.locked()) {
4021 session->add_undo ((*i)->region.playlist()->get_memento());
4022 (*i)->region.trim_front (new_bound, this);
4023 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4029 if (!rv->region.locked()) {
4030 session->add_undo (rv->region.playlist()->get_memento());
4031 rv->region.trim_front (new_bound, this);
4032 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4036 commit_reversible_command();
4041 begin_reversible_command (_("End point trim"));
4043 if (rv->get_selected()) {
4045 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i)
4047 if (!(*i)->region.locked()) {
4048 session->add_undo ((*i)->region.playlist()->get_memento());
4049 (*i)->region.trim_end (new_bound, this);
4050 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4056 if (!rv->region.locked()) {
4057 session->add_undo (rv->region.playlist()->get_memento());
4058 rv->region.trim_end (new_bound, this);
4059 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4063 commit_reversible_command();
4072 Editor::thaw_region_after_trim (AudioRegionView& rv)
4074 Region& region (rv.region);
4076 if (region.locked()) {
4080 region.thaw (_("trimmed region"));
4081 session->add_redo_no_execute (region.playlist()->get_memento());
4083 rv.unhide_envelope ();
4087 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4092 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4093 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4097 Location* location = find_location_from_marker (marker, is_start);
4098 location->set_hidden (true, this);
4103 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4110 drag_info.item = item;
4111 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4112 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4114 range_marker_op = op;
4116 if (!temp_location) {
4117 temp_location = new Location;
4121 case CreateRangeMarker:
4122 case CreateTransportMarker:
4124 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4125 drag_info.copy = true;
4127 drag_info.copy = false;
4129 start_grab (event, selector_cursor);
4133 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4138 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4140 jack_nframes_t start = 0;
4141 jack_nframes_t end = 0;
4142 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4144 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4145 snap_to (drag_info.current_pointer_frame);
4148 /* only alter selection if the current frame is
4149 different from the last frame position.
4152 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4154 switch (range_marker_op) {
4155 case CreateRangeMarker:
4156 case CreateTransportMarker:
4157 if (drag_info.first_move) {
4158 snap_to (drag_info.grab_frame);
4161 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4162 start = drag_info.current_pointer_frame;
4163 end = drag_info.grab_frame;
4165 end = drag_info.current_pointer_frame;
4166 start = drag_info.grab_frame;
4169 /* first drag: Either add to the selection
4170 or create a new selection->
4173 if (drag_info.first_move) {
4175 temp_location->set (start, end);
4179 update_marker_drag_item (temp_location);
4180 range_marker_drag_rect->show();
4181 range_marker_drag_rect->raise_to_top();
4187 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4188 start_canvas_autoscroll (1);
4192 temp_location->set (start, end);
4194 double x1 = frame_to_pixel (start);
4195 double x2 = frame_to_pixel (end);
4196 crect->property_x1() = x1;
4197 crect->property_x2() = x2;
4199 update_marker_drag_item (temp_location);
4202 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4203 drag_info.first_move = false;
4205 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4210 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4212 Location * newloc = 0;
4214 if (!drag_info.first_move) {
4215 drag_range_markerbar_op (item, event);
4217 switch (range_marker_op) {
4218 case CreateRangeMarker:
4219 begin_reversible_command (_("new range marker"));
4220 session->add_undo (session->locations()->get_memento());
4221 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed");
4222 session->locations()->add (newloc, true);
4223 session->add_redo_no_execute (session->locations()->get_memento());
4224 commit_reversible_command ();
4226 range_bar_drag_rect->hide();
4227 range_marker_drag_rect->hide();
4230 case CreateTransportMarker:
4231 // popup menu to pick loop or punch
4232 new_transport_marker_context_menu (&event->button, item);
4237 /* just a click, no pointer movement.*/
4239 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4246 stop_canvas_autoscroll ();
4252 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4254 drag_info.item = item;
4255 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4256 drag_info.finished_callback = &Editor::end_mouse_zoom;
4258 start_grab (event, zoom_cursor);
4260 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4264 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4266 jack_nframes_t start;
4269 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4270 snap_to (drag_info.current_pointer_frame);
4272 if (drag_info.first_move) {
4273 snap_to (drag_info.grab_frame);
4277 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4279 /* base start and end on initial click position */
4280 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4281 start = drag_info.current_pointer_frame;
4282 end = drag_info.grab_frame;
4284 end = drag_info.current_pointer_frame;
4285 start = drag_info.grab_frame;
4290 if (drag_info.first_move) {
4292 zoom_rect->raise_to_top();
4295 reposition_zoom_rect(start, end);
4297 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4298 drag_info.first_move = false;
4300 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4305 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4307 if (!drag_info.first_move) {
4308 drag_mouse_zoom (item, event);
4310 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4311 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4313 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4316 temporal_zoom_to_frame (false, drag_info.grab_frame);
4318 temporal_zoom_step (false);
4319 center_screen (drag_info.grab_frame);
4327 Editor::reposition_zoom_rect (jack_nframes_t start, jack_nframes_t end)
4329 double x1 = frame_to_pixel (start);
4330 double x2 = frame_to_pixel (end);
4331 double y2 = canvas_height - 2;
4333 zoom_rect->property_x1() = x1;
4334 zoom_rect->property_y1() = 1.0;
4335 zoom_rect->property_x2() = x2;
4336 zoom_rect->property_y2() = y2;
4340 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4342 drag_info.item = item;
4343 drag_info.motion_callback = &Editor::drag_rubberband_select;
4344 drag_info.finished_callback = &Editor::end_rubberband_select;
4346 start_grab (event, cross_hair_cursor);
4348 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4352 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4354 jack_nframes_t start;
4359 /* use a bigger drag threshold than the default */
4361 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4365 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4366 // snap_to (drag_info.current_pointer_frame);
4368 // if (drag_info.first_move) {
4369 // snap_to (drag_info.grab_frame);
4374 /* base start and end on initial click position */
4375 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4376 start = drag_info.current_pointer_frame;
4377 end = drag_info.grab_frame;
4379 end = drag_info.current_pointer_frame;
4380 start = drag_info.grab_frame;
4383 if (drag_info.current_pointer_y < drag_info.grab_y) {
4384 y1 = drag_info.current_pointer_y;
4385 y2 = drag_info.grab_y;
4388 y2 = drag_info.current_pointer_y;
4389 y1 = drag_info.grab_y;
4393 if (start != end || y1 != y2) {
4395 double x1 = frame_to_pixel (start);
4396 double x2 = frame_to_pixel (end);
4398 rubberband_rect->property_x1() = x1;
4399 rubberband_rect->property_y1() = y1;
4400 rubberband_rect->property_x2() = x2;
4401 rubberband_rect->property_y2() = y2;
4403 rubberband_rect->show();
4404 rubberband_rect->raise_to_top();
4406 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4407 drag_info.first_move = false;
4409 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4414 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4416 if (!drag_info.first_move) {
4418 drag_rubberband_select (item, event);
4421 if (drag_info.current_pointer_y < drag_info.grab_y) {
4422 y1 = drag_info.current_pointer_y;
4423 y2 = drag_info.grab_y;
4426 y2 = drag_info.current_pointer_y;
4427 y1 = drag_info.grab_y;
4431 bool add = Keyboard::modifier_state_contains (event->button.state, Keyboard::Shift);
4434 begin_reversible_command (_("select regions"));
4436 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4437 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, add);
4439 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, add);
4443 commit_reversible_command ();
4447 selection->clear_audio_regions();
4448 selection->clear_points ();
4449 selection->clear_lines ();
4452 rubberband_rect->hide();
4457 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4459 using namespace Gtkmm2ext;
4461 ArdourPrompter prompter (false);
4463 prompter.set_prompt (_("Name for region:"));
4464 prompter.set_initial_text (clicked_regionview->region.name());
4465 prompter.show_all ();
4466 switch (prompter.run ()) {
4467 case GTK_RESPONSE_ACCEPT:
4469 prompter.get_result(str);
4472 clicked_regionview->region.set_name (str);
4480 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4482 drag_info.item = item;
4483 drag_info.motion_callback = &Editor::time_fx_motion;
4484 drag_info.finished_callback = &Editor::end_time_fx;
4488 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4492 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4494 AudioRegionView* rv = clicked_regionview;
4496 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4497 snap_to (drag_info.current_pointer_frame);
4500 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4504 if (drag_info.current_pointer_frame > rv->region.position()) {
4505 rv->get_time_axis_view().show_timestretch (rv->region.position(), drag_info.current_pointer_frame);
4508 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4509 drag_info.first_move = false;
4511 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4515 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4517 clicked_regionview->get_time_axis_view().hide_timestretch ();
4519 if (drag_info.first_move) {
4523 jack_nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region.position();
4524 float percentage = (float) ((double) newlen - (double) clicked_regionview->region.length()) / ((double) newlen) * 100.0f;
4526 begin_reversible_command (_("timestretch"));
4528 if (run_timestretch (selection->audio_regions, percentage) == 0) {
4529 session->commit_reversible_command ();
4534 Editor::mouse_brush_insert_region (AudioRegionView* rv, jack_nframes_t pos)
4536 /* no brushing without a useful snap setting */
4538 switch (snap_mode) {
4540 return; /* can't work because it allows region to be placed anywhere */
4545 switch (snap_type) {
4548 case SnapToEditCursor:
4555 /* don't brush a copy over the original */
4557 if (pos == rv->region.position()) {
4561 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
4563 if (atv == 0 || !atv->is_audio_track()) {
4567 Playlist* playlist = atv->playlist();
4568 double speed = atv->get_diskstream()->speed();
4570 session->add_undo (playlist->get_memento());
4571 playlist->add_region (*(new AudioRegion (rv->region)), (jack_nframes_t) (pos * speed));
4572 session->add_redo_no_execute (playlist->get_memento());
4574 // playlist is frozen, so we have to update manually
4576 playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
4580 Editor::track_height_step_timeout ()
4583 struct timeval delta;
4585 gettimeofday (&now, 0);
4586 timersub (&now, &last_track_height_step_timestamp, &delta);
4588 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4589 current_stepping_trackview = 0;