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 session->add_undo (rv->region.playlist()->get_memento());
3249 rv->region.set_position (where, (void *) this);
3254 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3256 session->add_redo_no_execute ((*p)->get_memento());
3259 motion_frozen_playlists.clear ();
3262 commit_reversible_command ();
3267 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3269 /* Either add to or set the set the region selection, unless
3270 this is an alignment click (control used)
3273 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3274 TimeAxisView* tv = &rv.get_time_axis_view();
3275 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3277 if (atv && atv->is_audio_track()) {
3278 speed = atv->get_diskstream()->speed();
3281 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3283 align_region (rv.region, SyncPoint, (jack_nframes_t) (edit_cursor->current_frame * speed));
3285 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3287 align_region (rv.region, End, (jack_nframes_t) (edit_cursor->current_frame * speed));
3291 align_region (rv.region, Start, (jack_nframes_t) (edit_cursor->current_frame * speed));
3297 Editor::show_verbose_time_cursor (jack_nframes_t frame, double offset, double xpos, double ypos)
3308 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3309 case AudioClock::BBT:
3310 session->bbt_time (frame, bbt);
3311 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3314 case AudioClock::SMPTE:
3315 session->smpte_time (frame, smpte);
3316 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3319 case AudioClock::MinSec:
3320 /* XXX fix this to compute min/sec properly */
3321 session->smpte_time (frame, smpte);
3322 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3323 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3327 snprintf (buf, sizeof(buf), "%u", frame);
3331 if (xpos >= 0 && ypos >=0) {
3332 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3335 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3337 show_verbose_canvas_cursor ();
3341 Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_nframes_t end, double offset, double xpos, double ypos)
3348 Meter meter_at_start(session->tempo_map().meter_at(start));
3354 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3355 case AudioClock::BBT:
3356 session->bbt_time (start, sbbt);
3357 session->bbt_time (end, ebbt);
3360 /* XXX this computation won't work well if the
3361 user makes a selection that spans any meter changes.
3364 ebbt.bars -= sbbt.bars;
3365 if (ebbt.beats >= sbbt.beats) {
3366 ebbt.beats -= sbbt.beats;
3369 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3371 if (ebbt.ticks >= sbbt.ticks) {
3372 ebbt.ticks -= sbbt.ticks;
3375 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3378 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3381 case AudioClock::SMPTE:
3382 session->smpte_duration (end - start, smpte);
3383 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3386 case AudioClock::MinSec:
3387 /* XXX fix this to compute min/sec properly */
3388 session->smpte_duration (end - start, smpte);
3389 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3390 snprintf (buf, sizeof (buf), "%02ld:%02ld:%.4f", smpte.hours, smpte.minutes, secs);
3394 snprintf (buf, sizeof(buf), "%u", end - start);
3398 if (xpos >= 0 && ypos >=0) {
3399 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3402 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3404 show_verbose_canvas_cursor ();
3408 Editor::collect_new_region_view (AudioRegionView* rv)
3410 latest_regionview = rv;
3414 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3416 if (clicked_regionview == 0) {
3420 /* lets try to create new Region for the selection */
3422 vector<AudioRegion*> new_regions;
3423 create_region_from_selection (new_regions);
3425 if (new_regions.empty()) {
3429 /* XXX fix me one day to use all new regions */
3431 Region* region = new_regions.front();
3433 /* add it to the current stream/playlist.
3435 tricky: the streamview for the track will add a new regionview. we will
3436 catch the signal it sends when it creates the regionview to
3437 set the regionview we want to then drag.
3440 latest_regionview = 0;
3441 sigc::connection c = clicked_audio_trackview->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3443 /* A selection grab currently creates two undo/redo operations, one for
3444 creating the new region and another for moving it.
3447 begin_reversible_command (_("selection grab"));
3449 Playlist* playlist = clicked_trackview->playlist();
3451 session->add_undo (playlist->get_memento ());
3452 clicked_trackview->playlist()->add_region (*region, selection->time[clicked_selection].start);
3453 session->add_redo_no_execute (playlist->get_memento ());
3455 commit_reversible_command ();
3459 if (latest_regionview == 0) {
3460 /* something went wrong */
3464 /* we need to deselect all other regionviews, and select this one
3465 i'm ignoring undo stuff, because the region creation will take care of it */
3466 selection->set (latest_regionview);
3468 drag_info.item = latest_regionview->get_canvas_group();
3469 drag_info.data = latest_regionview;
3470 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3471 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3475 drag_info.last_trackview = clicked_trackview;
3476 drag_info.last_frame_position = latest_regionview->region.position();
3477 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3479 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3483 Editor::cancel_selection ()
3485 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3486 (*i)->hide_selection ();
3488 selection->clear ();
3489 clicked_selection = 0;
3493 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3495 jack_nframes_t start = 0;
3496 jack_nframes_t end = 0;
3502 drag_info.item = item;
3503 drag_info.motion_callback = &Editor::drag_selection;
3504 drag_info.finished_callback = &Editor::end_selection_op;
3509 case CreateSelection:
3511 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3512 drag_info.copy = true;
3514 drag_info.copy = false;
3516 start_grab (event, selector_cursor);
3519 case SelectionStartTrim:
3520 clicked_trackview->order_selection_trims (item, true);
3521 start_grab (event, trimmer_cursor);
3522 start = selection->time[clicked_selection].start;
3523 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3526 case SelectionEndTrim:
3527 clicked_trackview->order_selection_trims (item, false);
3528 start_grab (event, trimmer_cursor);
3529 end = selection->time[clicked_selection].end;
3530 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3534 start = selection->time[clicked_selection].start;
3536 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3540 if (selection_op == SelectionMove) {
3541 show_verbose_time_cursor(start, 10);
3543 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3548 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3550 jack_nframes_t start = 0;
3551 jack_nframes_t end = 0;
3552 jack_nframes_t length;
3553 jack_nframes_t pending_position;
3555 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3556 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3559 pending_position = 0;
3562 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3563 snap_to (pending_position);
3566 /* only alter selection if the current frame is
3567 different from the last frame position (adjusted)
3570 if (pending_position == drag_info.last_pointer_frame) return;
3572 switch (selection_op) {
3573 case CreateSelection:
3575 if (drag_info.first_move) {
3576 snap_to (drag_info.grab_frame);
3579 if (pending_position < drag_info.grab_frame) {
3580 start = pending_position;
3581 end = drag_info.grab_frame;
3583 end = pending_position;
3584 start = drag_info.grab_frame;
3587 /* first drag: Either add to the selection
3588 or create a new selection->
3591 if (drag_info.first_move) {
3593 begin_reversible_command (_("range selection"));
3595 if (drag_info.copy) {
3596 /* adding to the selection */
3597 clicked_selection = selection->add (start, end);
3598 drag_info.copy = false;
3600 /* new selection-> */
3601 clicked_selection = selection->set (clicked_trackview, start, end);
3606 case SelectionStartTrim:
3608 if (drag_info.first_move) {
3609 begin_reversible_command (_("trim selection start"));
3612 start = selection->time[clicked_selection].start;
3613 end = selection->time[clicked_selection].end;
3615 if (pending_position > end) {
3618 start = pending_position;
3622 case SelectionEndTrim:
3624 if (drag_info.first_move) {
3625 begin_reversible_command (_("trim selection end"));
3628 start = selection->time[clicked_selection].start;
3629 end = selection->time[clicked_selection].end;
3631 if (pending_position < start) {
3634 end = pending_position;
3641 if (drag_info.first_move) {
3642 begin_reversible_command (_("move selection"));
3645 start = selection->time[clicked_selection].start;
3646 end = selection->time[clicked_selection].end;
3648 length = end - start;
3650 start = pending_position;
3653 end = start + length;
3658 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3659 start_canvas_autoscroll (1);
3663 selection->replace (clicked_selection, start, end);
3666 drag_info.last_pointer_frame = pending_position;
3667 drag_info.first_move = false;
3669 if (selection_op == SelectionMove) {
3670 show_verbose_time_cursor(start, 10);
3672 show_verbose_time_cursor(pending_position, 10);
3677 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3679 if (!drag_info.first_move) {
3680 drag_selection (item, event);
3681 /* XXX this is not object-oriented programming at all. ick */
3682 if (selection->time.consolidate()) {
3683 selection->TimeChanged ();
3685 commit_reversible_command ();
3687 /* just a click, no pointer movement.*/
3689 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3691 selection->clear_time();
3696 /* XXX what happens if its a music selection? */
3697 session->set_audio_range (selection->time);
3698 stop_canvas_autoscroll ();
3702 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3705 TimeAxisView* tvp = clicked_trackview;
3706 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3708 if (tv && tv->is_audio_track()) {
3709 speed = tv->get_diskstream()->speed();
3712 jack_nframes_t region_start = (jack_nframes_t) (clicked_regionview->region.position() / speed);
3713 jack_nframes_t region_end = (jack_nframes_t) (clicked_regionview->region.last_frame() / speed);
3714 jack_nframes_t region_length = (jack_nframes_t) (clicked_regionview->region.length() / speed);
3716 //drag_info.item = clicked_regionview->get_name_highlight();
3717 drag_info.item = item;
3718 drag_info.motion_callback = &Editor::trim_motion_callback;
3719 drag_info.finished_callback = &Editor::trim_finished_callback;
3721 start_grab (event, trimmer_cursor);
3723 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3724 trim_op = ContentsTrim;
3726 /* These will get overridden for a point trim.*/
3727 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3728 /* closer to start */
3729 trim_op = StartTrim;
3730 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3738 show_verbose_time_cursor(region_start, 10);
3741 show_verbose_time_cursor(region_end, 10);
3744 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3750 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3752 AudioRegionView* rv = clicked_regionview;
3753 jack_nframes_t frame_delta = 0;
3754 bool left_direction;
3755 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3757 /* snap modifier works differently here..
3758 its' current state has to be passed to the
3759 various trim functions in order to work properly
3763 TimeAxisView* tvp = clicked_trackview;
3764 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3766 if (tv && tv->is_audio_track()) {
3767 speed = tv->get_diskstream()->speed();
3770 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3771 left_direction = true;
3773 left_direction = false;
3777 snap_to (drag_info.current_pointer_frame);
3780 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3784 if (drag_info.first_move) {
3790 trim_type = "Region start trim";
3793 trim_type = "Region end trim";
3796 trim_type = "Region content trim";
3800 begin_reversible_command (trim_type);
3802 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3803 (*i)->region.freeze ();
3804 (*i)->temporarily_hide_envelope ();
3805 session->add_undo ((*i)->region.playlist()->get_memento());
3809 if (left_direction) {
3810 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
3812 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
3817 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region.first_frame()/speed)) {
3820 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3821 single_start_trim (**i, frame_delta, left_direction, obey_snap);
3827 if ((left_direction == true) && (drag_info.current_pointer_frame > (jack_nframes_t) (rv->region.last_frame()/speed))) {
3830 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3831 single_end_trim (**i, frame_delta, left_direction, obey_snap);
3838 bool swap_direction = false;
3840 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3841 swap_direction = true;
3844 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
3845 i != selection->audio_regions.by_layer().end(); ++i)
3847 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
3855 show_verbose_time_cursor((jack_nframes_t) (rv->region.position()/speed), 10);
3858 show_verbose_time_cursor((jack_nframes_t) (rv->region.last_frame()/speed), 10);
3861 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3865 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
3866 drag_info.first_move = false;
3870 Editor::single_contents_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
3872 Region& region (rv.region);
3874 if (region.locked()) {
3878 jack_nframes_t new_bound;
3881 TimeAxisView* tvp = clicked_trackview;
3882 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3884 if (tv && tv->is_audio_track()) {
3885 speed = tv->get_diskstream()->speed();
3888 if (left_direction) {
3889 if (swap_direction) {
3890 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
3892 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
3895 if (swap_direction) {
3896 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
3898 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
3903 snap_to (new_bound);
3905 region.trim_start ((jack_nframes_t) (new_bound * speed), this);
3906 rv.region_changed (StartChanged);
3910 Editor::single_start_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
3912 Region& region (rv.region);
3914 if (region.locked()) {
3918 jack_nframes_t new_bound;
3921 TimeAxisView* tvp = clicked_trackview;
3922 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3924 if (tv && tv->is_audio_track()) {
3925 speed = tv->get_diskstream()->speed();
3928 if (left_direction) {
3929 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
3931 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
3935 snap_to (new_bound, (left_direction ? 0 : 1));
3938 region.trim_front ((jack_nframes_t) (new_bound * speed), this);
3940 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
3944 Editor::single_end_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
3946 Region& region (rv.region);
3948 if (region.locked()) {
3952 jack_nframes_t new_bound;
3955 TimeAxisView* tvp = clicked_trackview;
3956 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3958 if (tv && tv->is_audio_track()) {
3959 speed = tv->get_diskstream()->speed();
3962 if (left_direction) {
3963 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) - frame_delta;
3965 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) + frame_delta;
3969 snap_to (new_bound);
3971 region.trim_end ((jack_nframes_t) (new_bound * speed), this);
3972 rv.region_changed (LengthChanged);
3976 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3978 if (!drag_info.first_move) {
3979 trim_motion_callback (item, event);
3981 if (!clicked_regionview->get_selected()) {
3982 thaw_region_after_trim (*clicked_regionview);
3985 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
3986 i != selection->audio_regions.by_layer().end(); ++i)
3988 thaw_region_after_trim (**i);
3991 commit_reversible_command();
3993 /* no mouse movement */
3999 Editor::point_trim (GdkEvent* event)
4001 AudioRegionView* rv = clicked_regionview;
4002 jack_nframes_t new_bound = drag_info.current_pointer_frame;
4004 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4005 snap_to (new_bound);
4008 /* Choose action dependant on which button was pressed */
4009 switch (event->button.button) {
4011 trim_op = StartTrim;
4012 begin_reversible_command (_("Start point trim"));
4014 if (rv->get_selected()) {
4016 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4017 i != selection->audio_regions.by_layer().end(); ++i)
4019 if (!(*i)->region.locked()) {
4020 session->add_undo ((*i)->region.playlist()->get_memento());
4021 (*i)->region.trim_front (new_bound, this);
4022 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4028 if (!rv->region.locked()) {
4029 session->add_undo (rv->region.playlist()->get_memento());
4030 rv->region.trim_front (new_bound, this);
4031 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4035 commit_reversible_command();
4040 begin_reversible_command (_("End point trim"));
4042 if (rv->get_selected()) {
4044 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i)
4046 if (!(*i)->region.locked()) {
4047 session->add_undo ((*i)->region.playlist()->get_memento());
4048 (*i)->region.trim_end (new_bound, this);
4049 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4055 if (!rv->region.locked()) {
4056 session->add_undo (rv->region.playlist()->get_memento());
4057 rv->region.trim_end (new_bound, this);
4058 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4062 commit_reversible_command();
4071 Editor::thaw_region_after_trim (AudioRegionView& rv)
4073 Region& region (rv.region);
4075 if (region.locked()) {
4079 region.thaw (_("trimmed region"));
4080 session->add_redo_no_execute (region.playlist()->get_memento());
4082 rv.unhide_envelope ();
4086 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4091 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4092 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4096 Location* location = find_location_from_marker (marker, is_start);
4097 location->set_hidden (true, this);
4102 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4109 drag_info.item = item;
4110 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4111 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4113 range_marker_op = op;
4115 if (!temp_location) {
4116 temp_location = new Location;
4120 case CreateRangeMarker:
4121 case CreateTransportMarker:
4123 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4124 drag_info.copy = true;
4126 drag_info.copy = false;
4128 start_grab (event, selector_cursor);
4132 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4137 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4139 jack_nframes_t start = 0;
4140 jack_nframes_t end = 0;
4141 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4143 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4144 snap_to (drag_info.current_pointer_frame);
4147 /* only alter selection if the current frame is
4148 different from the last frame position.
4151 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4153 switch (range_marker_op) {
4154 case CreateRangeMarker:
4155 case CreateTransportMarker:
4156 if (drag_info.first_move) {
4157 snap_to (drag_info.grab_frame);
4160 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4161 start = drag_info.current_pointer_frame;
4162 end = drag_info.grab_frame;
4164 end = drag_info.current_pointer_frame;
4165 start = drag_info.grab_frame;
4168 /* first drag: Either add to the selection
4169 or create a new selection->
4172 if (drag_info.first_move) {
4174 temp_location->set (start, end);
4178 update_marker_drag_item (temp_location);
4179 range_marker_drag_rect->show();
4180 range_marker_drag_rect->raise_to_top();
4186 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4187 start_canvas_autoscroll (1);
4191 temp_location->set (start, end);
4193 double x1 = frame_to_pixel (start);
4194 double x2 = frame_to_pixel (end);
4195 crect->property_x1() = x1;
4196 crect->property_x2() = x2;
4198 update_marker_drag_item (temp_location);
4201 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4202 drag_info.first_move = false;
4204 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4209 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4211 Location * newloc = 0;
4213 if (!drag_info.first_move) {
4214 drag_range_markerbar_op (item, event);
4216 switch (range_marker_op) {
4217 case CreateRangeMarker:
4218 begin_reversible_command (_("new range marker"));
4219 session->add_undo (session->locations()->get_memento());
4220 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed");
4221 session->locations()->add (newloc, true);
4222 session->add_redo_no_execute (session->locations()->get_memento());
4223 commit_reversible_command ();
4225 range_bar_drag_rect->hide();
4226 range_marker_drag_rect->hide();
4229 case CreateTransportMarker:
4230 // popup menu to pick loop or punch
4231 new_transport_marker_context_menu (&event->button, item);
4236 /* just a click, no pointer movement.*/
4238 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4245 stop_canvas_autoscroll ();
4251 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4253 drag_info.item = item;
4254 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4255 drag_info.finished_callback = &Editor::end_mouse_zoom;
4257 start_grab (event, zoom_cursor);
4259 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4263 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4265 jack_nframes_t start;
4268 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4269 snap_to (drag_info.current_pointer_frame);
4271 if (drag_info.first_move) {
4272 snap_to (drag_info.grab_frame);
4276 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4278 /* base start and end on initial click position */
4279 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4280 start = drag_info.current_pointer_frame;
4281 end = drag_info.grab_frame;
4283 end = drag_info.current_pointer_frame;
4284 start = drag_info.grab_frame;
4289 if (drag_info.first_move) {
4291 zoom_rect->raise_to_top();
4294 reposition_zoom_rect(start, end);
4296 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4297 drag_info.first_move = false;
4299 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4304 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4306 if (!drag_info.first_move) {
4307 drag_mouse_zoom (item, event);
4309 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4310 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4312 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4315 temporal_zoom_to_frame (false, drag_info.grab_frame);
4317 temporal_zoom_step (false);
4318 center_screen (drag_info.grab_frame);
4326 Editor::reposition_zoom_rect (jack_nframes_t start, jack_nframes_t end)
4328 double x1 = frame_to_pixel (start);
4329 double x2 = frame_to_pixel (end);
4330 double y2 = canvas_height - 2;
4332 zoom_rect->property_x1() = x1;
4333 zoom_rect->property_y1() = 1.0;
4334 zoom_rect->property_x2() = x2;
4335 zoom_rect->property_y2() = y2;
4339 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4341 drag_info.item = item;
4342 drag_info.motion_callback = &Editor::drag_rubberband_select;
4343 drag_info.finished_callback = &Editor::end_rubberband_select;
4345 start_grab (event, cross_hair_cursor);
4347 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4351 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4353 jack_nframes_t start;
4358 /* use a bigger drag threshold than the default */
4360 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4364 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4365 // snap_to (drag_info.current_pointer_frame);
4367 // if (drag_info.first_move) {
4368 // snap_to (drag_info.grab_frame);
4373 /* base start and end on initial click position */
4374 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4375 start = drag_info.current_pointer_frame;
4376 end = drag_info.grab_frame;
4378 end = drag_info.current_pointer_frame;
4379 start = drag_info.grab_frame;
4382 if (drag_info.current_pointer_y < drag_info.grab_y) {
4383 y1 = drag_info.current_pointer_y;
4384 y2 = drag_info.grab_y;
4387 y2 = drag_info.current_pointer_y;
4388 y1 = drag_info.grab_y;
4392 if (start != end || y1 != y2) {
4394 double x1 = frame_to_pixel (start);
4395 double x2 = frame_to_pixel (end);
4397 rubberband_rect->property_x1() = x1;
4398 rubberband_rect->property_y1() = y1;
4399 rubberband_rect->property_x2() = x2;
4400 rubberband_rect->property_y2() = y2;
4402 rubberband_rect->show();
4403 rubberband_rect->raise_to_top();
4405 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4406 drag_info.first_move = false;
4408 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4413 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4415 if (!drag_info.first_move) {
4417 drag_rubberband_select (item, event);
4420 if (drag_info.current_pointer_y < drag_info.grab_y) {
4421 y1 = drag_info.current_pointer_y;
4422 y2 = drag_info.grab_y;
4425 y2 = drag_info.current_pointer_y;
4426 y1 = drag_info.grab_y;
4430 bool add = Keyboard::modifier_state_contains (event->button.state, Keyboard::Shift);
4433 begin_reversible_command (_("select regions"));
4435 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4436 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, add);
4438 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, add);
4442 commit_reversible_command ();
4446 selection->clear_audio_regions();
4447 selection->clear_points ();
4448 selection->clear_lines ();
4451 rubberband_rect->hide();
4456 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4458 using namespace Gtkmm2ext;
4460 ArdourPrompter prompter (false);
4462 prompter.set_prompt (_("Name for region:"));
4463 prompter.set_initial_text (clicked_regionview->region.name());
4464 prompter.show_all ();
4465 switch (prompter.run ()) {
4466 case GTK_RESPONSE_ACCEPT:
4468 prompter.get_result(str);
4471 clicked_regionview->region.set_name (str);
4479 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4481 drag_info.item = item;
4482 drag_info.motion_callback = &Editor::time_fx_motion;
4483 drag_info.finished_callback = &Editor::end_time_fx;
4487 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4491 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4493 AudioRegionView* rv = clicked_regionview;
4495 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4496 snap_to (drag_info.current_pointer_frame);
4499 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4503 if (drag_info.current_pointer_frame > rv->region.position()) {
4504 rv->get_time_axis_view().show_timestretch (rv->region.position(), drag_info.current_pointer_frame);
4507 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4508 drag_info.first_move = false;
4510 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4514 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4516 clicked_regionview->get_time_axis_view().hide_timestretch ();
4518 if (drag_info.first_move) {
4522 jack_nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region.position();
4523 float percentage = (float) ((double) newlen - (double) clicked_regionview->region.length()) / ((double) newlen) * 100.0f;
4525 begin_reversible_command (_("timestretch"));
4527 if (run_timestretch (selection->audio_regions, percentage) == 0) {
4528 session->commit_reversible_command ();
4533 Editor::mouse_brush_insert_region (AudioRegionView* rv, jack_nframes_t pos)
4535 /* no brushing without a useful snap setting */
4537 switch (snap_mode) {
4539 return; /* can't work because it allows region to be placed anywhere */
4544 switch (snap_type) {
4547 case SnapToEditCursor:
4554 /* don't brush a copy over the original */
4556 if (pos == rv->region.position()) {
4560 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
4562 if (atv == 0 || !atv->is_audio_track()) {
4566 Playlist* playlist = atv->playlist();
4567 double speed = atv->get_diskstream()->speed();
4569 session->add_undo (playlist->get_memento());
4570 playlist->add_region (*(new AudioRegion (rv->region)), (jack_nframes_t) (pos * speed));
4571 session->add_redo_no_execute (playlist->get_memento());
4573 // playlist is frozen, so we have to update manually
4575 playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
4579 Editor::track_height_step_timeout ()
4582 struct timeval delta;
4584 gettimeofday (&now, 0);
4585 timersub (&now, &last_track_height_step_timestamp, &delta);
4587 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4588 current_stepping_trackview = 0;