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 "pbd/enumwriter.h"
30 #include <gtkmm2ext/utils.h>
31 #include <gtkmm2ext/tearoff.h>
32 #include "pbd/memento_command.h"
33 #include "pbd/basename.h"
35 #include "ardour_ui.h"
38 #include "time_axis_view.h"
39 #include "audio_time_axis.h"
40 #include "audio_region_view.h"
41 #include "midi_region_view.h"
43 #include "streamview.h"
44 #include "region_gain_line.h"
45 #include "automation_time_axis.h"
46 #include "control_point.h"
49 #include "selection.h"
52 #include "rgb_macros.h"
53 #include "control_point_dialog.h"
54 #include "editor_drag.h"
56 #include "ardour/types.h"
57 #include "ardour/profile.h"
58 #include "ardour/route.h"
59 #include "ardour/audio_track.h"
60 #include "ardour/audio_diskstream.h"
61 #include "ardour/midi_diskstream.h"
62 #include "ardour/playlist.h"
63 #include "ardour/audioplaylist.h"
64 #include "ardour/audioregion.h"
65 #include "ardour/midi_region.h"
66 #include "ardour/dB.h"
67 #include "ardour/utils.h"
68 #include "ardour/region_factory.h"
69 #include "ardour/source_factory.h"
70 #include "ardour/session.h"
77 using namespace ARDOUR;
80 using namespace Editing;
81 using Gtkmm2ext::Keyboard;
84 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
88 Gdk::ModifierType mask;
89 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
90 Glib::RefPtr<const Gdk::Window> pointer_window;
96 pointer_window = canvas_window->get_pointer (x, y, mask);
98 if (pointer_window == track_canvas->get_bin_window()) {
101 in_track_canvas = true;
104 in_track_canvas = false;
109 event.type = GDK_BUTTON_RELEASE;
113 where = event_frame (&event, 0, 0);
118 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
132 switch (event->type) {
133 case GDK_BUTTON_RELEASE:
134 case GDK_BUTTON_PRESS:
135 case GDK_2BUTTON_PRESS:
136 case GDK_3BUTTON_PRESS:
138 *pcx = event->button.x;
139 *pcy = event->button.y;
140 _trackview_group->w2i(*pcx, *pcy);
142 case GDK_MOTION_NOTIFY:
144 *pcx = event->motion.x;
145 *pcy = event->motion.y;
146 _trackview_group->w2i(*pcx, *pcy);
148 case GDK_ENTER_NOTIFY:
149 case GDK_LEAVE_NOTIFY:
150 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
153 case GDK_KEY_RELEASE:
154 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
157 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
161 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
162 position is negative (as can be the case with motion events in particular),
163 the frame location is always positive.
166 return pixel_to_frame (*pcx);
170 Editor::which_grabber_cursor ()
172 Gdk::Cursor* c = grabber_cursor;
174 if (_internal_editing) {
175 switch (mouse_mode) {
177 c = midi_pencil_cursor;
185 c = midi_resize_cursor;
194 switch (_edit_point) {
196 c = grabber_edit_point_cursor;
207 Editor::set_canvas_cursor ()
209 if (_internal_editing) {
211 switch (mouse_mode) {
213 current_canvas_cursor = midi_pencil_cursor;
217 current_canvas_cursor = which_grabber_cursor();
221 current_canvas_cursor = midi_resize_cursor;
230 switch (mouse_mode) {
232 current_canvas_cursor = selector_cursor;
236 current_canvas_cursor = which_grabber_cursor();
240 current_canvas_cursor = cross_hair_cursor;
244 current_canvas_cursor = zoom_cursor;
248 current_canvas_cursor = time_fx_cursor; // just use playhead
252 current_canvas_cursor = speaker_cursor;
258 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
263 Editor::set_mouse_mode (MouseMode m, bool force)
269 if (!force && m == mouse_mode) {
273 Glib::RefPtr<Action> act;
277 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
281 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
285 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
289 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
293 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
297 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
303 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
306 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
307 tact->set_active (false);
308 tact->set_active (true);
312 Editor::mouse_mode_toggled (MouseMode m)
318 if (mouse_mode != MouseRange) {
320 /* in all modes except range, hide the range selection,
321 show the object (region) selection.
324 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
325 (*i)->set_should_show_selection (true);
327 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
328 (*i)->hide_selection ();
334 in range mode, show the range selection.
337 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
338 (*i)->show_selection (selection->time);
342 set_canvas_cursor ();
346 Editor::step_mouse_mode (bool next)
348 switch (current_mouse_mode()) {
351 if (Profile->get_sae()) {
352 set_mouse_mode (MouseZoom);
354 set_mouse_mode (MouseRange);
357 set_mouse_mode (MouseTimeFX);
362 if (next) set_mouse_mode (MouseZoom);
363 else set_mouse_mode (MouseObject);
368 if (Profile->get_sae()) {
369 set_mouse_mode (MouseTimeFX);
371 set_mouse_mode (MouseGain);
374 if (Profile->get_sae()) {
375 set_mouse_mode (MouseObject);
377 set_mouse_mode (MouseRange);
383 if (next) set_mouse_mode (MouseTimeFX);
384 else set_mouse_mode (MouseZoom);
389 set_mouse_mode (MouseAudition);
391 if (Profile->get_sae()) {
392 set_mouse_mode (MouseZoom);
394 set_mouse_mode (MouseGain);
400 if (next) set_mouse_mode (MouseObject);
401 else set_mouse_mode (MouseTimeFX);
407 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
409 /* in object/audition/timefx/gain-automation mode,
410 any button press sets the selection if the object
411 can be selected. this is a bit of hack, because
412 we want to avoid this if the mouse operation is a
415 note: not dbl-click or triple-click
418 if (((mouse_mode != MouseObject) &&
419 (mouse_mode != MouseAudition || item_type != RegionItem) &&
420 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
421 (mouse_mode != MouseGain) &&
422 (mouse_mode != MouseRange)) ||
424 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
429 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
431 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
433 /* almost no selection action on modified button-2 or button-3 events */
435 if (item_type != RegionItem && event->button.button != 2) {
441 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
442 bool press = (event->type == GDK_BUTTON_PRESS);
444 // begin_reversible_command (_("select on click"));
448 if (mouse_mode != MouseRange) {
449 set_selected_regionview_from_click (press, op, true);
450 } else if (event->type == GDK_BUTTON_PRESS) {
451 set_selected_track_as_side_effect ();
455 case RegionViewNameHighlight:
457 if (mouse_mode != MouseRange) {
458 set_selected_regionview_from_click (press, op, true);
459 } else if (event->type == GDK_BUTTON_PRESS) {
460 set_selected_track_as_side_effect ();
465 case FadeInHandleItem:
467 case FadeOutHandleItem:
469 if (mouse_mode != MouseRange) {
470 set_selected_regionview_from_click (press, op, true);
471 } else if (event->type == GDK_BUTTON_PRESS) {
472 set_selected_track_as_side_effect ();
476 case ControlPointItem:
477 set_selected_track_as_side_effect ();
478 if (mouse_mode != MouseRange) {
479 set_selected_control_point_from_click (op, false);
484 /* for context click, select track */
485 if (event->button.button == 3) {
486 set_selected_track_as_side_effect ();
490 case AutomationTrackItem:
491 set_selected_track_as_side_effect (true);
500 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
503 _drag->item()->ungrab (event->button.time);
508 /* single mouse clicks on any of these item types operate
509 independent of mouse mode, mostly because they are
510 not on the main track canvas or because we want
515 case PlayheadCursorItem:
517 _drag = new CursorDrag (this, item, true);
518 _drag->start_grab (event);
522 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
523 hide_marker (item, event);
526 _drag = new MarkerDrag (this, item);
527 _drag->start_grab (event);
531 case TempoMarkerItem:
533 _drag = new TempoMarkerDrag (
536 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
538 _drag->start_grab (event);
541 case MeterMarkerItem:
543 _drag = new MeterMarkerDrag (
546 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
548 _drag->start_grab (event);
554 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
556 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
557 _drag->start_grab (event);
563 case RangeMarkerBarItem:
565 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
566 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
568 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker);
570 _drag->start_grab (event);
574 case CdMarkerBarItem:
576 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
577 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
579 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker);
581 _drag->start_grab (event);
585 case TransportMarkerBarItem:
587 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
588 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
590 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker);
592 _drag->start_grab (event);
600 switch (mouse_mode) {
603 case StartSelectionTrimItem:
605 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim);
606 _drag->start_grab (event);
609 case EndSelectionTrimItem:
611 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim);
612 _drag->start_grab (event);
616 if (Keyboard::modifier_state_contains
617 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
618 // contains and not equals because I can't use alt as a modifier alone.
619 start_selection_grab (item, event);
620 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
621 /* grab selection for moving */
623 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionMove);
624 _drag->start_grab (event);
626 /* this was debated, but decided the more common action was to
627 make a new selection */
629 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
630 _drag->start_grab (event);
636 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
637 _drag->start_grab (event);
645 if (internal_editing()) {
646 /* Note: we don't get here if not in internal_editing() mode */
648 _drag = new NoteDrag (this, item);
649 _drag->start_grab (event);
658 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
659 event->type == GDK_BUTTON_PRESS) {
662 _drag = new RubberbandSelectDrag (this, item);
663 _drag->start_grab (event);
665 } else if (event->type == GDK_BUTTON_PRESS) {
668 case FadeInHandleItem:
671 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
672 _drag = new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
673 _drag->start_grab (event);
677 case FadeOutHandleItem:
680 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
681 _drag = new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
682 _drag->start_grab (event);
687 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
688 start_region_copy_grab (item, event, clicked_regionview);
689 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
690 start_region_brush_grab (item, event, clicked_regionview);
692 start_region_grab (item, event, clicked_regionview);
696 case RegionViewNameHighlight:
699 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
700 _drag = new TrimDrag (this, item, clicked_regionview, s.by_layer());
701 _drag->start_grab (event);
708 /* rename happens on edit clicks */
710 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
711 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer());
712 _drag->start_grab (event);
717 case ControlPointItem:
719 _drag = new ControlPointDrag (this, item);
720 _drag->start_grab (event);
724 case AutomationLineItem:
726 _drag = new LineDrag (this, item);
727 _drag->start_grab (event);
732 if (internal_editing()) {
734 _drag = new RegionCreateDrag (this, item, clicked_axisview);
735 _drag->start_grab (event);
739 case AutomationTrackItem:
741 _drag = new RubberbandSelectDrag (this, item);
742 _drag->start_grab (event);
746 case ImageFrameHandleStartItem:
747 imageframe_start_handle_op(item, event) ;
750 case ImageFrameHandleEndItem:
751 imageframe_end_handle_op(item, event) ;
754 case MarkerViewHandleStartItem:
755 markerview_item_start_handle_op(item, event) ;
758 case MarkerViewHandleEndItem:
759 markerview_item_end_handle_op(item, event) ;
763 start_markerview_grab(item, event) ;
766 start_imageframe_grab(item, event) ;
784 /* start a grab so that if we finish after moving
785 we can tell what happened.
788 _drag = new RegionGainDrag (this, item);
789 _drag->start_grab (event, current_canvas_cursor);
794 _drag = new LineDrag (this, item);
795 _drag->start_grab (event);
798 case ControlPointItem:
800 _drag = new ControlPointDrag (this, item);
801 _drag->start_grab (event);
812 case ControlPointItem:
814 _drag = new ControlPointDrag (this, item);
815 _drag->start_grab (event);
818 case AutomationLineItem:
820 _drag = new LineDrag (this, item);
821 _drag->start_grab (event);
825 // XXX need automation mode to identify which
827 // start_line_grab_from_regionview (item, event);
837 if (event->type == GDK_BUTTON_PRESS) {
839 _drag = new MouseZoomDrag (this, item);
840 _drag->start_grab (event);
847 if (internal_editing() && item_type == NoteItem) {
849 _drag = new NoteResizeDrag (this, item);
850 _drag->start_grab (event);
852 } else if (!internal_editing() && item_type == RegionItem) {
854 _drag = new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer());
855 _drag->start_grab (event);
861 _drag = new ScrubDrag (this, item);
862 _drag->start_grab (event);
864 scrub_reverse_distance = 0;
865 last_scrub_x = event->button.x;
866 scrubbing_direction = 0;
867 track_canvas->get_window()->set_cursor (*transparent_cursor);
879 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
881 switch (mouse_mode) {
885 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
886 start_region_copy_grab (item, event, clicked_regionview);
888 start_region_grab (item, event, clicked_regionview);
892 case ControlPointItem:
894 _drag = new ControlPointDrag (this, item);
895 _drag->start_grab (event);
904 case RegionViewNameHighlight:
906 _drag = new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer());
907 _drag->start_grab (event);
913 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer());
914 _drag->start_grab (event);
925 /* relax till release */
931 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
932 temporal_zoom_session();
934 temporal_zoom_to_frame (true, event_frame(event));
947 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
949 if (event->type != GDK_BUTTON_PRESS) {
953 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
956 Glib::RefPtr<const Gdk::Window> pointer_window;
959 Gdk::ModifierType mask;
961 pointer_window = canvas_window->get_pointer (x, y, mask);
963 if (pointer_window == track_canvas->get_bin_window()) {
964 track_canvas->window_to_world (x, y, wx, wy);
965 allow_vertical_scroll = true;
967 allow_vertical_scroll = false;
971 track_canvas->grab_focus();
973 if (_session && _session->actively_recording()) {
977 button_selection (item, event, item_type);
980 (Keyboard::is_delete_event (&event->button) ||
981 Keyboard::is_context_menu_event (&event->button) ||
982 Keyboard::is_edit_event (&event->button))) {
984 /* handled by button release */
988 switch (event->button.button) {
990 return button_press_handler_1 (item, event, item_type);
994 return button_press_handler_2 (item, event, item_type);
1009 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1011 nframes64_t where = event_frame (event, 0, 0);
1012 AutomationTimeAxisView* atv = 0;
1014 /* no action if we're recording */
1016 if (_session && _session->actively_recording()) {
1020 /* first, see if we're finishing a drag ... */
1022 bool were_dragging = false;
1024 bool const r = _drag->end_grab (event);
1028 /* grab dragged, so do nothing else */
1032 were_dragging = true;
1035 button_selection (item, event, item_type);
1037 /* edit events get handled here */
1039 if (_drag == 0 && Keyboard::is_edit_event (&event->button)) {
1040 switch (item_type) {
1045 case TempoMarkerItem:
1046 edit_tempo_marker (item);
1049 case MeterMarkerItem:
1050 edit_meter_marker (item);
1053 case RegionViewName:
1054 if (clicked_regionview->name_active()) {
1055 return mouse_rename_region (item, event);
1059 case ControlPointItem:
1060 edit_control_point (item);
1069 /* context menu events get handled here */
1071 if (Keyboard::is_context_menu_event (&event->button)) {
1075 /* no matter which button pops up the context menu, tell the menu
1076 widget to use button 1 to drive menu selection.
1079 switch (item_type) {
1081 case FadeInHandleItem:
1083 case FadeOutHandleItem:
1084 popup_fade_context_menu (1, event->button.time, item, item_type);
1088 popup_track_context_menu (1, event->button.time, item_type, false, where);
1092 case RegionViewNameHighlight:
1093 case RegionViewName:
1094 popup_track_context_menu (1, event->button.time, item_type, false, where);
1098 popup_track_context_menu (1, event->button.time, item_type, true, where);
1101 case AutomationTrackItem:
1102 popup_track_context_menu (1, event->button.time, item_type, false, where);
1106 case RangeMarkerBarItem:
1107 case TransportMarkerBarItem:
1108 case CdMarkerBarItem:
1111 popup_ruler_menu (where, item_type);
1115 marker_context_menu (&event->button, item);
1118 case TempoMarkerItem:
1119 tm_marker_context_menu (&event->button, item);
1122 case MeterMarkerItem:
1123 tm_marker_context_menu (&event->button, item);
1126 case CrossfadeViewItem:
1127 popup_track_context_menu (1, event->button.time, item_type, false, where);
1131 case ImageFrameItem:
1132 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1134 case ImageFrameTimeAxisItem:
1135 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1137 case MarkerViewItem:
1138 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1140 case MarkerTimeAxisItem:
1141 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1153 /* delete events get handled here */
1155 if (_drag == 0 && Keyboard::is_delete_event (&event->button)) {
1157 switch (item_type) {
1158 case TempoMarkerItem:
1159 remove_tempo_marker (item);
1162 case MeterMarkerItem:
1163 remove_meter_marker (item);
1167 remove_marker (*item, event);
1171 if (mouse_mode == MouseObject) {
1172 remove_clicked_region ();
1176 case ControlPointItem:
1177 if (mouse_mode == MouseGain) {
1178 remove_gain_control_point (item, event);
1180 remove_control_point (item, event);
1190 switch (event->button.button) {
1193 switch (item_type) {
1194 /* see comments in button_press_handler */
1195 case PlayheadCursorItem:
1198 case AutomationLineItem:
1199 case StartSelectionTrimItem:
1200 case EndSelectionTrimItem:
1204 if (!_dragging_playhead) {
1205 snap_to_with_modifier (where, event, 0, true);
1206 mouse_add_new_marker (where);
1210 case CdMarkerBarItem:
1211 if (!_dragging_playhead) {
1212 // if we get here then a dragged range wasn't done
1213 snap_to_with_modifier (where, event, 0, true);
1214 mouse_add_new_marker (where, true);
1219 if (!_dragging_playhead) {
1220 snap_to_with_modifier (where, event);
1221 mouse_add_new_tempo_event (where);
1226 if (!_dragging_playhead) {
1227 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1236 switch (mouse_mode) {
1238 switch (item_type) {
1239 case AutomationTrackItem:
1240 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1242 atv->add_automation_event (item, event, where, event->button.y);
1253 // Gain only makes sense for audio regions
1255 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1259 switch (item_type) {
1261 /* check that we didn't drag before releasing, since
1262 its really annoying to create new control
1263 points when doing this.
1265 if (were_dragging) {
1266 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1271 case AutomationTrackItem:
1272 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1273 add_automation_event (item, event, where, event->button.y);
1282 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1283 if (scrubbing_direction == 0) {
1284 /* no drag, just a click */
1285 switch (item_type) {
1287 play_selected_region ();
1293 /* make sure we stop */
1294 _session->request_transport_speed (0.0);
1308 switch (mouse_mode) {
1311 switch (item_type) {
1313 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1315 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1318 // Button2 click is unused
1331 // x_style_paste (where, 1.0);
1351 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1357 if (last_item_entered != item) {
1358 last_item_entered = item;
1359 last_item_entered_n = 0;
1362 switch (item_type) {
1363 case ControlPointItem:
1364 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1365 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1366 cp->set_visible (true);
1370 at_y = cp->get_y ();
1371 cp->item()->i2w (at_x, at_y);
1375 fraction = 1.0 - (cp->get_y() / cp->line().height());
1377 if (is_drawable() && dynamic_cast<ScrubDrag*> (_drag) == 0) {
1378 track_canvas->get_window()->set_cursor (*fader_cursor);
1381 last_item_entered_n++;
1382 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1383 if (last_item_entered_n < 10) {
1384 show_verbose_canvas_cursor ();
1390 if (mouse_mode == MouseGain) {
1391 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1393 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1394 if (is_drawable()) {
1395 track_canvas->get_window()->set_cursor (*fader_cursor);
1400 case AutomationLineItem:
1401 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1403 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1405 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1407 if (is_drawable()) {
1408 track_canvas->get_window()->set_cursor (*fader_cursor);
1413 case RegionViewNameHighlight:
1414 if (is_drawable() && mouse_mode == MouseObject) {
1415 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1419 case StartSelectionTrimItem:
1420 case EndSelectionTrimItem:
1423 case ImageFrameHandleStartItem:
1424 case ImageFrameHandleEndItem:
1425 case MarkerViewHandleStartItem:
1426 case MarkerViewHandleEndItem:
1429 if (is_drawable()) {
1430 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1434 case PlayheadCursorItem:
1435 if (is_drawable()) {
1436 switch (_edit_point) {
1438 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1441 track_canvas->get_window()->set_cursor (*grabber_cursor);
1447 case RegionViewName:
1449 /* when the name is not an active item, the entire name highlight is for trimming */
1451 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1452 if (mouse_mode == MouseObject && is_drawable()) {
1453 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1459 case AutomationTrackItem:
1460 if (is_drawable()) {
1461 Gdk::Cursor *cursor;
1462 switch (mouse_mode) {
1464 cursor = selector_cursor;
1467 cursor = zoom_cursor;
1470 cursor = cross_hair_cursor;
1474 track_canvas->get_window()->set_cursor (*cursor);
1476 AutomationTimeAxisView* atv;
1477 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1478 clear_entered_track = false;
1479 set_entered_track (atv);
1485 case RangeMarkerBarItem:
1486 case TransportMarkerBarItem:
1487 case CdMarkerBarItem:
1490 if (is_drawable()) {
1491 track_canvas->get_window()->set_cursor (*timebar_cursor);
1496 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1499 entered_marker = marker;
1500 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1502 case MeterMarkerItem:
1503 case TempoMarkerItem:
1504 if (is_drawable()) {
1505 track_canvas->get_window()->set_cursor (*timebar_cursor);
1508 case FadeInHandleItem:
1509 case FadeOutHandleItem:
1510 if (mouse_mode == MouseObject) {
1511 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1513 rect->property_fill_color_rgba() = 0;
1514 rect->property_outline_pixels() = 1;
1523 /* second pass to handle entered track status in a comprehensible way.
1526 switch (item_type) {
1528 case AutomationLineItem:
1529 case ControlPointItem:
1530 /* these do not affect the current entered track state */
1531 clear_entered_track = false;
1534 case AutomationTrackItem:
1535 /* handled above already */
1539 set_entered_track (0);
1547 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1556 switch (item_type) {
1557 case ControlPointItem:
1558 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1559 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1560 if (cp->line().npoints() > 1 && !cp->selected()) {
1561 cp->set_visible (false);
1565 if (is_drawable()) {
1566 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1569 hide_verbose_canvas_cursor ();
1572 case RegionViewNameHighlight:
1573 case StartSelectionTrimItem:
1574 case EndSelectionTrimItem:
1575 case PlayheadCursorItem:
1578 case ImageFrameHandleStartItem:
1579 case ImageFrameHandleEndItem:
1580 case MarkerViewHandleStartItem:
1581 case MarkerViewHandleEndItem:
1584 if (is_drawable()) {
1585 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1590 case AutomationLineItem:
1591 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1593 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1595 line->property_fill_color_rgba() = al->get_line_color();
1597 if (is_drawable()) {
1598 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1602 case RegionViewName:
1603 /* see enter_handler() for notes */
1604 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1605 if (is_drawable() && mouse_mode == MouseObject) {
1606 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1611 case RangeMarkerBarItem:
1612 case TransportMarkerBarItem:
1613 case CdMarkerBarItem:
1617 if (is_drawable()) {
1618 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1623 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1627 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1628 location_flags_changed (loc, this);
1631 case MeterMarkerItem:
1632 case TempoMarkerItem:
1634 if (is_drawable()) {
1635 track_canvas->get_window()->set_cursor (*timebar_cursor);
1640 case FadeInHandleItem:
1641 case FadeOutHandleItem:
1642 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1644 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1646 rect->property_fill_color_rgba() = rv->get_fill_color();
1647 rect->property_outline_pixels() = 0;
1652 case AutomationTrackItem:
1653 if (is_drawable()) {
1654 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1655 clear_entered_track = true;
1656 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1668 Editor::left_automation_track ()
1670 if (clear_entered_track) {
1671 set_entered_track (0);
1672 clear_entered_track = false;
1682 if (scrubbing_direction == 0) {
1684 _session->request_locate (_drag->current_pointer_frame(), false);
1685 _session->request_transport_speed (0.1);
1686 scrubbing_direction = 1;
1690 if (last_scrub_x > _drag->current_pointer_x()) {
1692 /* pointer moved to the left */
1694 if (scrubbing_direction > 0) {
1696 /* we reversed direction to go backwards */
1699 scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x());
1703 /* still moving to the left (backwards) */
1705 scrub_reversals = 0;
1706 scrub_reverse_distance = 0;
1708 delta = 0.01 * (last_scrub_x - _drag->current_pointer_x());
1709 _session->request_transport_speed (_session->transport_speed() - delta);
1713 /* pointer moved to the right */
1715 if (scrubbing_direction < 0) {
1716 /* we reversed direction to go forward */
1719 scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x);
1722 /* still moving to the right */
1724 scrub_reversals = 0;
1725 scrub_reverse_distance = 0;
1727 delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x);
1728 _session->request_transport_speed (_session->transport_speed() + delta);
1732 /* if there have been more than 2 opposite motion moves detected, or one that moves
1733 back more than 10 pixels, reverse direction
1736 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1738 if (scrubbing_direction > 0) {
1739 /* was forwards, go backwards */
1740 _session->request_transport_speed (-0.1);
1741 scrubbing_direction = -1;
1743 /* was backwards, go forwards */
1744 _session->request_transport_speed (0.1);
1745 scrubbing_direction = 1;
1748 scrub_reverse_distance = 0;
1749 scrub_reversals = 0;
1753 last_scrub_x = _drag->current_pointer_x();
1757 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1759 if (event->motion.is_hint) {
1762 /* We call this so that MOTION_NOTIFY events continue to be
1763 delivered to the canvas. We need to do this because we set
1764 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1765 the density of the events, at the expense of a round-trip
1766 to the server. Given that this will mostly occur on cases
1767 where DISPLAY = :0.0, and given the cost of what the motion
1768 event might do, its a good tradeoff.
1771 track_canvas->get_pointer (x, y);
1774 if (current_stepping_trackview) {
1775 /* don't keep the persistent stepped trackview if the mouse moves */
1776 current_stepping_trackview = 0;
1777 step_timeout.disconnect ();
1780 if (_session && _session->actively_recording()) {
1781 /* Sorry. no dragging stuff around while we record */
1785 bool handled = false;
1787 handled = _drag->motion_handler (event, from_autoscroll);
1794 track_canvas_motion (event);
1799 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1801 ControlPoint* control_point;
1803 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1804 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1808 // We shouldn't remove the first or last gain point
1809 if (control_point->line().is_last_point(*control_point) ||
1810 control_point->line().is_first_point(*control_point)) {
1814 control_point->line().remove_point (*control_point);
1818 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1820 ControlPoint* control_point;
1822 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1823 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1827 control_point->line().remove_point (*control_point);
1831 Editor::edit_control_point (ArdourCanvas::Item* item)
1833 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1836 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1840 ControlPointDialog d (p);
1841 d.set_position (Gtk::WIN_POS_MOUSE);
1844 if (d.run () != RESPONSE_ACCEPT) {
1848 p->line().modify_point_y (*p, d.get_y_fraction ());
1853 Editor::visible_order_range (int* low, int* high) const
1855 *low = TimeAxisView::max_order ();
1858 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1860 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1862 if (!rtv->hidden()) {
1864 if (*high < rtv->order()) {
1865 *high = rtv->order ();
1868 if (*low > rtv->order()) {
1869 *low = rtv->order ();
1876 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1878 /* Either add to or set the set the region selection, unless
1879 this is an alignment click (control used)
1882 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1883 TimeAxisView* tv = &rv.get_time_axis_view();
1884 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1886 if (rtv && rtv->is_track()) {
1887 speed = rtv->get_diskstream()->speed();
1890 nframes64_t where = get_preferred_edit_position();
1894 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1896 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1898 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1900 align_region (rv.region(), End, (nframes64_t) (where * speed));
1904 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1911 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
1914 Timecode::Time timecode;
1917 nframes64_t frame_rate;
1920 if (_session == 0) {
1926 if (Profile->get_sae() || Profile->get_small_screen()) {
1927 m = ARDOUR_UI::instance()->primary_clock.mode();
1929 m = ARDOUR_UI::instance()->secondary_clock.mode();
1933 case AudioClock::BBT:
1934 _session->bbt_time (frame, bbt);
1935 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
1938 case AudioClock::Timecode:
1939 _session->timecode_time (frame, timecode);
1940 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1943 case AudioClock::MinSec:
1944 /* XXX this is copied from show_verbose_duration_cursor() */
1945 frame_rate = _session->frame_rate();
1946 hours = frame / (frame_rate * 3600);
1947 frame = frame % (frame_rate * 3600);
1948 mins = frame / (frame_rate * 60);
1949 frame = frame % (frame_rate * 60);
1950 secs = (float) frame / (float) frame_rate;
1951 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
1955 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
1959 if (xpos >= 0 && ypos >=0) {
1960 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
1962 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset - horizontal_adjustment.get_value(), _drag->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
1964 show_verbose_canvas_cursor ();
1968 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
1971 Timecode::Time timecode;
1975 nframes64_t distance, frame_rate;
1977 Meter meter_at_start(_session->tempo_map().meter_at(start));
1979 if (_session == 0) {
1985 if (Profile->get_sae() || Profile->get_small_screen()) {
1986 m = ARDOUR_UI::instance()->primary_clock.mode ();
1988 m = ARDOUR_UI::instance()->secondary_clock.mode ();
1992 case AudioClock::BBT:
1993 _session->bbt_time (start, sbbt);
1994 _session->bbt_time (end, ebbt);
1997 /* XXX this computation won't work well if the
1998 user makes a selection that spans any meter changes.
2001 ebbt.bars -= sbbt.bars;
2002 if (ebbt.beats >= sbbt.beats) {
2003 ebbt.beats -= sbbt.beats;
2006 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2008 if (ebbt.ticks >= sbbt.ticks) {
2009 ebbt.ticks -= sbbt.ticks;
2012 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2015 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2018 case AudioClock::Timecode:
2019 _session->timecode_duration (end - start, timecode);
2020 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2023 case AudioClock::MinSec:
2024 /* XXX this stuff should be elsewhere.. */
2025 distance = end - start;
2026 frame_rate = _session->frame_rate();
2027 hours = distance / (frame_rate * 3600);
2028 distance = distance % (frame_rate * 3600);
2029 mins = distance / (frame_rate * 60);
2030 distance = distance % (frame_rate * 60);
2031 secs = (float) distance / (float) frame_rate;
2032 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2036 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2040 if (xpos >= 0 && ypos >=0) {
2041 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2044 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset, _drag->current_pointer_y() + offset);
2047 show_verbose_canvas_cursor ();
2051 Editor::collect_new_region_view (RegionView* rv)
2053 latest_regionviews.push_back (rv);
2057 Editor::collect_and_select_new_region_view (RegionView* rv)
2060 latest_regionviews.push_back (rv);
2064 Editor::cancel_selection ()
2066 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2067 (*i)->hide_selection ();
2070 selection->clear ();
2071 clicked_selection = 0;
2076 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
2078 boost::shared_ptr<Region> region (rv.region());
2080 if (region->locked()) {
2084 nframes64_t new_bound;
2087 TimeAxisView* tvp = clicked_axisview;
2088 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2090 if (tv && tv->is_track()) {
2091 speed = tv->get_diskstream()->speed();
2094 if (left_direction) {
2095 if (swap_direction) {
2096 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2098 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2101 if (swap_direction) {
2102 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2104 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2109 snap_to (new_bound);
2111 region->trim_start ((nframes64_t) (new_bound * speed), this);
2112 rv.region_changed (StartChanged);
2116 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2118 boost::shared_ptr<Region> region (rv.region());
2120 if (region->locked()) {
2124 nframes64_t new_bound;
2127 TimeAxisView* tvp = clicked_axisview;
2128 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2130 if (tv && tv->is_track()) {
2131 speed = tv->get_diskstream()->speed();
2134 if (left_direction) {
2135 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2137 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2141 snap_to (new_bound, (left_direction ? 0 : 1));
2144 nframes64_t pre_trim_first_frame = region->first_frame();
2146 region->trim_front ((nframes64_t) (new_bound * speed), this);
2149 //Get the next region on the left of this region and shrink/expand it.
2150 boost::shared_ptr<Playlist> playlist (region->playlist());
2151 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2153 bool regions_touching = false;
2155 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2156 regions_touching = true;
2159 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2160 if (region_left != 0 &&
2161 (region_left->last_frame() > region->first_frame() || regions_touching))
2163 region_left->trim_end(region->first_frame() - 1, this);
2167 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2171 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2173 boost::shared_ptr<Region> region (rv.region());
2175 if (region->locked()) {
2179 nframes64_t new_bound;
2182 TimeAxisView* tvp = clicked_axisview;
2183 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2185 if (tv && tv->is_track()) {
2186 speed = tv->get_diskstream()->speed();
2189 if (left_direction) {
2190 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
2192 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
2196 snap_to (new_bound);
2199 nframes64_t pre_trim_last_frame = region->last_frame();
2201 region->trim_end ((nframes64_t) (new_bound * speed), this);
2204 //Get the next region on the right of this region and shrink/expand it.
2205 boost::shared_ptr<Playlist> playlist (region->playlist());
2206 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2208 bool regions_touching = false;
2210 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)){
2211 regions_touching = true;
2214 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2215 if (region_right != 0 &&
2216 (region_right->first_frame() < region->last_frame() || regions_touching))
2218 region_right->trim_front(region->last_frame() + 1, this);
2221 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2224 rv.region_changed (LengthChanged);
2230 Editor::point_trim (GdkEvent* event)
2232 RegionView* rv = clicked_regionview;
2234 nframes64_t new_bound = _drag->current_pointer_frame();
2236 snap_to_with_modifier (new_bound, event);
2238 /* Choose action dependant on which button was pressed */
2239 switch (event->button.button) {
2241 begin_reversible_command (_("Start point trim"));
2243 if (selection->selected (rv)) {
2244 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2245 i != selection->regions.by_layer().end(); ++i)
2248 cerr << "region view contains null region" << endl;
2251 if (!(*i)->region()->locked()) {
2252 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2253 XMLNode &before = pl->get_state();
2255 (*i)->region()->trim_front (new_bound, this);
2257 XMLNode &after = pl->get_state();
2258 _session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2263 if (!rv->region()->locked()) {
2264 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2265 XMLNode &before = pl->get_state();
2266 rv->region()->trim_front (new_bound, this);
2267 XMLNode &after = pl->get_state();
2268 _session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2272 commit_reversible_command();
2276 begin_reversible_command (_("End point trim"));
2278 if (selection->selected (rv)) {
2280 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2282 if (!(*i)->region()->locked()) {
2283 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2284 XMLNode &before = pl->get_state();
2285 (*i)->region()->trim_end (new_bound, this);
2286 XMLNode &after = pl->get_state();
2287 _session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2293 if (!rv->region()->locked()) {
2294 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2295 XMLNode &before = pl->get_state();
2296 rv->region()->trim_end (new_bound, this);
2297 XMLNode &after = pl->get_state();
2298 _session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
2302 commit_reversible_command();
2311 Editor::thaw_region_after_trim (RegionView& rv)
2313 boost::shared_ptr<Region> region (rv.region());
2315 if (region->locked()) {
2319 region->thaw (_("trimmed region"));
2321 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2324 arv->unhide_envelope ();
2329 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2334 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2335 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2339 Location* location = find_location_from_marker (marker, is_start);
2340 location->set_hidden (true, this);
2345 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2347 double x1 = frame_to_pixel (start);
2348 double x2 = frame_to_pixel (end);
2349 double y2 = full_canvas_height - 1.0;
2351 zoom_rect->property_x1() = x1;
2352 zoom_rect->property_y1() = 1.0;
2353 zoom_rect->property_x2() = x2;
2354 zoom_rect->property_y2() = y2;
2359 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2361 using namespace Gtkmm2ext;
2363 ArdourPrompter prompter (false);
2365 prompter.set_prompt (_("Name for region:"));
2366 prompter.set_initial_text (clicked_regionview->region()->name());
2367 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2368 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2369 prompter.show_all ();
2370 switch (prompter.run ()) {
2371 case Gtk::RESPONSE_ACCEPT:
2373 prompter.get_result(str);
2375 clicked_regionview->region()->set_name (str);
2384 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2386 /* no brushing without a useful snap setting */
2388 switch (_snap_mode) {
2390 return; /* can't work because it allows region to be placed anywhere */
2395 switch (_snap_type) {
2403 /* don't brush a copy over the original */
2405 if (pos == rv->region()->position()) {
2409 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2411 if (rtv == 0 || !rtv->is_track()) {
2415 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2416 double speed = rtv->get_diskstream()->speed();
2418 XMLNode &before = playlist->get_state();
2419 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
2420 XMLNode &after = playlist->get_state();
2421 _session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
2423 // playlist is frozen, so we have to update manually
2425 playlist->Modified(); /* EMIT SIGNAL */
2429 Editor::track_height_step_timeout ()
2431 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2432 current_stepping_trackview = 0;
2439 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2441 assert (region_view);
2443 _region_motion_group->raise_to_top ();
2445 assert (_drag == 0);
2447 if (Config->get_edit_mode() == Splice) {
2448 _drag = new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer());
2450 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2451 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false);
2454 _drag->start_grab (event);
2456 begin_reversible_command (_("move region(s)"));
2458 /* sync the canvas to what we think is its current state */
2459 update_canvas_now();
2463 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2465 assert (region_view);
2466 assert (_drag == 0);
2468 _region_motion_group->raise_to_top ();
2470 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2471 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true);
2472 _drag->start_grab(event);
2476 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2478 assert (region_view);
2479 assert (_drag == 0);
2481 if (Config->get_edit_mode() == Splice) {
2485 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2486 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false);
2487 _drag->start_grab (event);
2489 begin_reversible_command (_("Drag region brush"));
2492 /** Start a grab where a time range is selected, track(s) are selected, and the
2493 * user clicks and drags a region with a modifier in order to create a new region containing
2494 * the section of the clicked region that lies within the time range.
2497 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2499 if (clicked_regionview == 0) {
2503 /* lets try to create new Region for the selection */
2505 vector<boost::shared_ptr<Region> > new_regions;
2506 create_region_from_selection (new_regions);
2508 if (new_regions.empty()) {
2512 /* XXX fix me one day to use all new regions */
2514 boost::shared_ptr<Region> region (new_regions.front());
2516 /* add it to the current stream/playlist.
2518 tricky: the streamview for the track will add a new regionview. we will
2519 catch the signal it sends when it creates the regionview to
2520 set the regionview we want to then drag.
2523 latest_regionviews.clear();
2524 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2526 /* A selection grab currently creates two undo/redo operations, one for
2527 creating the new region and another for moving it.
2530 begin_reversible_command (_("selection grab"));
2532 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2534 XMLNode *before = &(playlist->get_state());
2535 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2536 XMLNode *after = &(playlist->get_state());
2537 _session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
2539 commit_reversible_command ();
2543 if (latest_regionviews.empty()) {
2544 /* something went wrong */
2548 /* we need to deselect all other regionviews, and select this one
2549 i'm ignoring undo stuff, because the region creation will take care of it
2551 selection->set (latest_regionviews);
2553 assert (_drag == 0);
2554 _drag = new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false);
2555 _drag->start_grab (event);
2559 Editor::break_drag ()
2562 _drag->break_drag ();
2567 Editor::set_internal_edit (bool yn)
2569 _internal_editing = yn;
2572 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2573 mouse_select_button.get_image ()->show ();
2575 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2576 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2578 mtv->start_step_editing ();
2581 start_step_editing ();
2585 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2586 mouse_select_button.get_image ()->show ();
2587 stop_step_editing ();
2589 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2590 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2592 mtv->stop_step_editing ();
2597 set_canvas_cursor ();