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"
76 using namespace ARDOUR;
80 using namespace Editing;
83 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
87 Gdk::ModifierType mask;
88 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
89 Glib::RefPtr<const Gdk::Window> pointer_window;
95 pointer_window = canvas_window->get_pointer (x, y, mask);
97 if (pointer_window == track_canvas->get_bin_window()) {
100 in_track_canvas = true;
103 in_track_canvas = false;
108 event.type = GDK_BUTTON_RELEASE;
112 where = event_frame (&event, 0, 0);
117 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
131 switch (event->type) {
132 case GDK_BUTTON_RELEASE:
133 case GDK_BUTTON_PRESS:
134 case GDK_2BUTTON_PRESS:
135 case GDK_3BUTTON_PRESS:
137 *pcx = event->button.x;
138 *pcy = event->button.y;
139 _trackview_group->w2i(*pcx, *pcy);
141 case GDK_MOTION_NOTIFY:
143 *pcx = event->motion.x;
144 *pcy = event->motion.y;
145 _trackview_group->w2i(*pcx, *pcy);
147 case GDK_ENTER_NOTIFY:
148 case GDK_LEAVE_NOTIFY:
149 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
152 case GDK_KEY_RELEASE:
153 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
156 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
160 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
161 position is negative (as can be the case with motion events in particular),
162 the frame location is always positive.
165 return pixel_to_frame (*pcx);
169 Editor::which_grabber_cursor ()
171 Gdk::Cursor* c = grabber_cursor;
173 if (_internal_editing) {
174 switch (mouse_mode) {
176 c = midi_pencil_cursor;
184 c = midi_resize_cursor;
193 switch (_edit_point) {
195 c = grabber_edit_point_cursor;
206 Editor::set_canvas_cursor ()
208 if (_internal_editing) {
210 switch (mouse_mode) {
212 current_canvas_cursor = midi_pencil_cursor;
216 current_canvas_cursor = which_grabber_cursor();
220 current_canvas_cursor = midi_resize_cursor;
229 switch (mouse_mode) {
231 current_canvas_cursor = selector_cursor;
235 current_canvas_cursor = which_grabber_cursor();
239 current_canvas_cursor = cross_hair_cursor;
243 current_canvas_cursor = zoom_cursor;
247 current_canvas_cursor = time_fx_cursor; // just use playhead
251 current_canvas_cursor = speaker_cursor;
257 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
262 Editor::set_mouse_mode (MouseMode m, bool force)
268 if (!force && m == mouse_mode) {
272 Glib::RefPtr<Action> act;
276 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
280 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
284 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
288 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
292 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
296 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
302 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
305 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
306 tact->set_active (false);
307 tact->set_active (true);
311 Editor::mouse_mode_toggled (MouseMode m)
317 if (mouse_mode != MouseRange) {
319 /* in all modes except range, hide the range selection,
320 show the object (region) selection.
323 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
324 (*i)->set_should_show_selection (true);
326 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
327 (*i)->hide_selection ();
333 in range mode,show the range selection.
336 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
337 if ((*i)->get_selected()) {
338 (*i)->show_selection (selection->time);
343 set_canvas_cursor ();
347 Editor::step_mouse_mode (bool next)
349 switch (current_mouse_mode()) {
352 if (Profile->get_sae()) {
353 set_mouse_mode (MouseZoom);
355 set_mouse_mode (MouseRange);
358 set_mouse_mode (MouseTimeFX);
363 if (next) set_mouse_mode (MouseZoom);
364 else set_mouse_mode (MouseObject);
369 if (Profile->get_sae()) {
370 set_mouse_mode (MouseTimeFX);
372 set_mouse_mode (MouseGain);
375 if (Profile->get_sae()) {
376 set_mouse_mode (MouseObject);
378 set_mouse_mode (MouseRange);
384 if (next) set_mouse_mode (MouseTimeFX);
385 else set_mouse_mode (MouseZoom);
390 set_mouse_mode (MouseAudition);
392 if (Profile->get_sae()) {
393 set_mouse_mode (MouseZoom);
395 set_mouse_mode (MouseGain);
401 if (next) set_mouse_mode (MouseObject);
402 else set_mouse_mode (MouseTimeFX);
408 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
410 /* in object/audition/timefx/gain-automation mode,
411 any button press sets the selection if the object
412 can be selected. this is a bit of hack, because
413 we want to avoid this if the mouse operation is a
416 note: not dbl-click or triple-click
419 if (((mouse_mode != MouseObject) &&
420 (mouse_mode != MouseAudition || item_type != RegionItem) &&
421 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
422 (mouse_mode != MouseGain) &&
423 (mouse_mode != MouseRange)) ||
425 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
430 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
432 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
434 /* almost no selection action on modified button-2 or button-3 events */
436 if (item_type != RegionItem && event->button.button != 2) {
442 Selection::Operation op = Keyboard::selection_type (event->button.state);
443 bool press = (event->type == GDK_BUTTON_PRESS);
445 // begin_reversible_command (_("select on click"));
449 if (mouse_mode != MouseRange) {
450 set_selected_regionview_from_click (press, op, true);
451 } else if (event->type == GDK_BUTTON_PRESS) {
452 set_selected_track_as_side_effect ();
456 case RegionViewNameHighlight:
458 if (mouse_mode != MouseRange) {
459 set_selected_regionview_from_click (press, op, true);
460 } else if (event->type == GDK_BUTTON_PRESS) {
461 set_selected_track_as_side_effect ();
466 case FadeInHandleItem:
468 case FadeOutHandleItem:
470 if (mouse_mode != MouseRange) {
471 set_selected_regionview_from_click (press, op, true);
472 } else if (event->type == GDK_BUTTON_PRESS) {
473 set_selected_track_as_side_effect ();
477 case ControlPointItem:
478 set_selected_track_as_side_effect ();
479 if (mouse_mode != MouseRange) {
480 set_selected_control_point_from_click (op, false);
485 /* for context click or range selection, select track */
486 if (event->button.button == 3) {
487 set_selected_track_as_side_effect ();
488 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
489 set_selected_track_as_side_effect ();
493 case AutomationTrackItem:
494 set_selected_track_as_side_effect (true);
503 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
506 _drag->item()->ungrab (event->button.time);
509 /* single mouse clicks on any of these item types operate
510 independent of mouse mode, mostly because they are
511 not on the main track canvas or because we want
516 case PlayheadCursorItem:
518 _drag = new CursorDrag (this, item, true);
519 _drag->start_grab (event);
523 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
524 hide_marker (item, event);
527 _drag = new MarkerDrag (this, item);
528 _drag->start_grab (event);
532 case TempoMarkerItem:
534 _drag = new TempoMarkerDrag (
537 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
539 _drag->start_grab (event);
542 case MeterMarkerItem:
544 _drag = new MeterMarkerDrag (
547 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
549 _drag->start_grab (event);
555 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
557 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
558 _drag->start_grab (event);
564 case RangeMarkerBarItem:
566 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
567 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
569 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker);
571 _drag->start_grab (event);
575 case CdMarkerBarItem:
577 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
578 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
580 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker);
582 _drag->start_grab (event);
586 case TransportMarkerBarItem:
588 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
589 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
591 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker);
593 _drag->start_grab (event);
601 if (internal_editing()) {
605 _drag = new RegionCreateDrag (this, item, clicked_axisview);
606 _drag->start_grab (event);
609 /* Note: we don't get here if not in internal_editing() mode */
610 if (mouse_mode == MouseTimeFX) {
612 _drag = new NoteResizeDrag (this, item);
613 _drag->start_grab (event);
615 } else if (mouse_mode == MouseObject) {
617 _drag = new NoteDrag (this, item);
618 _drag->start_grab (event);
628 switch (mouse_mode) {
631 case StartSelectionTrimItem:
633 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim);
634 _drag->start_grab (event);
637 case EndSelectionTrimItem:
639 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim);
640 _drag->start_grab (event);
644 if (Keyboard::modifier_state_contains
645 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
646 // contains and not equals because I can't use alt as a modifier alone.
647 start_selection_grab (item, event);
648 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
649 /* grab selection for moving */
651 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionMove);
652 _drag->start_grab (event);
654 /* this was debated, but decided the more common action was to
655 make a new selection */
657 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
658 _drag->start_grab (event);
664 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
665 _drag->start_grab (event);
671 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
672 event->type == GDK_BUTTON_PRESS) {
675 _drag = new RubberbandSelectDrag (this, item);
676 _drag->start_grab (event);
678 } else if (event->type == GDK_BUTTON_PRESS) {
681 case FadeInHandleItem:
684 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
685 _drag = new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
686 _drag->start_grab (event);
690 case FadeOutHandleItem:
693 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
694 _drag = new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
695 _drag->start_grab (event);
700 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
701 start_region_copy_grab (item, event, clicked_regionview);
702 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
703 start_region_brush_grab (item, event, clicked_regionview);
705 start_region_grab (item, event, clicked_regionview);
709 case RegionViewNameHighlight:
712 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
713 _drag = new TrimDrag (this, item, clicked_regionview, s.by_layer());
714 _drag->start_grab (event);
721 /* rename happens on edit clicks */
723 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
724 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer());
725 _drag->start_grab (event);
730 case ControlPointItem:
732 _drag = new ControlPointDrag (this, item);
733 _drag->start_grab (event);
737 case AutomationLineItem:
739 _drag = new LineDrag (this, item);
740 _drag->start_grab (event);
745 case AutomationTrackItem:
747 _drag = new RubberbandSelectDrag (this, item);
748 _drag->start_grab (event);
752 case ImageFrameHandleStartItem:
753 imageframe_start_handle_op(item, event) ;
756 case ImageFrameHandleEndItem:
757 imageframe_end_handle_op(item, event) ;
760 case MarkerViewHandleStartItem:
761 markerview_item_start_handle_op(item, event) ;
764 case MarkerViewHandleEndItem:
765 markerview_item_end_handle_op(item, event) ;
769 start_markerview_grab(item, event) ;
772 start_imageframe_grab(item, event) ;
790 /* start a grab so that if we finish after moving
791 we can tell what happened.
794 _drag = new RegionGainDrag (this, item);
795 _drag->start_grab (event, current_canvas_cursor);
800 _drag = new LineDrag (this, item);
801 _drag->start_grab (event);
804 case ControlPointItem:
806 _drag = new ControlPointDrag (this, item);
807 _drag->start_grab (event);
818 case ControlPointItem:
820 _drag = new ControlPointDrag (this, item);
821 _drag->start_grab (event);
824 case AutomationLineItem:
826 _drag = new LineDrag (this, item);
827 _drag->start_grab (event);
831 // XXX need automation mode to identify which
833 // start_line_grab_from_regionview (item, event);
843 if (event->type == GDK_BUTTON_PRESS) {
845 _drag = new MouseZoomDrag (this, item);
846 _drag->start_grab (event);
853 if (item_type == RegionItem) {
855 _drag = new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer());
856 _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);
1254 // Gain only makes sense for audio regions
1256 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1260 switch (item_type) {
1262 /* check that we didn't drag before releasing, since
1263 its really annoying to create new control
1264 points when doing this.
1266 if (were_dragging) {
1267 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1272 case AutomationTrackItem:
1273 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1274 add_automation_event (item, event, where, event->button.y);
1283 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1284 if (scrubbing_direction == 0) {
1285 /* no drag, just a click */
1286 switch (item_type) {
1288 play_selected_region ();
1294 /* make sure we stop */
1295 session->request_transport_speed (0.0);
1309 switch (mouse_mode) {
1312 switch (item_type) {
1314 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1316 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1319 // Button2 click is unused
1332 // x_style_paste (where, 1.0);
1352 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1358 if (last_item_entered != item) {
1359 last_item_entered = item;
1360 last_item_entered_n = 0;
1363 switch (item_type) {
1364 case ControlPointItem:
1365 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1366 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1367 cp->set_visible (true);
1371 at_y = cp->get_y ();
1372 cp->item()->i2w (at_x, at_y);
1376 fraction = 1.0 - (cp->get_y() / cp->line().height());
1378 if (is_drawable() && dynamic_cast<ScrubDrag*> (_drag) == 0) {
1379 track_canvas->get_window()->set_cursor (*fader_cursor);
1382 last_item_entered_n++;
1383 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1384 if (last_item_entered_n < 10) {
1385 show_verbose_canvas_cursor ();
1391 if (mouse_mode == MouseGain) {
1392 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1394 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1395 if (is_drawable()) {
1396 track_canvas->get_window()->set_cursor (*fader_cursor);
1401 case AutomationLineItem:
1402 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1404 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1406 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1408 if (is_drawable()) {
1409 track_canvas->get_window()->set_cursor (*fader_cursor);
1414 case RegionViewNameHighlight:
1415 if (is_drawable() && mouse_mode == MouseObject) {
1416 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1420 case StartSelectionTrimItem:
1421 case EndSelectionTrimItem:
1424 case ImageFrameHandleStartItem:
1425 case ImageFrameHandleEndItem:
1426 case MarkerViewHandleStartItem:
1427 case MarkerViewHandleEndItem:
1430 if (is_drawable()) {
1431 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1435 case PlayheadCursorItem:
1436 if (is_drawable()) {
1437 switch (_edit_point) {
1439 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1442 track_canvas->get_window()->set_cursor (*grabber_cursor);
1448 case RegionViewName:
1450 /* when the name is not an active item, the entire name highlight is for trimming */
1452 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1453 if (mouse_mode == MouseObject && is_drawable()) {
1454 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1460 case AutomationTrackItem:
1461 if (is_drawable()) {
1462 Gdk::Cursor *cursor;
1463 switch (mouse_mode) {
1465 cursor = selector_cursor;
1468 cursor = zoom_cursor;
1471 cursor = cross_hair_cursor;
1475 track_canvas->get_window()->set_cursor (*cursor);
1477 AutomationTimeAxisView* atv;
1478 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1479 clear_entered_track = false;
1480 set_entered_track (atv);
1486 case RangeMarkerBarItem:
1487 case TransportMarkerBarItem:
1488 case CdMarkerBarItem:
1491 if (is_drawable()) {
1492 track_canvas->get_window()->set_cursor (*timebar_cursor);
1497 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1500 entered_marker = marker;
1501 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1503 case MeterMarkerItem:
1504 case TempoMarkerItem:
1505 if (is_drawable()) {
1506 track_canvas->get_window()->set_cursor (*timebar_cursor);
1509 case FadeInHandleItem:
1510 case FadeOutHandleItem:
1511 if (mouse_mode == MouseObject) {
1512 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1514 rect->property_fill_color_rgba() = 0;
1515 rect->property_outline_pixels() = 1;
1524 /* second pass to handle entered track status in a comprehensible way.
1527 switch (item_type) {
1529 case AutomationLineItem:
1530 case ControlPointItem:
1531 /* these do not affect the current entered track state */
1532 clear_entered_track = false;
1535 case AutomationTrackItem:
1536 /* handled above already */
1540 set_entered_track (0);
1548 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1557 switch (item_type) {
1558 case ControlPointItem:
1559 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1560 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1561 if (cp->line().npoints() > 1 && !cp->selected()) {
1562 cp->set_visible (false);
1566 if (is_drawable()) {
1567 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1570 hide_verbose_canvas_cursor ();
1573 case RegionViewNameHighlight:
1574 case StartSelectionTrimItem:
1575 case EndSelectionTrimItem:
1576 case PlayheadCursorItem:
1579 case ImageFrameHandleStartItem:
1580 case ImageFrameHandleEndItem:
1581 case MarkerViewHandleStartItem:
1582 case MarkerViewHandleEndItem:
1585 if (is_drawable()) {
1586 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1591 case AutomationLineItem:
1592 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1594 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1596 line->property_fill_color_rgba() = al->get_line_color();
1598 if (is_drawable()) {
1599 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1603 case RegionViewName:
1604 /* see enter_handler() for notes */
1605 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1606 if (is_drawable() && mouse_mode == MouseObject) {
1607 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1612 case RangeMarkerBarItem:
1613 case TransportMarkerBarItem:
1614 case CdMarkerBarItem:
1618 if (is_drawable()) {
1619 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1624 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1628 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1629 location_flags_changed (loc, this);
1632 case MeterMarkerItem:
1633 case TempoMarkerItem:
1635 if (is_drawable()) {
1636 track_canvas->get_window()->set_cursor (*timebar_cursor);
1641 case FadeInHandleItem:
1642 case FadeOutHandleItem:
1643 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1645 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1647 rect->property_fill_color_rgba() = rv->get_fill_color();
1648 rect->property_outline_pixels() = 0;
1653 case AutomationTrackItem:
1654 if (is_drawable()) {
1655 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1656 clear_entered_track = true;
1657 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1669 Editor::left_automation_track ()
1671 if (clear_entered_track) {
1672 set_entered_track (0);
1673 clear_entered_track = false;
1683 if (scrubbing_direction == 0) {
1685 session->request_locate (_drag->current_pointer_frame(), false);
1686 session->request_transport_speed (0.1);
1687 scrubbing_direction = 1;
1691 if (last_scrub_x > _drag->current_pointer_x()) {
1693 /* pointer moved to the left */
1695 if (scrubbing_direction > 0) {
1697 /* we reversed direction to go backwards */
1700 scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x());
1704 /* still moving to the left (backwards) */
1706 scrub_reversals = 0;
1707 scrub_reverse_distance = 0;
1709 delta = 0.01 * (last_scrub_x - _drag->current_pointer_x());
1710 session->request_transport_speed (session->transport_speed() - delta);
1714 /* pointer moved to the right */
1716 if (scrubbing_direction < 0) {
1717 /* we reversed direction to go forward */
1720 scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x);
1723 /* still moving to the right */
1725 scrub_reversals = 0;
1726 scrub_reverse_distance = 0;
1728 delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x);
1729 session->request_transport_speed (session->transport_speed() + delta);
1733 /* if there have been more than 2 opposite motion moves detected, or one that moves
1734 back more than 10 pixels, reverse direction
1737 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1739 if (scrubbing_direction > 0) {
1740 /* was forwards, go backwards */
1741 session->request_transport_speed (-0.1);
1742 scrubbing_direction = -1;
1744 /* was backwards, go forwards */
1745 session->request_transport_speed (0.1);
1746 scrubbing_direction = 1;
1749 scrub_reverse_distance = 0;
1750 scrub_reversals = 0;
1754 last_scrub_x = _drag->current_pointer_x();
1758 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1760 if (event->motion.is_hint) {
1763 /* We call this so that MOTION_NOTIFY events continue to be
1764 delivered to the canvas. We need to do this because we set
1765 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1766 the density of the events, at the expense of a round-trip
1767 to the server. Given that this will mostly occur on cases
1768 where DISPLAY = :0.0, and given the cost of what the motion
1769 event might do, its a good tradeoff.
1772 track_canvas->get_pointer (x, y);
1775 if (current_stepping_trackview) {
1776 /* don't keep the persistent stepped trackview if the mouse moves */
1777 current_stepping_trackview = 0;
1778 step_timeout.disconnect ();
1781 if (session && session->actively_recording()) {
1782 /* Sorry. no dragging stuff around while we record */
1786 bool handled = false;
1788 handled = _drag->motion_handler (event, from_autoscroll);
1795 track_canvas_motion (event);
1800 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1802 ControlPoint* control_point;
1804 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1805 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1809 // We shouldn't remove the first or last gain point
1810 if (control_point->line().is_last_point(*control_point) ||
1811 control_point->line().is_first_point(*control_point)) {
1815 control_point->line().remove_point (*control_point);
1819 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1821 ControlPoint* control_point;
1823 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1824 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1828 control_point->line().remove_point (*control_point);
1832 Editor::edit_control_point (ArdourCanvas::Item* item)
1834 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1837 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1841 ControlPointDialog d (p);
1842 d.set_position (Gtk::WIN_POS_MOUSE);
1845 if (d.run () != RESPONSE_ACCEPT) {
1849 p->line().modify_point_y (*p, d.get_y_fraction ());
1854 Editor::visible_order_range (int* low, int* high) const
1856 *low = TimeAxisView::max_order ();
1859 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1861 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1863 if (!rtv->hidden()) {
1865 if (*high < rtv->order()) {
1866 *high = rtv->order ();
1869 if (*low > rtv->order()) {
1870 *low = rtv->order ();
1877 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1879 /* Either add to or set the set the region selection, unless
1880 this is an alignment click (control used)
1883 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1884 TimeAxisView* tv = &rv.get_time_axis_view();
1885 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1887 if (rtv && rtv->is_track()) {
1888 speed = rtv->get_diskstream()->speed();
1891 nframes64_t where = get_preferred_edit_position();
1895 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1897 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1899 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1901 align_region (rv.region(), End, (nframes64_t) (where * speed));
1905 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1912 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
1918 nframes64_t frame_rate;
1927 if (Profile->get_sae() || Profile->get_small_screen()) {
1928 m = ARDOUR_UI::instance()->primary_clock.mode();
1930 m = ARDOUR_UI::instance()->secondary_clock.mode();
1934 case AudioClock::BBT:
1935 session->bbt_time (frame, bbt);
1936 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
1939 case AudioClock::SMPTE:
1940 session->smpte_time (frame, smpte);
1941 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
1944 case AudioClock::MinSec:
1945 /* XXX this is copied from show_verbose_duration_cursor() */
1946 frame_rate = session->frame_rate();
1947 hours = frame / (frame_rate * 3600);
1948 frame = frame % (frame_rate * 3600);
1949 mins = frame / (frame_rate * 60);
1950 frame = frame % (frame_rate * 60);
1951 secs = (float) frame / (float) frame_rate;
1952 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
1956 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
1960 if (xpos >= 0 && ypos >=0) {
1961 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
1964 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);
1966 show_verbose_canvas_cursor ();
1970 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
1977 nframes64_t distance, frame_rate;
1979 Meter meter_at_start(session->tempo_map().meter_at(start));
1987 if (Profile->get_sae() || Profile->get_small_screen()) {
1988 m = ARDOUR_UI::instance()->primary_clock.mode ();
1990 m = ARDOUR_UI::instance()->secondary_clock.mode ();
1994 case AudioClock::BBT:
1995 session->bbt_time (start, sbbt);
1996 session->bbt_time (end, ebbt);
1999 /* XXX this computation won't work well if the
2000 user makes a selection that spans any meter changes.
2003 ebbt.bars -= sbbt.bars;
2004 if (ebbt.beats >= sbbt.beats) {
2005 ebbt.beats -= sbbt.beats;
2008 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2010 if (ebbt.ticks >= sbbt.ticks) {
2011 ebbt.ticks -= sbbt.ticks;
2014 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2017 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2020 case AudioClock::SMPTE:
2021 session->smpte_duration (end - start, smpte);
2022 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
2025 case AudioClock::MinSec:
2026 /* XXX this stuff should be elsewhere.. */
2027 distance = end - start;
2028 frame_rate = session->frame_rate();
2029 hours = distance / (frame_rate * 3600);
2030 distance = distance % (frame_rate * 3600);
2031 mins = distance / (frame_rate * 60);
2032 distance = distance % (frame_rate * 60);
2033 secs = (float) distance / (float) frame_rate;
2034 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2038 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2042 if (xpos >= 0 && ypos >=0) {
2043 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2046 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset, _drag->current_pointer_y() + offset);
2049 show_verbose_canvas_cursor ();
2053 Editor::collect_new_region_view (RegionView* rv)
2055 latest_regionviews.push_back (rv);
2059 Editor::collect_and_select_new_region_view (RegionView* rv)
2062 latest_regionviews.push_back (rv);
2066 Editor::cancel_selection ()
2068 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2069 (*i)->hide_selection ();
2072 selection->clear ();
2073 clicked_selection = 0;
2078 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
2080 boost::shared_ptr<Region> region (rv.region());
2082 if (region->locked()) {
2086 nframes64_t new_bound;
2089 TimeAxisView* tvp = clicked_axisview;
2090 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2092 if (tv && tv->is_track()) {
2093 speed = tv->get_diskstream()->speed();
2096 if (left_direction) {
2097 if (swap_direction) {
2098 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2100 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2103 if (swap_direction) {
2104 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2106 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2111 snap_to (new_bound);
2113 region->trim_start ((nframes64_t) (new_bound * speed), this);
2114 rv.region_changed (StartChanged);
2118 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2120 boost::shared_ptr<Region> region (rv.region());
2122 if (region->locked()) {
2126 nframes64_t new_bound;
2129 TimeAxisView* tvp = clicked_axisview;
2130 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2132 if (tv && tv->is_track()) {
2133 speed = tv->get_diskstream()->speed();
2136 if (left_direction) {
2137 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2139 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2143 snap_to (new_bound, (left_direction ? 0 : 1));
2146 nframes64_t pre_trim_first_frame = region->first_frame();
2148 region->trim_front ((nframes64_t) (new_bound * speed), this);
2151 //Get the next region on the left of this region and shrink/expand it.
2152 boost::shared_ptr<Playlist> playlist (region->playlist());
2153 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2155 bool regions_touching = false;
2157 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2158 regions_touching = true;
2161 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2162 if (region_left != 0 &&
2163 (region_left->last_frame() > region->first_frame() || regions_touching))
2165 region_left->trim_end(region->first_frame(), this);
2171 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2175 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2177 boost::shared_ptr<Region> region (rv.region());
2179 if (region->locked()) {
2183 nframes64_t new_bound;
2186 TimeAxisView* tvp = clicked_axisview;
2187 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2189 if (tv && tv->is_track()) {
2190 speed = tv->get_diskstream()->speed();
2193 if (left_direction) {
2194 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
2196 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
2200 snap_to (new_bound);
2203 nframes64_t pre_trim_last_frame = region->last_frame();
2205 region->trim_end ((nframes64_t) (new_bound * speed), this);
2208 //Get the next region on the right of this region and shrink/expand it.
2209 boost::shared_ptr<Playlist> playlist (region->playlist());
2210 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2212 bool regions_touching = false;
2214 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)){
2215 regions_touching = true;
2218 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2219 if (region_right != 0 &&
2220 (region_right->first_frame() < region->last_frame() || regions_touching))
2222 region_right->trim_front(region->last_frame() + 1, this);
2225 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2228 rv.region_changed (LengthChanged);
2234 Editor::point_trim (GdkEvent* event)
2236 RegionView* rv = clicked_regionview;
2238 nframes64_t new_bound = _drag->current_pointer_frame();
2240 snap_to_with_modifier (new_bound, event);
2242 /* Choose action dependant on which button was pressed */
2243 switch (event->button.button) {
2245 begin_reversible_command (_("Start point trim"));
2247 if (selection->selected (rv)) {
2248 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2249 i != selection->regions.by_layer().end(); ++i)
2252 cerr << "region view contains null region" << endl;
2255 if (!(*i)->region()->locked()) {
2256 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2257 XMLNode &before = pl->get_state();
2259 (*i)->region()->trim_front (new_bound, this);
2261 XMLNode &after = pl->get_state();
2262 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2267 if (!rv->region()->locked()) {
2268 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2269 XMLNode &before = pl->get_state();
2270 rv->region()->trim_front (new_bound, this);
2271 XMLNode &after = pl->get_state();
2272 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2276 commit_reversible_command();
2280 begin_reversible_command (_("End point trim"));
2282 if (selection->selected (rv)) {
2284 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2286 if (!(*i)->region()->locked()) {
2287 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2288 XMLNode &before = pl->get_state();
2289 (*i)->region()->trim_end (new_bound, this);
2290 XMLNode &after = pl->get_state();
2291 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2297 if (!rv->region()->locked()) {
2298 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2299 XMLNode &before = pl->get_state();
2300 rv->region()->trim_end (new_bound, this);
2301 XMLNode &after = pl->get_state();
2302 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
2306 commit_reversible_command();
2315 Editor::thaw_region_after_trim (RegionView& rv)
2317 boost::shared_ptr<Region> region (rv.region());
2319 if (region->locked()) {
2323 region->thaw (_("trimmed region"));
2325 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2328 arv->unhide_envelope ();
2333 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2338 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2339 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2343 Location* location = find_location_from_marker (marker, is_start);
2344 location->set_hidden (true, this);
2349 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2351 double x1 = frame_to_pixel (start);
2352 double x2 = frame_to_pixel (end);
2353 double y2 = full_canvas_height - 1.0;
2355 zoom_rect->property_x1() = x1;
2356 zoom_rect->property_y1() = 1.0;
2357 zoom_rect->property_x2() = x2;
2358 zoom_rect->property_y2() = y2;
2363 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2365 using namespace Gtkmm2ext;
2367 ArdourPrompter prompter (false);
2369 prompter.set_prompt (_("Name for region:"));
2370 prompter.set_initial_text (clicked_regionview->region()->name());
2371 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2372 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2373 prompter.show_all ();
2374 switch (prompter.run ()) {
2375 case Gtk::RESPONSE_ACCEPT:
2377 prompter.get_result(str);
2379 clicked_regionview->region()->set_name (str);
2388 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2390 /* no brushing without a useful snap setting */
2392 switch (snap_mode) {
2394 return; /* can't work because it allows region to be placed anywhere */
2399 switch (snap_type) {
2407 /* don't brush a copy over the original */
2409 if (pos == rv->region()->position()) {
2413 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2415 if (rtv == 0 || !rtv->is_track()) {
2419 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2420 double speed = rtv->get_diskstream()->speed();
2422 XMLNode &before = playlist->get_state();
2423 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
2424 XMLNode &after = playlist->get_state();
2425 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
2427 // playlist is frozen, so we have to update manually
2429 playlist->Modified(); /* EMIT SIGNAL */
2433 Editor::track_height_step_timeout ()
2435 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2436 current_stepping_trackview = 0;
2443 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2445 assert (region_view);
2447 _region_motion_group->raise_to_top ();
2449 assert (_drag == 0);
2451 if (Config->get_edit_mode() == Splice) {
2452 _drag = new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer());
2454 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2455 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false);
2458 _drag->start_grab (event);
2460 begin_reversible_command (_("move region(s)"));
2462 /* sync the canvas to what we think is its current state */
2463 update_canvas_now();
2467 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2469 assert (region_view);
2470 assert (_drag == 0);
2472 _region_motion_group->raise_to_top ();
2474 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2475 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true);
2476 _drag->start_grab(event);
2480 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2482 assert (region_view);
2483 assert (_drag == 0);
2485 if (Config->get_edit_mode() == Splice) {
2489 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2490 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false);
2491 _drag->start_grab (event);
2493 begin_reversible_command (_("Drag region brush"));
2496 /** Start a grab where a time range is selected, track(s) are selected, and the
2497 * user clicks and drags a region with a modifier in order to create a new region containing
2498 * the section of the clicked region that lies within the time range.
2501 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2503 if (clicked_regionview == 0) {
2507 /* lets try to create new Region for the selection */
2509 vector<boost::shared_ptr<Region> > new_regions;
2510 create_region_from_selection (new_regions);
2512 if (new_regions.empty()) {
2516 /* XXX fix me one day to use all new regions */
2518 boost::shared_ptr<Region> region (new_regions.front());
2520 /* add it to the current stream/playlist.
2522 tricky: the streamview for the track will add a new regionview. we will
2523 catch the signal it sends when it creates the regionview to
2524 set the regionview we want to then drag.
2527 latest_regionviews.clear();
2528 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2530 /* A selection grab currently creates two undo/redo operations, one for
2531 creating the new region and another for moving it.
2534 begin_reversible_command (_("selection grab"));
2536 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2538 XMLNode *before = &(playlist->get_state());
2539 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2540 XMLNode *after = &(playlist->get_state());
2541 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
2543 commit_reversible_command ();
2547 if (latest_regionviews.empty()) {
2548 /* something went wrong */
2552 /* we need to deselect all other regionviews, and select this one
2553 i'm ignoring undo stuff, because the region creation will take care of it
2555 selection->set (latest_regionviews);
2557 assert (_drag == 0);
2558 _drag = new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false);
2559 _drag->start_grab (event);
2563 Editor::break_drag ()
2566 _drag->break_drag ();
2571 Editor::set_internal_edit (bool yn)
2573 _internal_editing = yn;
2576 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2578 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2579 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2581 mtv->start_step_editing ();
2584 start_step_editing ();
2588 mouse_select_button.set_image (*(manage (new Image (::get_xpm("tool_range.xpm")))));
2589 stop_step_editing ();
2591 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2592 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2594 mtv->stop_step_editing ();
2599 set_canvas_cursor ();