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);
100 // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
103 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
107 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
108 position is negative (as can be the case with motion events in particular),
109 the frame location is always positive.
112 return pixel_to_frame (*pcx);
116 Editor::mouse_mode_toggled (MouseMode m)
118 if (ignore_mouse_mode_toggle) {
124 if (mouse_select_button.get_active()) {
130 if (mouse_move_button.get_active()) {
136 if (mouse_gain_button.get_active()) {
142 if (mouse_zoom_button.get_active()) {
148 if (mouse_timefx_button.get_active()) {
154 if (mouse_audition_button.get_active()) {
165 Editor::set_mouse_mode (MouseMode m, bool force)
167 if (drag_info.item) {
171 if (m == mouse_mode && !force) {
179 if (mouse_mode != MouseRange) {
181 /* in all modes except range, hide the range selection,
182 show the object (region) selection.
185 for (AudioRegionSelection::iterator i = selection->audio_regions.begin(); i != selection->audio_regions.end(); ++i) {
186 (*i)->set_should_show_selection (true);
188 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
189 (*i)->hide_selection ();
194 /* in range mode, hide object (region) selection, and show the
198 for (AudioRegionSelection::iterator i = selection->audio_regions.begin(); i != selection->audio_regions.end(); ++i) {
199 (*i)->set_should_show_selection (false);
201 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
202 if ((*i)->selected()) {
203 (*i)->show_selection (selection->time);
208 /* XXX the hack of unsetting all other buttongs should go
209 away once GTK2 allows us to use regular radio buttons drawn like
210 normal buttons, rather than my silly GroupedButton hack.
213 ignore_mouse_mode_toggle = true;
215 switch (mouse_mode) {
217 mouse_select_button.set_active (true);
218 current_canvas_cursor = selector_cursor;
222 mouse_move_button.set_active (true);
223 current_canvas_cursor = grabber_cursor;
227 mouse_gain_button.set_active (true);
228 current_canvas_cursor = cross_hair_cursor;
232 mouse_zoom_button.set_active (true);
233 current_canvas_cursor = zoom_cursor;
237 mouse_timefx_button.set_active (true);
238 current_canvas_cursor = time_fx_cursor; // just use playhead
242 mouse_audition_button.set_active (true);
243 current_canvas_cursor = speaker_cursor;
247 ignore_mouse_mode_toggle = false;
250 track_canvas.get_window()->set_cursor(*current_canvas_cursor);
255 Editor::step_mouse_mode (bool next)
257 switch (current_mouse_mode()) {
259 if (next) set_mouse_mode (MouseRange);
260 else set_mouse_mode (MouseTimeFX);
264 if (next) set_mouse_mode (MouseZoom);
265 else set_mouse_mode (MouseObject);
269 if (next) set_mouse_mode (MouseGain);
270 else set_mouse_mode (MouseRange);
274 if (next) set_mouse_mode (MouseTimeFX);
275 else set_mouse_mode (MouseZoom);
279 if (next) set_mouse_mode (MouseAudition);
280 else set_mouse_mode (MouseGain);
284 if (next) set_mouse_mode (MouseObject);
285 else set_mouse_mode (MouseTimeFX);
291 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
293 jack_nframes_t where = event_frame (event, 0, 0);
295 track_canvas.grab_focus();
297 if (session && session->actively_recording()) {
301 /* in object/audition/timefx mode, any button press sets
302 the selection if the object can be selected. this is a
303 bit of hack, because we want to avoid this if the
304 mouse operation is a region alignment.
307 if (((mouse_mode == MouseObject) ||
308 (mouse_mode == MouseAudition && item_type == RegionItem) ||
309 (mouse_mode == MouseTimeFX && item_type == RegionItem)) &&
310 event->type == GDK_BUTTON_PRESS &&
311 event->button.button <= 3) {
316 /* not dbl-click or triple-click */
320 set_selected_regionview_from_click (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift), true);
323 case AudioRegionViewNameHighlight:
324 case AudioRegionViewName:
325 if ((rv = reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))) != 0) {
326 set_selected_regionview_from_click (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift), true);
330 case GainAutomationControlPointItem:
331 case PanAutomationControlPointItem:
332 case RedirectAutomationControlPointItem:
333 if ((cp = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) != 0) {
334 set_selected_control_point_from_click (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift), true);
341 case AutomationTrackItem:
349 #define SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
350 #ifdef SELECT_TRACK_FROM_CANVAS_IN_RANGE_MODE
351 /* in range mode, button 1/2/3 press potentially selects a track */
353 if (mouse_mode == MouseRange &&
354 event->type == GDK_BUTTON_PRESS &&
355 event->button.button <= 3) {
362 case AutomationTrackItem:
363 set_selected_track_from_click (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift), true, true);
366 case AudioRegionViewNameHighlight:
367 case AudioRegionViewName:
368 rv = reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"));
375 if (drag_info.item == 0 &&
376 (Keyboard::is_delete_event (&event->button) ||
377 Keyboard::is_context_menu_event (&event->button) ||
378 Keyboard::is_edit_event (&event->button))) {
380 /* handled by button release */
384 switch (event->button.button) {
387 if (event->type == GDK_BUTTON_PRESS) {
389 if (drag_info.item) {
390 drag_info.item->ungrab (event->button.time);
393 /* single mouse clicks on any of these item types operate
394 independent of mouse mode, mostly because they are
395 not on the main track canvas or because we want
401 case PlayheadCursorItem:
402 start_cursor_grab (item, event);
406 if (Keyboard::modifier_state_equals (event->button.state,
407 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
408 hide_marker (item, event);
410 start_marker_grab (item, event);
414 case TempoMarkerItem:
415 start_tempo_marker_grab (item, event);
418 case MeterMarkerItem:
419 start_meter_marker_grab (item, event);
428 case RangeMarkerBarItem:
429 start_range_markerbar_op (item, event, CreateRangeMarker);
432 case TransportMarkerBarItem:
433 start_range_markerbar_op (item, event, CreateTransportMarker);
442 switch (mouse_mode) {
445 case StartSelectionTrimItem:
446 start_selection_op (item, event, SelectionStartTrim);
449 case EndSelectionTrimItem:
450 start_selection_op (item, event, SelectionEndTrim);
454 if (Keyboard::modifier_state_contains
455 (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
456 // contains and not equals because I can't use alt as a modifier alone.
457 start_selection_grab (item, event);
458 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
459 /* grab selection for moving */
460 start_selection_op (item, event, SelectionMove);
463 /* this was debated, but decided the more common action was to
464 make a new selection */
465 start_selection_op (item, event, CreateSelection);
470 start_selection_op (item, event, CreateSelection);
476 if (Keyboard::modifier_state_contains (event->button.state,
477 Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt))
478 && event->type == GDK_BUTTON_PRESS) {
480 start_rubberband_select (item, event);
482 } else if (event->type == GDK_BUTTON_PRESS) {
485 case FadeInHandleItem:
486 start_fade_in_grab (item, event);
489 case FadeOutHandleItem:
490 start_fade_out_grab (item, event);
494 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
495 start_region_copy_grab (item, event);
496 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
497 start_region_brush_grab (item, event);
499 start_region_grab (item, event);
503 case AudioRegionViewNameHighlight:
504 start_trim (item, event);
508 case AudioRegionViewName:
509 /* rename happens on edit clicks */
510 start_trim (clicked_regionview->get_name_highlight(), event);
514 case GainAutomationControlPointItem:
515 case PanAutomationControlPointItem:
516 case RedirectAutomationControlPointItem:
517 start_control_point_grab (item, event);
521 case GainAutomationLineItem:
522 case PanAutomationLineItem:
523 case RedirectAutomationLineItem:
524 start_line_grab_from_line (item, event);
529 case AutomationTrackItem:
530 start_rubberband_select (item, event);
533 /* <CMT Additions> */
534 case ImageFrameHandleStartItem:
535 imageframe_start_handle_op(item, event) ;
538 case ImageFrameHandleEndItem:
539 imageframe_end_handle_op(item, event) ;
542 case MarkerViewHandleStartItem:
543 markerview_item_start_handle_op(item, event) ;
546 case MarkerViewHandleEndItem:
547 markerview_item_end_handle_op(item, event) ;
550 /* </CMT Additions> */
552 /* <CMT Additions> */
554 start_markerview_grab(item, event) ;
557 start_imageframe_grab(item, event) ;
559 /* </CMT Additions> */
571 // start_line_grab_from_regionview (item, event);
574 case GainControlPointItem:
575 start_control_point_grab (item, event);
579 start_line_grab_from_line (item, event);
582 case GainAutomationControlPointItem:
583 case PanAutomationControlPointItem:
584 case RedirectAutomationControlPointItem:
585 start_control_point_grab (item, event);
596 case GainAutomationControlPointItem:
597 case PanAutomationControlPointItem:
598 case RedirectAutomationControlPointItem:
599 start_control_point_grab (item, event);
602 case GainAutomationLineItem:
603 case PanAutomationLineItem:
604 case RedirectAutomationLineItem:
605 start_line_grab_from_line (item, event);
609 // XXX need automation mode to identify which
611 // start_line_grab_from_regionview (item, event);
621 if (event->type == GDK_BUTTON_PRESS) {
622 start_mouse_zoom (item, event);
629 if (item_type == RegionItem) {
630 start_time_fx (item, event);
635 /* handled in release */
644 switch (mouse_mode) {
646 if (event->type == GDK_BUTTON_PRESS) {
649 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
650 start_region_copy_grab (item, event);
652 start_region_grab (item, event);
656 case GainAutomationControlPointItem:
657 case PanAutomationControlPointItem:
658 case RedirectAutomationControlPointItem:
659 start_control_point_grab (item, event);
670 case AudioRegionViewNameHighlight:
671 start_trim (item, event);
675 case AudioRegionViewName:
676 start_trim (clicked_regionview->get_name_highlight(), event);
687 if (event->type == GDK_BUTTON_PRESS) {
688 /* relax till release */
695 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
696 temporal_zoom_session();
698 temporal_zoom_to_frame (true, event_frame(event));
713 switch (mouse_mode) {
715 //temporal_zoom_to_frame (true, where);
716 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
717 temporal_zoom_to_frame (true, where);
720 temporal_zoom_step (true);
725 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
726 scroll_backward (0.6f);
728 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
729 scroll_tracks_up_line ();
731 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
732 if (clicked_trackview) {
733 if (!current_stepping_trackview) {
734 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
735 current_stepping_trackview = clicked_trackview;
737 gettimeofday (&last_track_height_step_timestamp, 0);
738 current_stepping_trackview->step_height (true);
741 else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
742 temporal_zoom_to_frame (true, where);
749 switch (mouse_mode) {
751 // temporal_zoom_to_frame (false, where);
752 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
753 temporal_zoom_to_frame (false, where);
756 temporal_zoom_step (false);
761 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
762 scroll_forward (0.6f);
764 else if (Keyboard::no_modifier_keys_pressed (&event->button)) {
765 scroll_tracks_down_line ();
767 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
768 if (clicked_trackview) {
769 if (!current_stepping_trackview) {
770 step_timeout = Glib::signal_timeout().connect (mem_fun(*this, &Editor::track_height_step_timeout), 500);
771 current_stepping_trackview = clicked_trackview;
773 gettimeofday (&last_track_height_step_timestamp, 0);
774 current_stepping_trackview->step_height (false);
776 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
777 temporal_zoom_to_frame (false, where);
791 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
793 jack_nframes_t where = event_frame (event, 0, 0);
795 /* no action if we're recording */
797 if (session && session->actively_recording()) {
801 /* first, see if we're finishing a drag ... */
803 if (drag_info.item) {
804 if (end_grab (item, event)) {
805 /* grab dragged, so do nothing else */
810 /* edit events get handled here */
812 if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
818 case TempoMarkerItem:
819 edit_tempo_marker (item);
822 case MeterMarkerItem:
823 edit_meter_marker (item);
826 case AudioRegionViewName:
827 if (clicked_regionview->name_active()) {
828 return mouse_rename_region (item, event);
838 /* context menu events get handled here */
840 if (Keyboard::is_context_menu_event (&event->button)) {
842 if (drag_info.item == 0) {
844 /* no matter which button pops up the context menu, tell the menu
845 widget to use button 1 to drive menu selection.
850 case FadeInHandleItem:
852 case FadeOutHandleItem:
853 popup_fade_context_menu (1, event->button.time, item, item_type);
857 popup_track_context_menu (1, event->button.time, item_type, false, where);
861 case AudioRegionViewNameHighlight:
862 case AudioRegionViewName:
863 popup_track_context_menu (1, event->button.time, item_type, false, where);
867 popup_track_context_menu (1, event->button.time, item_type, true, where);
870 case AutomationTrackItem:
871 popup_track_context_menu (1, event->button.time, item_type, false, where);
875 case RangeMarkerBarItem:
876 case TransportMarkerBarItem:
879 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
883 marker_context_menu (&event->button, item);
886 case TempoMarkerItem:
887 tm_marker_context_menu (&event->button, item);
890 case MeterMarkerItem:
891 tm_marker_context_menu (&event->button, item);
894 case CrossfadeViewItem:
895 popup_track_context_menu (1, event->button.time, item_type, false, where);
898 /* <CMT Additions> */
900 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
902 case ImageFrameTimeAxisItem:
903 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
906 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
908 case MarkerTimeAxisItem:
909 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
911 /* <CMT Additions> */
922 /* delete events get handled here */
924 if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
927 case TempoMarkerItem:
928 remove_tempo_marker (item);
931 case MeterMarkerItem:
932 remove_meter_marker (item);
937 remove_marker (*item, event);
942 if (mouse_mode == MouseObject) {
943 remove_clicked_region ();
947 case GainControlPointItem:
948 if (mouse_mode == MouseGain) {
949 remove_gain_control_point (item, event);
953 case GainAutomationControlPointItem:
954 case PanAutomationControlPointItem:
955 case RedirectAutomationControlPointItem:
956 remove_control_point (item, event);
965 switch (event->button.button) {
969 /* see comments in button_press_handler */
971 case PlayheadCursorItem:
974 case GainAutomationLineItem:
975 case PanAutomationLineItem:
976 case RedirectAutomationLineItem:
977 case StartSelectionTrimItem:
978 case EndSelectionTrimItem:
982 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
983 snap_to (where, 0, true);
985 mouse_add_new_marker (where);
989 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
992 mouse_add_new_tempo_event (where);
996 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1004 switch (mouse_mode) {
1006 switch (item_type) {
1007 case AutomationTrackItem:
1008 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event
1022 switch (item_type) {
1024 clicked_regionview->add_gain_point_event (item, event);
1028 case AutomationTrackItem:
1029 dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
1030 add_automation_event (item, event, where, event->button.y);
1039 switch (item_type) {
1041 audition_selected_region ();
1058 switch (mouse_mode) {
1061 switch (item_type) {
1063 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
1065 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
1068 // Button2 click is unused
1081 // x_style_paste (where, 1.0);
1101 Editor::maybe_autoscroll (GdkEvent* event)
1103 jack_nframes_t one_page = (jack_nframes_t) rint (canvas_width * frames_per_unit);
1104 jack_nframes_t rightmost_frame = leftmost_frame + one_page;
1106 jack_nframes_t frame = drag_info.current_pointer_frame;
1108 if (autoscroll_timeout_tag < 0) {
1109 if (frame > rightmost_frame) {
1110 if (rightmost_frame < max_frames) {
1111 start_canvas_autoscroll (1);
1113 } else if (frame < leftmost_frame) {
1114 if (leftmost_frame > 0) {
1115 start_canvas_autoscroll (-1);
1119 if (frame >= leftmost_frame && frame < rightmost_frame) {
1120 stop_canvas_autoscroll ();
1126 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1132 switch (item_type) {
1133 case GainControlPointItem:
1134 if (mouse_mode == MouseGain) {
1135 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1136 cp->set_visible (true);
1140 at_y = cp->get_y ();
1141 cp->item->i2w (at_x, at_y);
1145 fraction = 1.0 - (cp->get_y() / cp->line.height());
1147 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1148 show_verbose_canvas_cursor ();
1150 if (is_drawable()) {
1151 track_canvas.get_window()->set_cursor (*fader_cursor);
1156 case GainAutomationControlPointItem:
1157 case PanAutomationControlPointItem:
1158 case RedirectAutomationControlPointItem:
1159 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1160 cp->set_visible (true);
1164 at_y = cp->get_y ();
1165 cp->item->i2w (at_x, at_y);
1169 fraction = 1.0 - (cp->get_y() / cp->line.height());
1171 set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
1172 show_verbose_canvas_cursor ();
1174 if (is_drawable()) {
1175 track_canvas.get_window()->set_cursor (*fader_cursor);
1180 if (mouse_mode == MouseGain) {
1181 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1183 line->property_fill_color_rgba() = color_map[cEnteredGainLine];
1184 if (is_drawable()) {
1185 track_canvas.get_window()->set_cursor (*fader_cursor);
1190 case GainAutomationLineItem:
1191 case RedirectAutomationLineItem:
1192 case PanAutomationLineItem:
1194 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1196 line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
1198 if (is_drawable()) {
1199 track_canvas.get_window()->set_cursor (*fader_cursor);
1203 case AudioRegionViewNameHighlight:
1204 if (is_drawable() && mouse_mode == MouseObject) {
1205 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1209 case StartSelectionTrimItem:
1210 case EndSelectionTrimItem:
1211 /* <CMT Additions> */
1212 case ImageFrameHandleStartItem:
1213 case ImageFrameHandleEndItem:
1214 case MarkerViewHandleStartItem:
1215 case MarkerViewHandleEndItem:
1216 /* </CMT Additions> */
1218 if (is_drawable()) {
1219 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1223 case EditCursorItem:
1224 case PlayheadCursorItem:
1225 if (is_drawable()) {
1226 track_canvas.get_window()->set_cursor (*grabber_cursor);
1230 case AudioRegionViewName:
1232 /* when the name is not an active item, the entire name highlight is for trimming */
1234 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1235 if (mouse_mode == MouseObject && is_drawable()) {
1236 track_canvas.get_window()->set_cursor (*trimmer_cursor);
1242 case AutomationTrackItem:
1243 if (is_drawable()) {
1244 Gdk::Cursor *cursor;
1245 switch (mouse_mode) {
1247 cursor = selector_cursor;
1250 cursor = zoom_cursor;
1253 cursor = cross_hair_cursor;
1257 track_canvas.get_window()->set_cursor (*cursor);
1259 AutomationTimeAxisView* atv;
1260 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1261 clear_entered_track = false;
1262 set_entered_track (atv);
1268 case RangeMarkerBarItem:
1269 case TransportMarkerBarItem:
1272 if (is_drawable()) {
1273 time_canvas.get_window()->set_cursor (*timebar_cursor);
1278 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1281 marker->set_color_rgba (color_map[cEnteredMarker]);
1283 case MeterMarkerItem:
1284 case TempoMarkerItem:
1285 if (is_drawable()) {
1286 time_canvas.get_window()->set_cursor (*timebar_cursor);
1289 case FadeInHandleItem:
1290 case FadeOutHandleItem:
1291 if (mouse_mode == MouseObject) {
1292 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1294 rect->property_fill_color_rgba() = 0;
1295 rect->property_outline_pixels() = 1;
1304 /* second pass to handle entered track status in a comprehensible way.
1307 switch (item_type) {
1309 case GainAutomationLineItem:
1310 case RedirectAutomationLineItem:
1311 case PanAutomationLineItem:
1312 case GainControlPointItem:
1313 case GainAutomationControlPointItem:
1314 case PanAutomationControlPointItem:
1315 case RedirectAutomationControlPointItem:
1316 /* these do not affect the current entered track state */
1317 clear_entered_track = false;
1320 case AutomationTrackItem:
1321 /* handled above already */
1325 set_entered_track (0);
1333 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1339 AudioRegionView* rv;
1342 switch (item_type) {
1343 case GainControlPointItem:
1344 case GainAutomationControlPointItem:
1345 case PanAutomationControlPointItem:
1346 case RedirectAutomationControlPointItem:
1347 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1348 if (cp->line.npoints() > 1) {
1349 if (!cp->selected) {
1350 cp->set_visible (false);
1354 if (is_drawable()) {
1355 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1358 hide_verbose_canvas_cursor ();
1361 case AudioRegionViewNameHighlight:
1362 case StartSelectionTrimItem:
1363 case EndSelectionTrimItem:
1364 case EditCursorItem:
1365 case PlayheadCursorItem:
1366 /* <CMT Additions> */
1367 case ImageFrameHandleStartItem:
1368 case ImageFrameHandleEndItem:
1369 case MarkerViewHandleStartItem:
1370 case MarkerViewHandleEndItem:
1371 /* </CMT Additions> */
1372 if (is_drawable()) {
1373 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1378 case GainAutomationLineItem:
1379 case RedirectAutomationLineItem:
1380 case PanAutomationLineItem:
1381 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1383 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1385 line->property_fill_color_rgba() = al->get_line_color();
1387 if (is_drawable()) {
1388 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1392 case AudioRegionViewName:
1393 /* see enter_handler() for notes */
1394 if (!reinterpret_cast<AudioRegionView *> (item->get_data ("regionview"))->name_active()) {
1395 if (is_drawable() && mouse_mode == MouseObject) {
1396 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1401 case RangeMarkerBarItem:
1402 case TransportMarkerBarItem:
1406 if (is_drawable()) {
1407 time_canvas.get_window()->set_cursor (*timebar_cursor);
1412 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1415 loc = find_location_from_marker (marker, is_start);
1416 if (loc) location_flags_changed (loc, this);
1418 case MeterMarkerItem:
1419 case TempoMarkerItem:
1421 if (is_drawable()) {
1422 time_canvas.get_window()->set_cursor (*timebar_cursor);
1427 case FadeInHandleItem:
1428 case FadeOutHandleItem:
1429 rv = static_cast<AudioRegionView*>(item->get_data ("regionview"));
1431 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1433 rect->property_fill_color_rgba() = rv->get_fill_color();
1434 rect->property_outline_pixels() = 0;
1439 case AutomationTrackItem:
1440 if (is_drawable()) {
1441 track_canvas.get_window()->set_cursor (*current_canvas_cursor);
1442 clear_entered_track = true;
1443 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1455 Editor::left_automation_track ()
1457 if (clear_entered_track) {
1458 set_entered_track (0);
1459 clear_entered_track = false;
1465 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1469 /* We call this so that MOTION_NOTIFY events continue to be
1470 delivered to the canvas. We need to do this because we set
1471 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1472 the density of the events, at the expense of a round-trip
1473 to the server. Given that this will mostly occur on cases
1474 where DISPLAY = :0.0, and given the cost of what the motion
1475 event might do, its a good tradeoff.
1478 track_canvas.get_pointer (x, y);
1480 if (current_stepping_trackview) {
1481 /* don't keep the persistent stepped trackview if the mouse moves */
1482 current_stepping_trackview = 0;
1483 step_timeout.disconnect ();
1486 if (session && session->actively_recording()) {
1487 /* Sorry. no dragging stuff around while we record */
1491 drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1492 &drag_info.current_pointer_y);
1494 if (drag_info.item) {
1495 /* item != 0 is the best test i can think of for
1498 if (!drag_info.move_threshold_passsed)
1500 drag_info.move_threshold_passsed = (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1502 // and change the initial grab loc/frame if this drag info wants us to
1503 if (drag_info.want_move_threshold && drag_info.move_threshold_passsed) {
1504 drag_info.grab_frame = drag_info.current_pointer_frame;
1505 drag_info.grab_x = drag_info.current_pointer_x;
1506 drag_info.grab_y = drag_info.current_pointer_y;
1507 drag_info.last_pointer_frame = drag_info.grab_frame;
1508 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1513 switch (item_type) {
1514 case PlayheadCursorItem:
1515 case EditCursorItem:
1517 case GainControlPointItem:
1518 case RedirectAutomationControlPointItem:
1519 case GainAutomationControlPointItem:
1520 case PanAutomationControlPointItem:
1521 case TempoMarkerItem:
1522 case MeterMarkerItem:
1523 case AudioRegionViewNameHighlight:
1524 case StartSelectionTrimItem:
1525 case EndSelectionTrimItem:
1528 case RedirectAutomationLineItem:
1529 case GainAutomationLineItem:
1530 case PanAutomationLineItem:
1531 case FadeInHandleItem:
1532 case FadeOutHandleItem:
1533 /* <CMT Additions> */
1534 case ImageFrameHandleStartItem:
1535 case ImageFrameHandleEndItem:
1536 case MarkerViewHandleStartItem:
1537 case MarkerViewHandleEndItem:
1538 /* </CMT Additions> */
1539 if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1540 (event->motion.state & Gdk::BUTTON2_MASK))) {
1541 maybe_autoscroll (event);
1542 (this->*(drag_info.motion_callback)) (item, event);
1551 switch (mouse_mode) {
1556 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1557 (event->motion.state & GDK_BUTTON2_MASK))) {
1558 maybe_autoscroll (event);
1559 (this->*(drag_info.motion_callback)) (item, event);
1570 track_canvas_motion (event);
1578 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1580 if (drag_info.item == 0) {
1581 fatal << _("programming error: start_grab called without drag item") << endmsg;
1587 cursor = grabber_cursor;
1590 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1592 if (event->button.button == 2) {
1593 drag_info.x_constrained = true;
1595 drag_info.x_constrained = false;
1598 drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
1599 drag_info.last_pointer_frame = drag_info.grab_frame;
1600 drag_info.current_pointer_frame = drag_info.grab_frame;
1601 drag_info.current_pointer_x = drag_info.grab_x;
1602 drag_info.current_pointer_y = drag_info.grab_y;
1603 drag_info.cumulative_x_drag = 0;
1604 drag_info.cumulative_y_drag = 0;
1605 drag_info.first_move = true;
1606 drag_info.move_threshold_passsed = false;
1607 drag_info.want_move_threshold = false;
1608 drag_info.pointer_frame_offset = 0;
1609 drag_info.brushing = false;
1610 drag_info.copied_location = 0;
1612 drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1614 event->button.time);
1616 if (session && session->transport_rolling()) {
1617 drag_info.was_rolling = true;
1619 drag_info.was_rolling = false;
1622 switch (snap_type) {
1623 case SnapToRegionStart:
1624 case SnapToRegionEnd:
1625 case SnapToRegionSync:
1626 case SnapToRegionBoundary:
1627 build_region_boundary_cache ();
1635 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1637 bool did_drag = false;
1639 stop_canvas_autoscroll ();
1641 if (drag_info.item == 0) {
1645 drag_info.item->ungrab (event->button.time);
1647 if (drag_info.finished_callback) {
1648 (this->*(drag_info.finished_callback)) (item, event);
1651 did_drag = !drag_info.first_move;
1653 hide_verbose_canvas_cursor();
1656 drag_info.copy = false;
1657 drag_info.motion_callback = 0;
1658 drag_info.finished_callback = 0;
1659 drag_info.last_trackview = 0;
1660 drag_info.last_frame_position = 0;
1661 drag_info.grab_frame = 0;
1662 drag_info.last_pointer_frame = 0;
1663 drag_info.current_pointer_frame = 0;
1664 drag_info.brushing = false;
1666 if (drag_info.copied_location) {
1667 delete drag_info.copied_location;
1668 drag_info.copied_location = 0;
1675 Editor::set_edit_cursor (GdkEvent* event)
1677 jack_nframes_t pointer_frame = event_frame (event);
1679 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1680 if (snap_type != SnapToEditCursor) {
1681 snap_to (pointer_frame);
1685 edit_cursor->set_position (pointer_frame);
1686 edit_cursor_clock.set (pointer_frame);
1690 Editor::set_playhead_cursor (GdkEvent* event)
1692 jack_nframes_t pointer_frame = event_frame (event);
1694 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1695 snap_to (pointer_frame);
1699 session->request_locate (pointer_frame, session->transport_rolling());
1704 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1706 drag_info.item = item;
1707 drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1708 drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1712 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1713 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1717 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1719 drag_info.pointer_frame_offset = drag_info.grab_frame - ((jack_nframes_t) arv->region.fade_in().back()->when + arv->region.position());
1723 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1725 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1727 jack_nframes_t fade_length;
1729 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1730 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1736 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1740 if (pos < (arv->region.position() + 64)) {
1741 fade_length = 64; // this should be a minimum defined somewhere
1742 } else if (pos > arv->region.last_frame()) {
1743 fade_length = arv->region.length();
1745 fade_length = pos - arv->region.position();
1748 arv->reset_fade_in_shape_width (fade_length);
1750 show_verbose_duration_cursor (arv->region.position(), arv->region.position() + fade_length, 10);
1752 drag_info.first_move = false;
1756 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1758 if (drag_info.first_move) return;
1760 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1762 jack_nframes_t fade_length;
1764 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1765 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1771 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1775 if (pos < (arv->region.position() + 64)) {
1776 fade_length = 64; // this should be a minimum defined somewhere
1778 else if (pos > arv->region.last_frame()) {
1779 fade_length = arv->region.length();
1782 fade_length = pos - arv->region.position();
1785 begin_reversible_command (_("change fade in length"));
1786 session->add_undo (arv->region.get_memento());
1787 arv->region.set_fade_in_length (fade_length);
1788 session->add_redo_no_execute (arv->region.get_memento());
1789 commit_reversible_command ();
1790 fade_in_drag_motion_callback (item, event);
1794 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1796 drag_info.item = item;
1797 drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1798 drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1802 if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1803 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1807 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1809 drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region.length() - (jack_nframes_t) arv->region.fade_out().back()->when + arv->region.position());
1813 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1815 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1817 jack_nframes_t fade_length;
1819 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1820 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1826 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1830 if (pos > (arv->region.last_frame() - 64)) {
1831 fade_length = 64; // this should really be a minimum fade defined somewhere
1833 else if (pos < arv->region.position()) {
1834 fade_length = arv->region.length();
1837 fade_length = arv->region.last_frame() - pos;
1840 arv->reset_fade_out_shape_width (fade_length);
1842 show_verbose_duration_cursor (arv->region.last_frame() - fade_length, arv->region.last_frame(), 10);
1844 drag_info.first_move = false;
1848 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1850 if (drag_info.first_move) return;
1852 AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1854 jack_nframes_t fade_length;
1856 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1857 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1863 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1867 if (pos > (arv->region.last_frame() - 64)) {
1868 fade_length = 64; // this should really be a minimum fade defined somewhere
1870 else if (pos < arv->region.position()) {
1871 fade_length = arv->region.length();
1874 fade_length = arv->region.last_frame() - pos;
1877 begin_reversible_command (_("change fade out length"));
1878 session->add_undo (arv->region.get_memento());
1879 arv->region.set_fade_out_length (fade_length);
1880 session->add_redo_no_execute (arv->region.get_memento());
1881 commit_reversible_command ();
1883 fade_out_drag_motion_callback (item, event);
1887 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1889 drag_info.item = item;
1890 drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1891 drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1895 if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1896 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1900 Cursor* cursor = (Cursor *) drag_info.data;
1902 if (session && cursor == playhead_cursor) {
1903 if (drag_info.was_rolling) {
1904 session->request_stop ();
1908 drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;
1910 show_verbose_time_cursor (cursor->current_frame, 10);
1914 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1916 Cursor* cursor = (Cursor *) drag_info.data;
1917 jack_nframes_t adjusted_frame;
1919 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1920 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1926 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1927 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1928 snap_to (adjusted_frame);
1932 if (adjusted_frame == drag_info.last_pointer_frame) return;
1934 cursor->set_position (adjusted_frame);
1936 if (cursor == edit_cursor) {
1937 edit_cursor_clock.set (cursor->current_frame);
1940 show_verbose_time_cursor (cursor->current_frame, 10);
1942 drag_info.last_pointer_frame = adjusted_frame;
1943 drag_info.first_move = false;
1947 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1949 if (drag_info.first_move) return;
1951 cursor_drag_motion_callback (item, event);
1953 if (item == &playhead_cursor->canvas_item) {
1955 session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1957 } else if (item == &edit_cursor->canvas_item) {
1958 edit_cursor->set_position (edit_cursor->current_frame);
1959 edit_cursor_clock.set (edit_cursor->current_frame);
1964 Editor::update_marker_drag_item (Location *location)
1966 double x1 = frame_to_pixel (location->start());
1967 double x2 = frame_to_pixel (location->end());
1969 if (location->is_mark()) {
1970 marker_drag_line_points.front().set_x(x1);
1971 marker_drag_line_points.back().set_x(x1);
1972 marker_drag_line->property_points() = marker_drag_line_points;
1975 range_marker_drag_rect->property_x1() = x1;
1976 range_marker_drag_rect->property_x2() = x2;
1981 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
1985 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1986 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1992 Location *location = find_location_from_marker (marker, is_start);
1994 drag_info.item = item;
1995 drag_info.data = marker;
1996 drag_info.motion_callback = &Editor::marker_drag_motion_callback;
1997 drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2001 drag_info.copied_location = new Location (*location);
2002 drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());
2004 update_marker_drag_item (location);
2006 if (location->is_mark()) {
2007 marker_drag_line->show();
2008 marker_drag_line->raise_to_top();
2011 range_marker_drag_rect->show();
2012 range_marker_drag_rect->raise_to_top();
2015 if (is_start) show_verbose_time_cursor (location->start(), 10);
2016 else show_verbose_time_cursor (location->end(), 10);
2020 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2022 jack_nframes_t f_delta;
2023 Marker* marker = (Marker *) drag_info.data;
2024 Location *real_location;
2025 Location *copy_location;
2027 bool move_both = false;
2029 jack_nframes_t newframe;
2030 if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2031 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2037 jack_nframes_t next = newframe;
2039 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2040 snap_to (newframe, 0, true);
2043 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
2045 /* call this to find out if its the start or end */
2047 real_location = find_location_from_marker (marker, is_start);
2049 /* use the copy that we're "dragging" around */
2051 copy_location = drag_info.copied_location;
2053 f_delta = copy_location->end() - copy_location->start();
2055 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2059 if (is_start) { // start marker
2062 copy_location->set_start (newframe);
2063 copy_location->set_end (newframe + f_delta);
2064 } else if (newframe < copy_location->end()) {
2065 copy_location->set_start (newframe);
2067 snap_to (next, 1, true);
2068 copy_location->set_end (next);
2069 copy_location->set_start (newframe);
2072 } else { // end marker
2075 copy_location->set_end (newframe);
2076 copy_location->set_start (newframe - f_delta);
2077 } else if (newframe > copy_location->start()) {
2078 copy_location->set_end (newframe);
2080 } else if (newframe > 0) {
2081 snap_to (next, -1, true);
2082 copy_location->set_start (next);
2083 copy_location->set_end (newframe);
2087 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2088 drag_info.first_move = false;
2090 update_marker_drag_item (copy_location);
2092 LocationMarkers* lm = find_location_markers (real_location);
2093 lm->set_position (copy_location->start(), copy_location->end());
2095 show_verbose_time_cursor (newframe, 10);
2099 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2101 if (drag_info.first_move) {
2102 marker_drag_motion_callback (item, event);
2106 Marker* marker = (Marker *) drag_info.data;
2108 Location * location = find_location_from_marker (marker, is_start);
2110 location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2113 marker_drag_line->hide();
2114 range_marker_drag_rect->hide();
2118 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2121 MeterMarker* meter_marker;
2123 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2124 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2128 meter_marker = dynamic_cast<MeterMarker*> (marker);
2130 MetricSection& section (meter_marker->meter());
2132 if (!section.movable()) {
2136 drag_info.item = item;
2137 drag_info.data = marker;
2138 drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2139 drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2143 drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();
2145 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2149 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2151 MeterMarker* marker = (MeterMarker *) drag_info.data;
2152 jack_nframes_t adjusted_frame;
2154 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2155 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2161 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2162 snap_to (adjusted_frame);
2165 if (adjusted_frame == drag_info.last_pointer_frame) return;
2167 marker->set_position (adjusted_frame);
2170 drag_info.last_pointer_frame = adjusted_frame;
2171 drag_info.first_move = false;
2173 show_verbose_time_cursor (adjusted_frame, 10);
2177 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2179 if (drag_info.first_move) return;
2181 meter_marker_drag_motion_callback (item, event);
2183 MeterMarker* marker = (MeterMarker *) drag_info.data;
2186 TempoMap& map (session->tempo_map());
2187 map.bbt_time (drag_info.last_pointer_frame, when);
2189 begin_reversible_command (_("move meter mark"));
2190 session->add_undo (map.get_memento());
2191 map.move_meter (marker->meter(), when);
2192 session->add_redo_no_execute (map.get_memento());
2193 commit_reversible_command ();
2197 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2200 TempoMarker* tempo_marker;
2202 if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2203 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2207 if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2208 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2212 MetricSection& section (tempo_marker->tempo());
2214 if (!section.movable()) {
2218 drag_info.item = item;
2219 drag_info.data = marker;
2220 drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2221 drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2225 drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2226 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2230 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2232 TempoMarker* marker = (TempoMarker *) drag_info.data;
2233 jack_nframes_t adjusted_frame;
2235 if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2236 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2242 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2243 snap_to (adjusted_frame);
2246 if (adjusted_frame == drag_info.last_pointer_frame) return;
2248 /* OK, we've moved far enough to make it worth actually move the thing. */
2250 marker->set_position (adjusted_frame);
2252 show_verbose_time_cursor (adjusted_frame, 10);
2254 drag_info.last_pointer_frame = adjusted_frame;
2255 drag_info.first_move = false;
2259 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2261 if (drag_info.first_move) return;
2263 tempo_marker_drag_motion_callback (item, event);
2265 TempoMarker* marker = (TempoMarker *) drag_info.data;
2268 TempoMap& map (session->tempo_map());
2269 map.bbt_time (drag_info.last_pointer_frame, when);
2271 begin_reversible_command (_("move tempo mark"));
2272 session->add_undo (map.get_memento());
2273 map.move_tempo (marker->tempo(), when);
2274 session->add_redo_no_execute (map.get_memento());
2275 commit_reversible_command ();
2279 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2281 ControlPoint* control_point;
2283 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2284 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2288 // We shouldn't remove the first or last gain point
2289 if (control_point->line.is_last_point(*control_point) ||
2290 control_point->line.is_first_point(*control_point)) {
2294 control_point->line.remove_point (*control_point);
2298 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2300 ControlPoint* control_point;
2302 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2303 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2307 control_point->line.remove_point (*control_point);
2311 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2313 ControlPoint* control_point;
2315 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2316 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2320 drag_info.item = item;
2321 drag_info.data = control_point;
2322 drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2323 drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2325 start_grab (event, fader_cursor);
2327 control_point->line.start_drag (control_point, 0);
2329 float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2330 set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction),
2331 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2333 show_verbose_canvas_cursor ();
2337 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2339 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2341 double cx = drag_info.current_pointer_x;
2342 double cy = drag_info.current_pointer_y;
2344 drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2345 drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2347 bool x_constrained = false;
2349 if (drag_info.x_constrained) {
2350 if (fabs(drag_info.cumulative_x_drag) < fabs(drag_info.cumulative_y_drag)) {
2351 cx = drag_info.grab_x;
2352 x_constrained = true;
2355 cy = drag_info.grab_y;
2360 cp->line.parent_group().w2i (cx, cy);
2364 cy = min ((double) cp->line.height(), cy);
2366 //translate cx to frames
2367 jack_nframes_t cx_frames = (jack_nframes_t) floor (cx * frames_per_unit);
2369 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !x_constrained) {
2370 snap_to (cx_frames);
2373 float fraction = 1.0 - (cy / cp->line.height());
2377 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2383 cp->line.point_drag (*cp, cx_frames , fraction, push);
2385 set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2389 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2391 ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2392 control_point_drag_motion_callback (item, event);
2393 cp->line.end_drag (cp);
2397 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2399 switch (mouse_mode) {
2401 start_line_grab (clicked_regionview->get_gain_line(), event);
2409 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2413 if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2414 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2418 start_line_grab (al, event);
2422 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2426 jack_nframes_t frame_within_region;
2428 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2432 cx = event->button.x;
2433 cy = event->button.y;
2434 line->parent_group().w2i (cx, cy);
2435 frame_within_region = (jack_nframes_t) floor (cx * frames_per_unit);
2437 if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before,
2438 current_line_drag_info.after)) {
2439 /* no adjacent points */
2443 drag_info.item = &line->grab_item();
2444 drag_info.data = line;
2445 drag_info.motion_callback = &Editor::line_drag_motion_callback;
2446 drag_info.finished_callback = &Editor::line_drag_finished_callback;
2448 start_grab (event, fader_cursor);
2450 double fraction = 1.0 - (cy / line->height());
2452 line->start_drag (0, fraction);
2454 set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2455 drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2456 show_verbose_canvas_cursor ();
2460 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2462 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2463 double cx = drag_info.current_pointer_x;
2464 double cy = drag_info.current_pointer_y;
2466 line->parent_group().w2i (cx, cy);
2469 fraction = 1.0 - (cy / line->height());
2473 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2479 line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2481 set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2485 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2487 AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2488 line_drag_motion_callback (item, event);
2493 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2495 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2499 drag_info.copy = false;
2500 drag_info.item = item;
2501 drag_info.data = clicked_regionview;
2502 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2503 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2508 TimeAxisView* tvp = clicked_trackview;
2509 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2511 if (tv && tv->is_audio_track()) {
2512 speed = tv->get_diskstream()->speed();
2515 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2516 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2517 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2518 // we want a move threshold
2519 drag_info.want_move_threshold = true;
2521 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2523 begin_reversible_command (_("move region(s)"));
2527 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2529 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2533 /* this is committed in the grab finished callback. */
2535 begin_reversible_command (_("Drag region copy"));
2537 /* duplicate the region(s) */
2539 vector<AudioRegionView*> new_regionviews;
2541 set<Playlist*> affected_playlists;
2542 pair<set<Playlist*>::iterator,bool> insert_result;
2544 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2545 AudioRegionView* rv;
2549 Playlist* to_playlist = rv->region.playlist();
2550 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
2552 insert_result = affected_playlists.insert (to_playlist);
2553 if (insert_result.second) {
2554 session->add_undo (to_playlist->get_memento ());
2557 latest_regionview = 0;
2559 sigc::connection c = atv->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2561 /* create a new region with the same name.
2564 AudioRegion* newregion = new AudioRegion (rv->region);
2566 /* if the original region was locked, we don't care */
2568 newregion->set_locked (false);
2570 to_playlist->add_region (*newregion, (jack_nframes_t) (rv->region.position() * atv->get_diskstream()->speed()));
2574 if (latest_regionview) {
2575 new_regionviews.push_back (latest_regionview);
2580 if (new_regionviews.empty()) {
2584 /* reset selection to new regionviews */
2586 selection->set (new_regionviews);
2588 drag_info.item = new_regionviews.front()->get_canvas_group ();
2589 drag_info.copy = true;
2590 drag_info.data = new_regionviews.front();
2591 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2592 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2596 TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2597 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
2600 if (atv && atv->is_audio_track()) {
2601 speed = atv->get_diskstream()->speed();
2604 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2605 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2606 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2607 // we want a move threshold
2608 drag_info.want_move_threshold = true;
2610 show_verbose_time_cursor (drag_info.last_frame_position, 10);
2612 //begin_reversible_command (_("copy region(s)"));
2616 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2618 if (selection->audio_regions.empty() || clicked_regionview == 0) {
2622 drag_info.copy = false;
2623 drag_info.item = item;
2624 drag_info.data = clicked_regionview;
2625 drag_info.motion_callback = &Editor::region_drag_motion_callback;
2626 drag_info.finished_callback = &Editor::region_drag_finished_callback;
2631 TimeAxisView* tvp = clicked_trackview;
2632 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2634 if (tv && tv->is_audio_track()) {
2635 speed = tv->get_diskstream()->speed();
2638 drag_info.last_frame_position = (jack_nframes_t) (clicked_regionview->region.position() / speed);
2639 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2640 drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2641 // we want a move threshold
2642 drag_info.want_move_threshold = true;
2643 drag_info.brushing = true;
2645 begin_reversible_command (_("Drag region brush"));
2649 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2653 AudioRegionView *rv = reinterpret_cast<AudioRegionView*> (drag_info.data);
2654 jack_nframes_t pending_region_position = 0;
2655 int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2656 int32_t visible_y_high = 0, visible_y_low = 512; //high meaning higher numbered.. not the height on the screen
2657 bool clamp_y_axis = false;
2658 vector<int32_t> height_list(512) ;
2659 vector<int32_t>::iterator j;
2661 /* Which trackview is this ? */
2663 TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2664 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2666 /* The region motion is only processed if the pointer is over
2670 if (!tv || !tv->is_audio_track()) {
2671 /* To make sure we hide the verbose canvas cursor when the mouse is
2672 not held over and audiotrack.
2674 hide_verbose_canvas_cursor ();
2678 original_pointer_order = drag_info.last_trackview->order;
2680 /************************************************************
2682 ************************************************************/
2684 if (drag_info.brushing) {
2685 clamp_y_axis = true;
2690 if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2692 int32_t children = 0, numtracks = 0;
2693 // XXX hard coding track limit, oh my, so very very bad
2694 bitset <1024> tracks (0x00);
2695 /* get a bitmask representing the visible tracks */
2697 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2698 TimeAxisView *tracklist_timeview;
2699 tracklist_timeview = (*i);
2700 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2701 list<TimeAxisView*> children_list;
2703 /* zeroes are audio tracks. ones are other types. */
2705 if (!atv2->hidden()) {
2707 if (visible_y_high < atv2->order) {
2708 visible_y_high = atv2->order;
2710 if (visible_y_low > atv2->order) {
2711 visible_y_low = atv2->order;
2714 if (!atv2->is_audio_track()) {
2715 tracks = tracks |= (0x01 << atv2->order);
2718 height_list[atv2->order] = (*i)->height;
2720 if ((children_list = atv2->get_child_list()).size() > 0) {
2721 for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) {
2722 tracks = tracks |= (0x01 << (atv2->order + children));
2723 height_list[atv2->order + children] = (*j)->height;
2731 /* find the actual span according to the canvas */
2733 canvas_pointer_y_span = pointer_y_span;
2734 if (drag_info.last_trackview->order >= tv->order) {
2736 for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2737 if (height_list[y] == 0 ) {
2738 canvas_pointer_y_span--;
2743 for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2744 if ( height_list[y] == 0 ) {
2745 canvas_pointer_y_span++;
2750 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2751 AudioRegionView* rv2;
2753 double ix1, ix2, iy1, iy2;
2756 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2757 rv2->get_canvas_group()->i2w (ix1, iy1);
2758 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2759 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
2761 if (atv2->order != original_pointer_order) {
2762 /* this isn't the pointer track */
2764 if (canvas_pointer_y_span > 0) {
2766 /* moving up the canvas */
2767 if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2769 int32_t visible_tracks = 0;
2770 while (visible_tracks < canvas_pointer_y_span ) {
2773 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2774 /* we're passing through a hidden track */
2779 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {
2780 clamp_y_axis = true;
2784 clamp_y_axis = true;
2787 } else if (canvas_pointer_y_span < 0) {
2789 /*moving down the canvas*/
2791 if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2794 int32_t visible_tracks = 0;
2796 while (visible_tracks > canvas_pointer_y_span ) {
2799 while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2803 if ( tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2804 clamp_y_axis = true;
2809 clamp_y_axis = true;
2815 /* this is the pointer's track */
2816 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow
2817 clamp_y_axis = true;
2818 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2819 clamp_y_axis = true;
2827 } else if (drag_info.last_trackview == tv) {
2828 clamp_y_axis = true;
2832 if (!clamp_y_axis) {
2833 drag_info.last_trackview = tv;
2836 /************************************************************
2838 ************************************************************/
2840 /* compute the amount of pointer motion in frames, and where
2841 the region would be if we moved it by that much.
2844 if (drag_info.move_threshold_passsed) {
2846 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2848 jack_nframes_t sync_frame;
2849 jack_nframes_t sync_offset;
2852 pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2854 sync_offset = rv->region.sync_offset (sync_dir);
2855 sync_frame = rv->region.adjust_to_sync (pending_region_position);
2857 /* we snap if the snap modifier is not enabled.
2860 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2861 snap_to (sync_frame);
2864 if (sync_frame - sync_offset <= sync_frame) {
2865 pending_region_position = sync_frame - (sync_dir*sync_offset);
2867 pending_region_position = 0;
2871 pending_region_position = 0;
2874 if (pending_region_position > max_frames - rv->region.length()) {
2875 pending_region_position = drag_info.last_frame_position;
2878 // printf ("3: pending_region_position= %lu %lu\n", pending_region_position, drag_info.last_frame_position );
2880 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
2882 /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
2883 to make it appear at the new location.
2886 if (pending_region_position > drag_info.last_frame_position) {
2887 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
2889 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
2892 drag_info.last_frame_position = pending_region_position;
2899 /* threshold not passed */
2904 /*************************************************************
2906 ************************************************************/
2908 if (x_delta == 0 && (pointer_y_span == 0)) {
2909 /* haven't reached next snap point, and we're not switching
2910 trackviews. nothing to do.
2916 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2918 AudioRegionView* rv2;
2921 /* if any regionview is at zero, we need to know so we can
2922 stop further leftward motion.
2925 double ix1, ix2, iy1, iy2;
2926 rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2927 rv2->get_canvas_group()->i2w (ix1, iy1);
2936 /*************************************************************
2938 ************************************************************/
2940 pair<set<Playlist*>::iterator,bool> insert_result;
2942 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
2944 AudioRegionView* rv;
2946 double ix1, ix2, iy1, iy2;
2947 int32_t temp_pointer_y_span = pointer_y_span;
2949 /* get item BBox, which will be relative to parent. so we have
2950 to query on a child, then convert to world coordinates using
2954 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2955 rv->get_canvas_group()->i2w (ix1, iy1);
2956 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2957 AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
2958 AudioTimeAxisView* temp_atv;
2960 if ((pointer_y_span != 0) && !clamp_y_axis) {
2963 for (j = height_list.begin(); j!= height_list.end(); j++) {
2964 if (x == canvas_atv->order) {
2965 /* we found the track the region is on */
2966 if (x != original_pointer_order) {
2967 /*this isn't from the same track we're dragging from */
2968 temp_pointer_y_span = canvas_pointer_y_span;
2970 while (temp_pointer_y_span > 0) {
2971 /* we're moving up canvas-wise,
2972 so we need to find the next track height
2974 if (j != height_list.begin()) {
2977 if (x != original_pointer_order) {
2978 /* we're not from the dragged track, so ignore hidden tracks. */
2980 temp_pointer_y_span++;
2984 temp_pointer_y_span--;
2986 while (temp_pointer_y_span < 0) {
2988 if (x != original_pointer_order) {
2990 temp_pointer_y_span--;
2994 if (j != height_list.end()) {
2997 temp_pointer_y_span++;
2999 /* find out where we'll be when we move and set height accordingly */
3001 tvp2 = trackview_by_y_position (iy1 + y_delta);
3002 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3003 rv->set_height (temp_atv->height);
3005 /* if you un-comment the following, the region colours will follow the track colours whilst dragging,
3006 personally, i think this can confuse things, but never mind.
3009 //const GdkColor& col (temp_atv->view->get_region_color());
3010 //rv->set_color (const_cast<GdkColor&>(col));
3017 /* prevent the regionview from being moved to before
3018 the zero position on the canvas.
3023 if (-x_delta > ix1) {
3026 } else if ((x_delta > 0) &&(rv->region.last_frame() > max_frames - x_delta)) {
3027 x_delta = max_frames - rv->region.last_frame();
3030 if (drag_info.first_move) {
3032 /* hide any dependent views */
3034 // rv->get_time_axis_view().hide_dependent_views (*rv);
3036 /* this is subtle. raising the regionview itself won't help,
3037 because raise_to_top() just puts the item on the top of
3038 its parent's stack. so, we need to put the trackview canvas_display group
3039 on the top, since its parent is the whole canvas.
3042 rv->get_canvas_group()->raise_to_top();
3043 rv->get_time_axis_view().canvas_display->raise_to_top();
3044 cursor_group->raise_to_top();
3046 /* freeze the playlists from notifying till
3050 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&rv->get_time_axis_view());
3051 if (atv && atv->is_audio_track()) {
3052 AudioPlaylist* pl = atv->get_diskstream()->playlist();
3054 /* only freeze and capture state once */
3056 insert_result = motion_frozen_playlists.insert (pl);
3057 if (insert_result.second) {
3059 session->add_undo(pl->get_memento());
3065 if (drag_info.brushing) {
3066 mouse_brush_insert_region (rv, pending_region_position);
3068 rv->move (x_delta, y_delta);
3072 if (drag_info.first_move) {
3073 cursor_group->raise_to_top();
3076 drag_info.first_move = false;
3078 if (x_delta != 0 && !drag_info.brushing) {
3079 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3085 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3087 jack_nframes_t where;
3088 AudioRegionView* rv = reinterpret_cast<AudioRegionView *> (drag_info.data);
3089 pair<set<Playlist*>::iterator,bool> insert_result;
3090 bool nocommit = true;
3092 AudioTimeAxisView* atv;
3093 bool regionview_y_movement;
3094 bool regionview_x_movement;
3096 /* first_move is set to false if the regionview has been moved in the
3100 if (drag_info.first_move) {
3107 /* The regionview has been moved at some stage during the grab so we need
3108 to account for any mouse movement between this event and the last one.
3111 region_drag_motion_callback (item, event);
3113 if (drag_info.brushing) {
3114 /* all changes were made during motion event handlers */
3118 /* adjust for track speed */
3121 atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3122 if (atv && atv->get_diskstream()) {
3123 speed = atv->get_diskstream()->speed();
3126 regionview_x_movement = (drag_info.last_frame_position != (jack_nframes_t) (rv->region.position()/speed));
3127 regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3129 //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed);
3130 //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str());
3132 if (regionview_y_movement) {
3134 /* motion between tracks */
3136 list<AudioRegionView*> new_selection;
3138 /* moved to a different audio track. */
3140 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ) {
3142 AudioRegionView* rv2 = (*i);
3144 /* the region that used to be in the old playlist is not
3145 moved to the new one - we make a copy of it. as a result,
3146 any existing editor for the region should no longer be
3150 if (!drag_info.copy) {
3151 rv2->hide_region_editor();
3153 new_selection.push_back (rv2);
3157 /* first, freeze the target tracks */
3159 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3161 Playlist* from_playlist;
3162 Playlist* to_playlist;
3164 double ix1, ix2, iy1, iy2;
3166 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3167 (*i)->get_canvas_group()->i2w (ix1, iy1);
3168 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3169 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3171 from_playlist = (*i)->region.playlist();
3172 to_playlist = atv2->playlist();
3174 /* the from_playlist was frozen in the "first_move" case
3175 of the motion handler. the insert can fail,
3176 but that doesn't matter. it just means
3177 we already have the playlist in the list.
3180 motion_frozen_playlists.insert (from_playlist);
3182 /* only freeze the to_playlist once */
3184 insert_result = motion_frozen_playlists.insert(to_playlist);
3185 if (insert_result.second) {
3186 to_playlist->freeze();
3187 session->add_undo(to_playlist->get_memento());
3192 /* now do it again with the actual operations */
3194 for (list<AudioRegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3196 Playlist* from_playlist;
3197 Playlist* to_playlist;
3199 double ix1, ix2, iy1, iy2;
3201 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3202 (*i)->get_canvas_group()->i2w (ix1, iy1);
3203 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3204 AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3206 from_playlist = (*i)->region.playlist();
3207 to_playlist = atv2->playlist();
3209 latest_regionview = 0;
3211 where = (jack_nframes_t) (unit_to_frame (ix1) * speed);
3212 Region* new_region = createRegion ((*i)->region);
3214 from_playlist->remove_region (&((*i)->region));
3216 sigc::connection c = atv2->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3217 to_playlist->add_region (*new_region, where);
3220 if (latest_regionview) {
3221 selection->add (latest_regionview);
3227 /* motion within a single track */
3229 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3233 if (rv->region.locked()) {
3237 if (regionview_x_movement) {
3238 double ownspeed = 1.0;
3239 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3241 if (atv && atv->get_diskstream()) {
3242 ownspeed = atv->get_diskstream()->speed();
3245 /* base the new region position on the current position of the regionview.*/
3247 double ix1, ix2, iy1, iy2;
3249 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3250 rv->get_canvas_group()->i2w (ix1, iy1);
3251 where = (jack_nframes_t) (unit_to_frame (ix1) * ownspeed);
3255 where = rv->region.position();
3258 rv->get_time_axis_view().reveal_dependent_views (*rv);
3260 /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3262 rv->region.set_position (where, (void *) this);
3267 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3269 session->add_redo_no_execute ((*p)->get_memento());
3272 motion_frozen_playlists.clear ();
3275 commit_reversible_command ();
3280 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3282 /* Either add to or set the set the region selection, unless
3283 this is an alignment click (control used)
3286 if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3287 TimeAxisView* tv = &rv.get_time_axis_view();
3288 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3290 if (atv && atv->is_audio_track()) {
3291 speed = atv->get_diskstream()->speed();
3294 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3296 align_region (rv.region, SyncPoint, (jack_nframes_t) (edit_cursor->current_frame * speed));
3298 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3300 align_region (rv.region, End, (jack_nframes_t) (edit_cursor->current_frame * speed));
3304 align_region (rv.region, Start, (jack_nframes_t) (edit_cursor->current_frame * speed));
3310 Editor::show_verbose_time_cursor (jack_nframes_t frame, double offset, double xpos, double ypos)
3321 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3322 case AudioClock::BBT:
3323 session->bbt_time (frame, bbt);
3324 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3327 case AudioClock::SMPTE:
3328 session->smpte_time (frame, smpte);
3329 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3332 case AudioClock::MinSec:
3333 /* XXX fix this to compute min/sec properly */
3334 session->smpte_time (frame, smpte);
3335 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3336 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", smpte.hours, smpte.minutes, secs);
3340 snprintf (buf, sizeof(buf), "%u", frame);
3344 if (xpos >= 0 && ypos >=0) {
3345 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3348 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3350 show_verbose_canvas_cursor ();
3354 Editor::show_verbose_duration_cursor (jack_nframes_t start, jack_nframes_t end, double offset, double xpos, double ypos)
3361 Meter meter_at_start(session->tempo_map().meter_at(start));
3367 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3368 case AudioClock::BBT:
3369 session->bbt_time (start, sbbt);
3370 session->bbt_time (end, ebbt);
3373 /* XXX this computation won't work well if the
3374 user makes a selection that spans any meter changes.
3377 ebbt.bars -= sbbt.bars;
3378 if (ebbt.beats >= sbbt.beats) {
3379 ebbt.beats -= sbbt.beats;
3382 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3384 if (ebbt.ticks >= sbbt.ticks) {
3385 ebbt.ticks -= sbbt.ticks;
3388 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3391 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3394 case AudioClock::SMPTE:
3395 session->smpte_duration (end - start, smpte);
3396 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3399 case AudioClock::MinSec:
3400 /* XXX fix this to compute min/sec properly */
3401 session->smpte_duration (end - start, smpte);
3402 secs = smpte.seconds + ((float) smpte.frames / session->smpte_frames_per_second);
3403 snprintf (buf, sizeof (buf), "%02ld:%02ld:%.4f", smpte.hours, smpte.minutes, secs);
3407 snprintf (buf, sizeof(buf), "%u", end - start);
3411 if (xpos >= 0 && ypos >=0) {
3412 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3415 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3417 show_verbose_canvas_cursor ();
3421 Editor::collect_new_region_view (AudioRegionView* rv)
3423 latest_regionview = rv;
3427 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3429 if (clicked_regionview == 0) {
3433 /* lets try to create new Region for the selection */
3435 vector<AudioRegion*> new_regions;
3436 create_region_from_selection (new_regions);
3438 if (new_regions.empty()) {
3442 /* XXX fix me one day to use all new regions */
3444 Region* region = new_regions.front();
3446 /* add it to the current stream/playlist.
3448 tricky: the streamview for the track will add a new regionview. we will
3449 catch the signal it sends when it creates the regionview to
3450 set the regionview we want to then drag.
3453 latest_regionview = 0;
3454 sigc::connection c = clicked_audio_trackview->view->AudioRegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3456 /* A selection grab currently creates two undo/redo operations, one for
3457 creating the new region and another for moving it.
3460 begin_reversible_command (_("selection grab"));
3462 Playlist* playlist = clicked_trackview->playlist();
3464 session->add_undo (playlist->get_memento ());
3465 clicked_trackview->playlist()->add_region (*region, selection->time[clicked_selection].start);
3466 session->add_redo_no_execute (playlist->get_memento ());
3468 commit_reversible_command ();
3472 if (latest_regionview == 0) {
3473 /* something went wrong */
3477 /* we need to deselect all other regionviews, and select this one
3478 i'm ignoring undo stuff, because the region creation will take care of it */
3479 selection->set (latest_regionview);
3481 drag_info.item = latest_regionview->get_canvas_group();
3482 drag_info.data = latest_regionview;
3483 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3484 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3488 drag_info.last_trackview = clicked_trackview;
3489 drag_info.last_frame_position = latest_regionview->region.position();
3490 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3492 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3496 Editor::cancel_selection ()
3498 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3499 (*i)->hide_selection ();
3501 selection->clear ();
3502 clicked_selection = 0;
3506 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3508 jack_nframes_t start = 0;
3509 jack_nframes_t end = 0;
3515 drag_info.item = item;
3516 drag_info.motion_callback = &Editor::drag_selection;
3517 drag_info.finished_callback = &Editor::end_selection_op;
3522 case CreateSelection:
3524 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3525 drag_info.copy = true;
3527 drag_info.copy = false;
3529 start_grab (event, selector_cursor);
3532 case SelectionStartTrim:
3533 clicked_trackview->order_selection_trims (item, true);
3534 start_grab (event, trimmer_cursor);
3535 start = selection->time[clicked_selection].start;
3536 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3539 case SelectionEndTrim:
3540 clicked_trackview->order_selection_trims (item, false);
3541 start_grab (event, trimmer_cursor);
3542 end = selection->time[clicked_selection].end;
3543 drag_info.pointer_frame_offset = drag_info.grab_frame - end;
3547 start = selection->time[clicked_selection].start;
3549 drag_info.pointer_frame_offset = drag_info.grab_frame - start;
3553 if (selection_op == SelectionMove) {
3554 show_verbose_time_cursor(start, 10);
3556 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3561 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3563 jack_nframes_t start = 0;
3564 jack_nframes_t end = 0;
3565 jack_nframes_t length;
3566 jack_nframes_t pending_position;
3568 if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3569 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3572 pending_position = 0;
3575 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3576 snap_to (pending_position);
3579 /* only alter selection if the current frame is
3580 different from the last frame position (adjusted)
3583 if (pending_position == drag_info.last_pointer_frame) return;
3585 switch (selection_op) {
3586 case CreateSelection:
3588 if (drag_info.first_move) {
3589 snap_to (drag_info.grab_frame);
3592 if (pending_position < drag_info.grab_frame) {
3593 start = pending_position;
3594 end = drag_info.grab_frame;
3596 end = pending_position;
3597 start = drag_info.grab_frame;
3600 /* first drag: Either add to the selection
3601 or create a new selection->
3604 if (drag_info.first_move) {
3606 begin_reversible_command (_("range selection"));
3608 if (drag_info.copy) {
3609 /* adding to the selection */
3610 clicked_selection = selection->add (start, end);
3611 drag_info.copy = false;
3613 /* new selection-> */
3614 clicked_selection = selection->set (clicked_trackview, start, end);
3619 case SelectionStartTrim:
3621 if (drag_info.first_move) {
3622 begin_reversible_command (_("trim selection start"));
3625 start = selection->time[clicked_selection].start;
3626 end = selection->time[clicked_selection].end;
3628 if (pending_position > end) {
3631 start = pending_position;
3635 case SelectionEndTrim:
3637 if (drag_info.first_move) {
3638 begin_reversible_command (_("trim selection end"));
3641 start = selection->time[clicked_selection].start;
3642 end = selection->time[clicked_selection].end;
3644 if (pending_position < start) {
3647 end = pending_position;
3654 if (drag_info.first_move) {
3655 begin_reversible_command (_("move selection"));
3658 start = selection->time[clicked_selection].start;
3659 end = selection->time[clicked_selection].end;
3661 length = end - start;
3663 start = pending_position;
3666 end = start + length;
3671 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3672 start_canvas_autoscroll (1);
3676 selection->replace (clicked_selection, start, end);
3679 drag_info.last_pointer_frame = pending_position;
3680 drag_info.first_move = false;
3682 if (selection_op == SelectionMove) {
3683 show_verbose_time_cursor(start, 10);
3685 show_verbose_time_cursor(pending_position, 10);
3690 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3692 if (!drag_info.first_move) {
3693 drag_selection (item, event);
3694 /* XXX this is not object-oriented programming at all. ick */
3695 if (selection->time.consolidate()) {
3696 selection->TimeChanged ();
3698 commit_reversible_command ();
3700 /* just a click, no pointer movement.*/
3702 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3704 selection->clear_time();
3709 /* XXX what happens if its a music selection? */
3710 session->set_audio_range (selection->time);
3711 stop_canvas_autoscroll ();
3715 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3718 TimeAxisView* tvp = clicked_trackview;
3719 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3721 if (tv && tv->is_audio_track()) {
3722 speed = tv->get_diskstream()->speed();
3725 jack_nframes_t region_start = (jack_nframes_t) (clicked_regionview->region.position() / speed);
3726 jack_nframes_t region_end = (jack_nframes_t) (clicked_regionview->region.last_frame() / speed);
3727 jack_nframes_t region_length = (jack_nframes_t) (clicked_regionview->region.length() / speed);
3729 motion_frozen_playlists.clear();
3731 //drag_info.item = clicked_regionview->get_name_highlight();
3732 drag_info.item = item;
3733 drag_info.motion_callback = &Editor::trim_motion_callback;
3734 drag_info.finished_callback = &Editor::trim_finished_callback;
3736 start_grab (event, trimmer_cursor);
3738 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3739 trim_op = ContentsTrim;
3741 /* These will get overridden for a point trim.*/
3742 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3743 /* closer to start */
3744 trim_op = StartTrim;
3745 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3753 show_verbose_time_cursor(region_start, 10);
3756 show_verbose_time_cursor(region_end, 10);
3759 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3765 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3767 AudioRegionView* rv = clicked_regionview;
3768 jack_nframes_t frame_delta = 0;
3769 bool left_direction;
3770 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3772 /* snap modifier works differently here..
3773 its' current state has to be passed to the
3774 various trim functions in order to work properly
3778 TimeAxisView* tvp = clicked_trackview;
3779 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3780 pair<set<Playlist*>::iterator,bool> insert_result;
3782 if (tv && tv->is_audio_track()) {
3783 speed = tv->get_diskstream()->speed();
3786 if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3787 left_direction = true;
3789 left_direction = false;
3793 snap_to (drag_info.current_pointer_frame);
3796 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3800 if (drag_info.first_move) {
3806 trim_type = "Region start trim";
3809 trim_type = "Region end trim";
3812 trim_type = "Region content trim";
3816 begin_reversible_command (trim_type);
3818 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3819 (*i)->region.freeze ();
3820 (*i)->temporarily_hide_envelope ();
3822 Playlist * pl = (*i)->region.playlist();
3823 insert_result = motion_frozen_playlists.insert (pl);
3824 if (insert_result.second) {
3825 session->add_undo (pl->get_memento());
3830 if (left_direction) {
3831 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
3833 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
3838 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region.first_frame()/speed)) {
3841 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3842 single_start_trim (**i, frame_delta, left_direction, obey_snap);
3848 if ((left_direction == true) && (drag_info.current_pointer_frame > (jack_nframes_t) (rv->region.last_frame()/speed))) {
3851 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i) {
3852 single_end_trim (**i, frame_delta, left_direction, obey_snap);
3859 bool swap_direction = false;
3861 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3862 swap_direction = true;
3865 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
3866 i != selection->audio_regions.by_layer().end(); ++i)
3868 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
3876 show_verbose_time_cursor((jack_nframes_t) (rv->region.position()/speed), 10);
3879 show_verbose_time_cursor((jack_nframes_t) (rv->region.last_frame()/speed), 10);
3882 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
3886 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
3887 drag_info.first_move = false;
3891 Editor::single_contents_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
3893 Region& region (rv.region);
3895 if (region.locked()) {
3899 jack_nframes_t new_bound;
3902 TimeAxisView* tvp = clicked_trackview;
3903 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3905 if (tv && tv->is_audio_track()) {
3906 speed = tv->get_diskstream()->speed();
3909 if (left_direction) {
3910 if (swap_direction) {
3911 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
3913 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
3916 if (swap_direction) {
3917 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
3919 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
3924 snap_to (new_bound);
3926 region.trim_start ((jack_nframes_t) (new_bound * speed), this);
3927 rv.region_changed (StartChanged);
3931 Editor::single_start_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
3933 Region& region (rv.region);
3935 if (region.locked()) {
3939 jack_nframes_t new_bound;
3942 TimeAxisView* tvp = clicked_trackview;
3943 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3945 if (tv && tv->is_audio_track()) {
3946 speed = tv->get_diskstream()->speed();
3949 if (left_direction) {
3950 new_bound = (jack_nframes_t) (region.position()/speed) - frame_delta;
3952 new_bound = (jack_nframes_t) (region.position()/speed) + frame_delta;
3956 snap_to (new_bound, (left_direction ? 0 : 1));
3959 region.trim_front ((jack_nframes_t) (new_bound * speed), this);
3961 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
3965 Editor::single_end_trim (AudioRegionView& rv, jack_nframes_t frame_delta, bool left_direction, bool obey_snap)
3967 Region& region (rv.region);
3969 if (region.locked()) {
3973 jack_nframes_t new_bound;
3976 TimeAxisView* tvp = clicked_trackview;
3977 AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3979 if (tv && tv->is_audio_track()) {
3980 speed = tv->get_diskstream()->speed();
3983 if (left_direction) {
3984 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) - frame_delta;
3986 new_bound = (jack_nframes_t) ((region.last_frame() + 1)/speed) + frame_delta;
3990 snap_to (new_bound);
3992 region.trim_end ((jack_nframes_t) (new_bound * speed), this);
3993 rv.region_changed (LengthChanged);
3997 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3999 if (!drag_info.first_move) {
4000 trim_motion_callback (item, event);
4002 if (!clicked_regionview->get_selected()) {
4003 thaw_region_after_trim (*clicked_regionview);
4006 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4007 i != selection->audio_regions.by_layer().end(); ++i)
4009 thaw_region_after_trim (**i);
4013 for (set<Playlist*>::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4015 session->add_redo_no_execute ((*p)->get_memento());
4018 motion_frozen_playlists.clear ();
4020 commit_reversible_command();
4022 /* no mouse movement */
4028 Editor::point_trim (GdkEvent* event)
4030 AudioRegionView* rv = clicked_regionview;
4031 jack_nframes_t new_bound = drag_info.current_pointer_frame;
4033 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4034 snap_to (new_bound);
4037 /* Choose action dependant on which button was pressed */
4038 switch (event->button.button) {
4040 trim_op = StartTrim;
4041 begin_reversible_command (_("Start point trim"));
4043 if (rv->get_selected()) {
4045 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin();
4046 i != selection->audio_regions.by_layer().end(); ++i)
4048 if (!(*i)->region.locked()) {
4049 session->add_undo ((*i)->region.playlist()->get_memento());
4050 (*i)->region.trim_front (new_bound, this);
4051 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4057 if (!rv->region.locked()) {
4058 session->add_undo (rv->region.playlist()->get_memento());
4059 rv->region.trim_front (new_bound, this);
4060 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4064 commit_reversible_command();
4069 begin_reversible_command (_("End point trim"));
4071 if (rv->get_selected()) {
4073 for (list<AudioRegionView*>::const_iterator i = selection->audio_regions.by_layer().begin(); i != selection->audio_regions.by_layer().end(); ++i)
4075 if (!(*i)->region.locked()) {
4076 session->add_undo ((*i)->region.playlist()->get_memento());
4077 (*i)->region.trim_end (new_bound, this);
4078 session->add_redo_no_execute ((*i)->region.playlist()->get_memento());
4084 if (!rv->region.locked()) {
4085 session->add_undo (rv->region.playlist()->get_memento());
4086 rv->region.trim_end (new_bound, this);
4087 session->add_redo_no_execute (rv->region.playlist()->get_memento());
4091 commit_reversible_command();
4100 Editor::thaw_region_after_trim (AudioRegionView& rv)
4102 Region& region (rv.region);
4104 if (region.locked()) {
4108 region.thaw (_("trimmed region"));
4109 session->add_redo_no_execute (region.playlist()->get_memento());
4111 rv.unhide_envelope ();
4115 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4120 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4121 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4125 Location* location = find_location_from_marker (marker, is_start);
4126 location->set_hidden (true, this);
4131 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4138 drag_info.item = item;
4139 drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4140 drag_info.finished_callback = &Editor::end_range_markerbar_op;
4142 range_marker_op = op;
4144 if (!temp_location) {
4145 temp_location = new Location;
4149 case CreateRangeMarker:
4150 case CreateTransportMarker:
4152 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4153 drag_info.copy = true;
4155 drag_info.copy = false;
4157 start_grab (event, selector_cursor);
4161 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4166 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4168 jack_nframes_t start = 0;
4169 jack_nframes_t end = 0;
4170 ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4172 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4173 snap_to (drag_info.current_pointer_frame);
4176 /* only alter selection if the current frame is
4177 different from the last frame position.
4180 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4182 switch (range_marker_op) {
4183 case CreateRangeMarker:
4184 case CreateTransportMarker:
4185 if (drag_info.first_move) {
4186 snap_to (drag_info.grab_frame);
4189 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4190 start = drag_info.current_pointer_frame;
4191 end = drag_info.grab_frame;
4193 end = drag_info.current_pointer_frame;
4194 start = drag_info.grab_frame;
4197 /* first drag: Either add to the selection
4198 or create a new selection->
4201 if (drag_info.first_move) {
4203 temp_location->set (start, end);
4207 update_marker_drag_item (temp_location);
4208 range_marker_drag_rect->show();
4209 range_marker_drag_rect->raise_to_top();
4215 if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4216 start_canvas_autoscroll (1);
4220 temp_location->set (start, end);
4222 double x1 = frame_to_pixel (start);
4223 double x2 = frame_to_pixel (end);
4224 crect->property_x1() = x1;
4225 crect->property_x2() = x2;
4227 update_marker_drag_item (temp_location);
4230 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4231 drag_info.first_move = false;
4233 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);
4238 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4240 Location * newloc = 0;
4242 if (!drag_info.first_move) {
4243 drag_range_markerbar_op (item, event);
4245 switch (range_marker_op) {
4246 case CreateRangeMarker:
4247 begin_reversible_command (_("new range marker"));
4248 session->add_undo (session->locations()->get_memento());
4249 newloc = new Location(temp_location->start(), temp_location->end(), "unnamed");
4250 session->locations()->add (newloc, true);
4251 session->add_redo_no_execute (session->locations()->get_memento());
4252 commit_reversible_command ();
4254 range_bar_drag_rect->hide();
4255 range_marker_drag_rect->hide();
4258 case CreateTransportMarker:
4259 // popup menu to pick loop or punch
4260 new_transport_marker_context_menu (&event->button, item);
4265 /* just a click, no pointer movement.*/
4267 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4274 stop_canvas_autoscroll ();
4280 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4282 drag_info.item = item;
4283 drag_info.motion_callback = &Editor::drag_mouse_zoom;
4284 drag_info.finished_callback = &Editor::end_mouse_zoom;
4286 start_grab (event, zoom_cursor);
4288 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4292 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4294 jack_nframes_t start;
4297 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4298 snap_to (drag_info.current_pointer_frame);
4300 if (drag_info.first_move) {
4301 snap_to (drag_info.grab_frame);
4305 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4307 /* base start and end on initial click position */
4308 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4309 start = drag_info.current_pointer_frame;
4310 end = drag_info.grab_frame;
4312 end = drag_info.current_pointer_frame;
4313 start = drag_info.grab_frame;
4318 if (drag_info.first_move) {
4320 zoom_rect->raise_to_top();
4323 reposition_zoom_rect(start, end);
4325 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4326 drag_info.first_move = false;
4328 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4333 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4335 if (!drag_info.first_move) {
4336 drag_mouse_zoom (item, event);
4338 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4339 temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4341 temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4344 temporal_zoom_to_frame (false, drag_info.grab_frame);
4346 temporal_zoom_step (false);
4347 center_screen (drag_info.grab_frame);
4355 Editor::reposition_zoom_rect (jack_nframes_t start, jack_nframes_t end)
4357 double x1 = frame_to_pixel (start);
4358 double x2 = frame_to_pixel (end);
4359 double y2 = canvas_height - 2;
4361 zoom_rect->property_x1() = x1;
4362 zoom_rect->property_y1() = 1.0;
4363 zoom_rect->property_x2() = x2;
4364 zoom_rect->property_y2() = y2;
4368 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4370 drag_info.item = item;
4371 drag_info.motion_callback = &Editor::drag_rubberband_select;
4372 drag_info.finished_callback = &Editor::end_rubberband_select;
4374 start_grab (event, cross_hair_cursor);
4376 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4380 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4382 jack_nframes_t start;
4387 /* use a bigger drag threshold than the default */
4389 if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4393 // if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4394 // snap_to (drag_info.current_pointer_frame);
4396 // if (drag_info.first_move) {
4397 // snap_to (drag_info.grab_frame);
4402 /* base start and end on initial click position */
4403 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4404 start = drag_info.current_pointer_frame;
4405 end = drag_info.grab_frame;
4407 end = drag_info.current_pointer_frame;
4408 start = drag_info.grab_frame;
4411 if (drag_info.current_pointer_y < drag_info.grab_y) {
4412 y1 = drag_info.current_pointer_y;
4413 y2 = drag_info.grab_y;
4416 y2 = drag_info.current_pointer_y;
4417 y1 = drag_info.grab_y;
4421 if (start != end || y1 != y2) {
4423 double x1 = frame_to_pixel (start);
4424 double x2 = frame_to_pixel (end);
4426 rubberband_rect->property_x1() = x1;
4427 rubberband_rect->property_y1() = y1;
4428 rubberband_rect->property_x2() = x2;
4429 rubberband_rect->property_y2() = y2;
4431 rubberband_rect->show();
4432 rubberband_rect->raise_to_top();
4434 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4435 drag_info.first_move = false;
4437 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4442 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4444 if (!drag_info.first_move) {
4446 drag_rubberband_select (item, event);
4449 if (drag_info.current_pointer_y < drag_info.grab_y) {
4450 y1 = drag_info.current_pointer_y;
4451 y2 = drag_info.grab_y;
4454 y2 = drag_info.current_pointer_y;
4455 y1 = drag_info.grab_y;
4459 bool add = Keyboard::modifier_state_contains (event->button.state, Keyboard::Shift);
4462 begin_reversible_command (_("select regions"));
4464 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4465 commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, add);
4467 commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, add);
4471 commit_reversible_command ();
4475 selection->clear_audio_regions();
4476 selection->clear_points ();
4477 selection->clear_lines ();
4480 rubberband_rect->hide();
4485 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4487 using namespace Gtkmm2ext;
4489 ArdourPrompter prompter (false);
4491 prompter.set_prompt (_("Name for region:"));
4492 prompter.set_initial_text (clicked_regionview->region.name());
4493 prompter.show_all ();
4494 switch (prompter.run ()) {
4495 case Gtk::RESPONSE_ACCEPT:
4497 prompter.get_result(str);
4499 clicked_regionview->region.set_name (str);
4507 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4509 drag_info.item = item;
4510 drag_info.motion_callback = &Editor::time_fx_motion;
4511 drag_info.finished_callback = &Editor::end_time_fx;
4515 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4519 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4521 AudioRegionView* rv = clicked_regionview;
4523 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4524 snap_to (drag_info.current_pointer_frame);
4527 if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4531 if (drag_info.current_pointer_frame > rv->region.position()) {
4532 rv->get_time_axis_view().show_timestretch (rv->region.position(), drag_info.current_pointer_frame);
4535 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4536 drag_info.first_move = false;
4538 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4542 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4544 clicked_regionview->get_time_axis_view().hide_timestretch ();
4546 if (drag_info.first_move) {
4550 jack_nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region.position();
4551 float percentage = (float) ((double) newlen - (double) clicked_regionview->region.length()) / ((double) newlen) * 100.0f;
4553 begin_reversible_command (_("timestretch"));
4555 if (run_timestretch (selection->audio_regions, percentage) == 0) {
4556 session->commit_reversible_command ();
4561 Editor::mouse_brush_insert_region (AudioRegionView* rv, jack_nframes_t pos)
4563 /* no brushing without a useful snap setting */
4565 switch (snap_mode) {
4567 return; /* can't work because it allows region to be placed anywhere */
4572 switch (snap_type) {
4575 case SnapToEditCursor:
4582 /* don't brush a copy over the original */
4584 if (pos == rv->region.position()) {
4588 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(&rv->get_time_axis_view());
4590 if (atv == 0 || !atv->is_audio_track()) {
4594 Playlist* playlist = atv->playlist();
4595 double speed = atv->get_diskstream()->speed();
4597 session->add_undo (playlist->get_memento());
4598 playlist->add_region (*(new AudioRegion (rv->region)), (jack_nframes_t) (pos * speed));
4599 session->add_redo_no_execute (playlist->get_memento());
4601 // playlist is frozen, so we have to update manually
4603 playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
4607 Editor::track_height_step_timeout ()
4610 struct timeval delta;
4612 gettimeofday (&now, 0);
4613 timersub (&now, &last_track_height_step_timestamp, &delta);
4615 if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4616 current_stepping_trackview = 0;