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);
612 switch (mouse_mode) {
615 case StartSelectionTrimItem:
617 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim);
618 _drag->start_grab (event);
621 case EndSelectionTrimItem:
623 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim);
624 _drag->start_grab (event);
628 if (Keyboard::modifier_state_contains
629 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
630 // contains and not equals because I can't use alt as a modifier alone.
631 start_selection_grab (item, event);
632 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
633 /* grab selection for moving */
635 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionMove);
636 _drag->start_grab (event);
638 /* this was debated, but decided the more common action was to
639 make a new selection */
641 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
642 _drag->start_grab (event);
648 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
649 _drag->start_grab (event);
655 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
656 event->type == GDK_BUTTON_PRESS) {
659 _drag = new RubberbandSelectDrag (this, item);
660 _drag->start_grab (event);
662 } else if (event->type == GDK_BUTTON_PRESS) {
665 case FadeInHandleItem:
668 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
669 _drag = new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
670 _drag->start_grab (event);
674 case FadeOutHandleItem:
677 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
678 _drag = new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
679 _drag->start_grab (event);
684 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
685 start_region_copy_grab (item, event, clicked_regionview);
686 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
687 start_region_brush_grab (item, event, clicked_regionview);
689 start_region_grab (item, event, clicked_regionview);
693 case RegionViewNameHighlight:
696 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
697 _drag = new TrimDrag (this, item, clicked_regionview, s.by_layer());
698 _drag->start_grab (event);
705 /* rename happens on edit clicks */
707 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
708 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer());
709 _drag->start_grab (event);
714 case ControlPointItem:
716 _drag = new ControlPointDrag (this, item);
717 _drag->start_grab (event);
721 case AutomationLineItem:
723 _drag = new LineDrag (this, item);
724 _drag->start_grab (event);
729 case AutomationTrackItem:
731 _drag = new RubberbandSelectDrag (this, item);
732 _drag->start_grab (event);
736 case ImageFrameHandleStartItem:
737 imageframe_start_handle_op(item, event) ;
740 case ImageFrameHandleEndItem:
741 imageframe_end_handle_op(item, event) ;
744 case MarkerViewHandleStartItem:
745 markerview_item_start_handle_op(item, event) ;
748 case MarkerViewHandleEndItem:
749 markerview_item_end_handle_op(item, event) ;
753 start_markerview_grab(item, event) ;
756 start_imageframe_grab(item, event) ;
774 /* start a grab so that if we finish after moving
775 we can tell what happened.
778 _drag = new RegionGainDrag (this, item);
779 _drag->start_grab (event, current_canvas_cursor);
784 _drag = new LineDrag (this, item);
785 _drag->start_grab (event);
788 case ControlPointItem:
790 _drag = new ControlPointDrag (this, item);
791 _drag->start_grab (event);
802 case ControlPointItem:
804 _drag = new ControlPointDrag (this, item);
805 _drag->start_grab (event);
808 case AutomationLineItem:
810 _drag = new LineDrag (this, item);
811 _drag->start_grab (event);
815 // XXX need automation mode to identify which
817 // start_line_grab_from_regionview (item, event);
827 if (event->type == GDK_BUTTON_PRESS) {
829 _drag = new MouseZoomDrag (this, item);
830 _drag->start_grab (event);
837 if (item_type == RegionItem) {
839 _drag = new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer());
840 _drag->start_grab (event);
845 _drag = new ScrubDrag (this, item);
846 _drag->start_grab (event);
848 scrub_reverse_distance = 0;
849 last_scrub_x = event->button.x;
850 scrubbing_direction = 0;
851 track_canvas->get_window()->set_cursor (*transparent_cursor);
863 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
865 switch (mouse_mode) {
869 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
870 start_region_copy_grab (item, event, clicked_regionview);
872 start_region_grab (item, event, clicked_regionview);
876 case ControlPointItem:
878 _drag = new ControlPointDrag (this, item);
879 _drag->start_grab (event);
888 case RegionViewNameHighlight:
890 _drag = new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer());
891 _drag->start_grab (event);
897 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer());
898 _drag->start_grab (event);
909 /* relax till release */
915 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
916 temporal_zoom_session();
918 temporal_zoom_to_frame (true, event_frame(event));
931 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
933 if (event->type != GDK_BUTTON_PRESS) {
937 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
940 Glib::RefPtr<const Gdk::Window> pointer_window;
943 Gdk::ModifierType mask;
945 pointer_window = canvas_window->get_pointer (x, y, mask);
947 if (pointer_window == track_canvas->get_bin_window()) {
948 track_canvas->window_to_world (x, y, wx, wy);
949 allow_vertical_scroll = true;
951 allow_vertical_scroll = false;
955 track_canvas->grab_focus();
957 if (session && session->actively_recording()) {
961 button_selection (item, event, item_type);
964 (Keyboard::is_delete_event (&event->button) ||
965 Keyboard::is_context_menu_event (&event->button) ||
966 Keyboard::is_edit_event (&event->button))) {
968 /* handled by button release */
972 switch (event->button.button) {
974 return button_press_handler_1 (item, event, item_type);
978 return button_press_handler_2 (item, event, item_type);
993 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
995 nframes64_t where = event_frame (event, 0, 0);
996 AutomationTimeAxisView* atv = 0;
998 /* no action if we're recording */
1000 if (session && session->actively_recording()) {
1004 /* first, see if we're finishing a drag ... */
1006 bool were_dragging = false;
1008 bool const r = _drag->end_grab (event);
1012 /* grab dragged, so do nothing else */
1016 were_dragging = true;
1019 button_selection (item, event, item_type);
1021 /* edit events get handled here */
1023 if (_drag == 0 && Keyboard::is_edit_event (&event->button)) {
1024 switch (item_type) {
1029 case TempoMarkerItem:
1030 edit_tempo_marker (item);
1033 case MeterMarkerItem:
1034 edit_meter_marker (item);
1037 case RegionViewName:
1038 if (clicked_regionview->name_active()) {
1039 return mouse_rename_region (item, event);
1043 case ControlPointItem:
1044 edit_control_point (item);
1053 /* context menu events get handled here */
1055 if (Keyboard::is_context_menu_event (&event->button)) {
1059 /* no matter which button pops up the context menu, tell the menu
1060 widget to use button 1 to drive menu selection.
1063 switch (item_type) {
1065 case FadeInHandleItem:
1067 case FadeOutHandleItem:
1068 popup_fade_context_menu (1, event->button.time, item, item_type);
1072 popup_track_context_menu (1, event->button.time, item_type, false, where);
1076 case RegionViewNameHighlight:
1077 case RegionViewName:
1078 popup_track_context_menu (1, event->button.time, item_type, false, where);
1082 popup_track_context_menu (1, event->button.time, item_type, true, where);
1085 case AutomationTrackItem:
1086 popup_track_context_menu (1, event->button.time, item_type, false, where);
1090 case RangeMarkerBarItem:
1091 case TransportMarkerBarItem:
1092 case CdMarkerBarItem:
1095 popup_ruler_menu (where, item_type);
1099 marker_context_menu (&event->button, item);
1102 case TempoMarkerItem:
1103 tm_marker_context_menu (&event->button, item);
1106 case MeterMarkerItem:
1107 tm_marker_context_menu (&event->button, item);
1110 case CrossfadeViewItem:
1111 popup_track_context_menu (1, event->button.time, item_type, false, where);
1115 case ImageFrameItem:
1116 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1118 case ImageFrameTimeAxisItem:
1119 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1121 case MarkerViewItem:
1122 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1124 case MarkerTimeAxisItem:
1125 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1137 /* delete events get handled here */
1139 if (_drag == 0 && Keyboard::is_delete_event (&event->button)) {
1141 switch (item_type) {
1142 case TempoMarkerItem:
1143 remove_tempo_marker (item);
1146 case MeterMarkerItem:
1147 remove_meter_marker (item);
1151 remove_marker (*item, event);
1155 if (mouse_mode == MouseObject) {
1156 remove_clicked_region ();
1160 case ControlPointItem:
1161 if (mouse_mode == MouseGain) {
1162 remove_gain_control_point (item, event);
1164 remove_control_point (item, event);
1174 switch (event->button.button) {
1177 switch (item_type) {
1178 /* see comments in button_press_handler */
1179 case PlayheadCursorItem:
1182 case AutomationLineItem:
1183 case StartSelectionTrimItem:
1184 case EndSelectionTrimItem:
1188 if (!_dragging_playhead) {
1189 snap_to_with_modifier (where, event, 0, true);
1190 mouse_add_new_marker (where);
1194 case CdMarkerBarItem:
1195 if (!_dragging_playhead) {
1196 // if we get here then a dragged range wasn't done
1197 snap_to_with_modifier (where, event, 0, true);
1198 mouse_add_new_marker (where, true);
1203 if (!_dragging_playhead) {
1204 snap_to_with_modifier (where, event);
1205 mouse_add_new_tempo_event (where);
1210 if (!_dragging_playhead) {
1211 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1220 switch (mouse_mode) {
1222 switch (item_type) {
1223 case AutomationTrackItem:
1224 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1226 atv->add_automation_event (item, event, where, event->button.y);
1238 // Gain only makes sense for audio regions
1240 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1244 switch (item_type) {
1246 /* check that we didn't drag before releasing, since
1247 its really annoying to create new control
1248 points when doing this.
1250 if (were_dragging) {
1251 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1256 case AutomationTrackItem:
1257 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1258 add_automation_event (item, event, where, event->button.y);
1267 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1268 if (scrubbing_direction == 0) {
1269 /* no drag, just a click */
1270 switch (item_type) {
1272 play_selected_region ();
1278 /* make sure we stop */
1279 session->request_transport_speed (0.0);
1293 switch (mouse_mode) {
1296 switch (item_type) {
1298 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1300 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1303 // Button2 click is unused
1316 // x_style_paste (where, 1.0);
1336 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1342 if (last_item_entered != item) {
1343 last_item_entered = item;
1344 last_item_entered_n = 0;
1347 switch (item_type) {
1348 case ControlPointItem:
1349 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1350 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1351 cp->set_visible (true);
1355 at_y = cp->get_y ();
1356 cp->item()->i2w (at_x, at_y);
1360 fraction = 1.0 - (cp->get_y() / cp->line().height());
1362 if (is_drawable() && dynamic_cast<ScrubDrag*> (_drag) == 0) {
1363 track_canvas->get_window()->set_cursor (*fader_cursor);
1366 last_item_entered_n++;
1367 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1368 if (last_item_entered_n < 10) {
1369 show_verbose_canvas_cursor ();
1375 if (mouse_mode == MouseGain) {
1376 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1378 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1379 if (is_drawable()) {
1380 track_canvas->get_window()->set_cursor (*fader_cursor);
1385 case AutomationLineItem:
1386 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1388 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1390 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1392 if (is_drawable()) {
1393 track_canvas->get_window()->set_cursor (*fader_cursor);
1398 case RegionViewNameHighlight:
1399 if (is_drawable() && mouse_mode == MouseObject) {
1400 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1404 case StartSelectionTrimItem:
1405 case EndSelectionTrimItem:
1408 case ImageFrameHandleStartItem:
1409 case ImageFrameHandleEndItem:
1410 case MarkerViewHandleStartItem:
1411 case MarkerViewHandleEndItem:
1414 if (is_drawable()) {
1415 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1419 case PlayheadCursorItem:
1420 if (is_drawable()) {
1421 switch (_edit_point) {
1423 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1426 track_canvas->get_window()->set_cursor (*grabber_cursor);
1432 case RegionViewName:
1434 /* when the name is not an active item, the entire name highlight is for trimming */
1436 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1437 if (mouse_mode == MouseObject && is_drawable()) {
1438 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1444 case AutomationTrackItem:
1445 if (is_drawable()) {
1446 Gdk::Cursor *cursor;
1447 switch (mouse_mode) {
1449 cursor = selector_cursor;
1452 cursor = zoom_cursor;
1455 cursor = cross_hair_cursor;
1459 track_canvas->get_window()->set_cursor (*cursor);
1461 AutomationTimeAxisView* atv;
1462 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1463 clear_entered_track = false;
1464 set_entered_track (atv);
1470 case RangeMarkerBarItem:
1471 case TransportMarkerBarItem:
1472 case CdMarkerBarItem:
1475 if (is_drawable()) {
1476 track_canvas->get_window()->set_cursor (*timebar_cursor);
1481 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1484 entered_marker = marker;
1485 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1487 case MeterMarkerItem:
1488 case TempoMarkerItem:
1489 if (is_drawable()) {
1490 track_canvas->get_window()->set_cursor (*timebar_cursor);
1493 case FadeInHandleItem:
1494 case FadeOutHandleItem:
1495 if (mouse_mode == MouseObject) {
1496 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1498 rect->property_fill_color_rgba() = 0;
1499 rect->property_outline_pixels() = 1;
1508 /* second pass to handle entered track status in a comprehensible way.
1511 switch (item_type) {
1513 case AutomationLineItem:
1514 case ControlPointItem:
1515 /* these do not affect the current entered track state */
1516 clear_entered_track = false;
1519 case AutomationTrackItem:
1520 /* handled above already */
1524 set_entered_track (0);
1532 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1541 switch (item_type) {
1542 case ControlPointItem:
1543 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1544 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1545 if (cp->line().npoints() > 1 && !cp->selected()) {
1546 cp->set_visible (false);
1550 if (is_drawable()) {
1551 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1554 hide_verbose_canvas_cursor ();
1557 case RegionViewNameHighlight:
1558 case StartSelectionTrimItem:
1559 case EndSelectionTrimItem:
1560 case PlayheadCursorItem:
1563 case ImageFrameHandleStartItem:
1564 case ImageFrameHandleEndItem:
1565 case MarkerViewHandleStartItem:
1566 case MarkerViewHandleEndItem:
1569 if (is_drawable()) {
1570 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1575 case AutomationLineItem:
1576 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1578 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1580 line->property_fill_color_rgba() = al->get_line_color();
1582 if (is_drawable()) {
1583 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1587 case RegionViewName:
1588 /* see enter_handler() for notes */
1589 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1590 if (is_drawable() && mouse_mode == MouseObject) {
1591 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1596 case RangeMarkerBarItem:
1597 case TransportMarkerBarItem:
1598 case CdMarkerBarItem:
1602 if (is_drawable()) {
1603 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1608 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1612 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1613 location_flags_changed (loc, this);
1616 case MeterMarkerItem:
1617 case TempoMarkerItem:
1619 if (is_drawable()) {
1620 track_canvas->get_window()->set_cursor (*timebar_cursor);
1625 case FadeInHandleItem:
1626 case FadeOutHandleItem:
1627 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1629 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1631 rect->property_fill_color_rgba() = rv->get_fill_color();
1632 rect->property_outline_pixels() = 0;
1637 case AutomationTrackItem:
1638 if (is_drawable()) {
1639 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1640 clear_entered_track = true;
1641 Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1653 Editor::left_automation_track ()
1655 if (clear_entered_track) {
1656 set_entered_track (0);
1657 clear_entered_track = false;
1667 if (scrubbing_direction == 0) {
1669 session->request_locate (_drag->current_pointer_frame(), false);
1670 session->request_transport_speed (0.1);
1671 scrubbing_direction = 1;
1675 if (last_scrub_x > _drag->current_pointer_x()) {
1677 /* pointer moved to the left */
1679 if (scrubbing_direction > 0) {
1681 /* we reversed direction to go backwards */
1684 scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x());
1688 /* still moving to the left (backwards) */
1690 scrub_reversals = 0;
1691 scrub_reverse_distance = 0;
1693 delta = 0.01 * (last_scrub_x - _drag->current_pointer_x());
1694 session->request_transport_speed (session->transport_speed() - delta);
1698 /* pointer moved to the right */
1700 if (scrubbing_direction < 0) {
1701 /* we reversed direction to go forward */
1704 scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x);
1707 /* still moving to the right */
1709 scrub_reversals = 0;
1710 scrub_reverse_distance = 0;
1712 delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x);
1713 session->request_transport_speed (session->transport_speed() + delta);
1717 /* if there have been more than 2 opposite motion moves detected, or one that moves
1718 back more than 10 pixels, reverse direction
1721 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1723 if (scrubbing_direction > 0) {
1724 /* was forwards, go backwards */
1725 session->request_transport_speed (-0.1);
1726 scrubbing_direction = -1;
1728 /* was backwards, go forwards */
1729 session->request_transport_speed (0.1);
1730 scrubbing_direction = 1;
1733 scrub_reverse_distance = 0;
1734 scrub_reversals = 0;
1738 last_scrub_x = _drag->current_pointer_x();
1742 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1744 if (event->motion.is_hint) {
1747 /* We call this so that MOTION_NOTIFY events continue to be
1748 delivered to the canvas. We need to do this because we set
1749 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1750 the density of the events, at the expense of a round-trip
1751 to the server. Given that this will mostly occur on cases
1752 where DISPLAY = :0.0, and given the cost of what the motion
1753 event might do, its a good tradeoff.
1756 track_canvas->get_pointer (x, y);
1759 if (current_stepping_trackview) {
1760 /* don't keep the persistent stepped trackview if the mouse moves */
1761 current_stepping_trackview = 0;
1762 step_timeout.disconnect ();
1765 if (session && session->actively_recording()) {
1766 /* Sorry. no dragging stuff around while we record */
1770 bool handled = false;
1772 handled = _drag->motion_handler (event, from_autoscroll);
1779 track_canvas_motion (event);
1784 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1786 ControlPoint* control_point;
1788 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1789 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1793 // We shouldn't remove the first or last gain point
1794 if (control_point->line().is_last_point(*control_point) ||
1795 control_point->line().is_first_point(*control_point)) {
1799 control_point->line().remove_point (*control_point);
1803 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1805 ControlPoint* control_point;
1807 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1808 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1812 control_point->line().remove_point (*control_point);
1816 Editor::edit_control_point (ArdourCanvas::Item* item)
1818 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1821 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1825 ControlPointDialog d (p);
1826 d.set_position (Gtk::WIN_POS_MOUSE);
1829 if (d.run () != RESPONSE_ACCEPT) {
1833 p->line().modify_point_y (*p, d.get_y_fraction ());
1838 Editor::visible_order_range (int* low, int* high) const
1840 *low = TimeAxisView::max_order ();
1843 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1845 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1847 if (!rtv->hidden()) {
1849 if (*high < rtv->order()) {
1850 *high = rtv->order ();
1853 if (*low > rtv->order()) {
1854 *low = rtv->order ();
1861 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1863 /* Either add to or set the set the region selection, unless
1864 this is an alignment click (control used)
1867 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1868 TimeAxisView* tv = &rv.get_time_axis_view();
1869 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1871 if (rtv && rtv->is_track()) {
1872 speed = rtv->get_diskstream()->speed();
1875 nframes64_t where = get_preferred_edit_position();
1879 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1881 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1883 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1885 align_region (rv.region(), End, (nframes64_t) (where * speed));
1889 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1896 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
1902 nframes64_t frame_rate;
1911 if (Profile->get_sae() || Profile->get_small_screen()) {
1912 m = ARDOUR_UI::instance()->primary_clock.mode();
1914 m = ARDOUR_UI::instance()->secondary_clock.mode();
1918 case AudioClock::BBT:
1919 session->bbt_time (frame, bbt);
1920 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
1923 case AudioClock::SMPTE:
1924 session->smpte_time (frame, smpte);
1925 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
1928 case AudioClock::MinSec:
1929 /* XXX this is copied from show_verbose_duration_cursor() */
1930 frame_rate = session->frame_rate();
1931 hours = frame / (frame_rate * 3600);
1932 frame = frame % (frame_rate * 3600);
1933 mins = frame / (frame_rate * 60);
1934 frame = frame % (frame_rate * 60);
1935 secs = (float) frame / (float) frame_rate;
1936 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
1940 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
1944 if (xpos >= 0 && ypos >=0) {
1945 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
1948 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);
1950 show_verbose_canvas_cursor ();
1954 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
1961 nframes64_t distance, frame_rate;
1963 Meter meter_at_start(session->tempo_map().meter_at(start));
1971 if (Profile->get_sae() || Profile->get_small_screen()) {
1972 m = ARDOUR_UI::instance()->primary_clock.mode ();
1974 m = ARDOUR_UI::instance()->secondary_clock.mode ();
1978 case AudioClock::BBT:
1979 session->bbt_time (start, sbbt);
1980 session->bbt_time (end, ebbt);
1983 /* XXX this computation won't work well if the
1984 user makes a selection that spans any meter changes.
1987 ebbt.bars -= sbbt.bars;
1988 if (ebbt.beats >= sbbt.beats) {
1989 ebbt.beats -= sbbt.beats;
1992 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
1994 if (ebbt.ticks >= sbbt.ticks) {
1995 ebbt.ticks -= sbbt.ticks;
1998 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2001 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2004 case AudioClock::SMPTE:
2005 session->smpte_duration (end - start, smpte);
2006 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
2009 case AudioClock::MinSec:
2010 /* XXX this stuff should be elsewhere.. */
2011 distance = end - start;
2012 frame_rate = session->frame_rate();
2013 hours = distance / (frame_rate * 3600);
2014 distance = distance % (frame_rate * 3600);
2015 mins = distance / (frame_rate * 60);
2016 distance = distance % (frame_rate * 60);
2017 secs = (float) distance / (float) frame_rate;
2018 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2022 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2026 if (xpos >= 0 && ypos >=0) {
2027 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2030 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset, _drag->current_pointer_y() + offset);
2033 show_verbose_canvas_cursor ();
2037 Editor::collect_new_region_view (RegionView* rv)
2039 latest_regionviews.push_back (rv);
2043 Editor::collect_and_select_new_region_view (RegionView* rv)
2046 latest_regionviews.push_back (rv);
2050 Editor::cancel_selection ()
2052 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2053 (*i)->hide_selection ();
2056 selection->clear ();
2057 clicked_selection = 0;
2062 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
2064 boost::shared_ptr<Region> region (rv.region());
2066 if (region->locked()) {
2070 nframes64_t new_bound;
2073 TimeAxisView* tvp = clicked_axisview;
2074 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2076 if (tv && tv->is_track()) {
2077 speed = tv->get_diskstream()->speed();
2080 if (left_direction) {
2081 if (swap_direction) {
2082 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2084 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2087 if (swap_direction) {
2088 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2090 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2095 snap_to (new_bound);
2097 region->trim_start ((nframes64_t) (new_bound * speed), this);
2098 rv.region_changed (StartChanged);
2102 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2104 boost::shared_ptr<Region> region (rv.region());
2106 if (region->locked()) {
2110 nframes64_t new_bound;
2113 TimeAxisView* tvp = clicked_axisview;
2114 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2116 if (tv && tv->is_track()) {
2117 speed = tv->get_diskstream()->speed();
2120 if (left_direction) {
2121 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2123 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2127 snap_to (new_bound, (left_direction ? 0 : 1));
2130 nframes64_t pre_trim_first_frame = region->first_frame();
2132 region->trim_front ((nframes64_t) (new_bound * speed), this);
2135 //Get the next region on the left of this region and shrink/expand it.
2136 boost::shared_ptr<Playlist> playlist (region->playlist());
2137 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2139 bool regions_touching = false;
2141 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2142 regions_touching = true;
2145 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2146 if (region_left != 0 &&
2147 (region_left->last_frame() > region->first_frame() || regions_touching))
2149 region_left->trim_end(region->first_frame(), this);
2155 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2159 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2161 boost::shared_ptr<Region> region (rv.region());
2163 if (region->locked()) {
2167 nframes64_t new_bound;
2170 TimeAxisView* tvp = clicked_axisview;
2171 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2173 if (tv && tv->is_track()) {
2174 speed = tv->get_diskstream()->speed();
2177 if (left_direction) {
2178 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
2180 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
2184 snap_to (new_bound);
2187 nframes64_t pre_trim_last_frame = region->last_frame();
2189 region->trim_end ((nframes64_t) (new_bound * speed), this);
2192 //Get the next region on the right of this region and shrink/expand it.
2193 boost::shared_ptr<Playlist> playlist (region->playlist());
2194 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2196 bool regions_touching = false;
2198 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)){
2199 regions_touching = true;
2202 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2203 if (region_right != 0 &&
2204 (region_right->first_frame() < region->last_frame() || regions_touching))
2206 region_right->trim_front(region->last_frame() + 1, this);
2209 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2212 rv.region_changed (LengthChanged);
2218 Editor::point_trim (GdkEvent* event)
2220 RegionView* rv = clicked_regionview;
2222 nframes64_t new_bound = _drag->current_pointer_frame();
2224 snap_to_with_modifier (new_bound, event);
2226 /* Choose action dependant on which button was pressed */
2227 switch (event->button.button) {
2229 begin_reversible_command (_("Start point trim"));
2231 if (selection->selected (rv)) {
2232 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2233 i != selection->regions.by_layer().end(); ++i)
2236 cerr << "region view contains null region" << endl;
2239 if (!(*i)->region()->locked()) {
2240 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2241 XMLNode &before = pl->get_state();
2243 (*i)->region()->trim_front (new_bound, this);
2245 XMLNode &after = pl->get_state();
2246 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2251 if (!rv->region()->locked()) {
2252 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2253 XMLNode &before = pl->get_state();
2254 rv->region()->trim_front (new_bound, this);
2255 XMLNode &after = pl->get_state();
2256 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2260 commit_reversible_command();
2264 begin_reversible_command (_("End point trim"));
2266 if (selection->selected (rv)) {
2268 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2270 if (!(*i)->region()->locked()) {
2271 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2272 XMLNode &before = pl->get_state();
2273 (*i)->region()->trim_end (new_bound, this);
2274 XMLNode &after = pl->get_state();
2275 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2281 if (!rv->region()->locked()) {
2282 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2283 XMLNode &before = pl->get_state();
2284 rv->region()->trim_end (new_bound, this);
2285 XMLNode &after = pl->get_state();
2286 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
2290 commit_reversible_command();
2299 Editor::thaw_region_after_trim (RegionView& rv)
2301 boost::shared_ptr<Region> region (rv.region());
2303 if (region->locked()) {
2307 region->thaw (_("trimmed region"));
2309 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2312 arv->unhide_envelope ();
2317 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2322 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2323 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2327 Location* location = find_location_from_marker (marker, is_start);
2328 location->set_hidden (true, this);
2333 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2335 double x1 = frame_to_pixel (start);
2336 double x2 = frame_to_pixel (end);
2337 double y2 = full_canvas_height - 1.0;
2339 zoom_rect->property_x1() = x1;
2340 zoom_rect->property_y1() = 1.0;
2341 zoom_rect->property_x2() = x2;
2342 zoom_rect->property_y2() = y2;
2347 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2349 using namespace Gtkmm2ext;
2351 ArdourPrompter prompter (false);
2353 prompter.set_prompt (_("Name for region:"));
2354 prompter.set_initial_text (clicked_regionview->region()->name());
2355 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2356 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2357 prompter.show_all ();
2358 switch (prompter.run ()) {
2359 case Gtk::RESPONSE_ACCEPT:
2361 prompter.get_result(str);
2363 clicked_regionview->region()->set_name (str);
2372 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2374 /* no brushing without a useful snap setting */
2376 switch (snap_mode) {
2378 return; /* can't work because it allows region to be placed anywhere */
2383 switch (snap_type) {
2391 /* don't brush a copy over the original */
2393 if (pos == rv->region()->position()) {
2397 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2399 if (rtv == 0 || !rtv->is_track()) {
2403 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2404 double speed = rtv->get_diskstream()->speed();
2406 XMLNode &before = playlist->get_state();
2407 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
2408 XMLNode &after = playlist->get_state();
2409 session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
2411 // playlist is frozen, so we have to update manually
2413 playlist->Modified(); /* EMIT SIGNAL */
2417 Editor::track_height_step_timeout ()
2419 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2420 current_stepping_trackview = 0;
2427 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2429 assert (region_view);
2431 _region_motion_group->raise_to_top ();
2433 assert (_drag == 0);
2435 if (Config->get_edit_mode() == Splice) {
2436 _drag = new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer());
2438 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2439 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false);
2442 _drag->start_grab (event);
2444 begin_reversible_command (_("move region(s)"));
2446 /* sync the canvas to what we think is its current state */
2447 update_canvas_now();
2451 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2453 assert (region_view);
2454 assert (_drag == 0);
2456 _region_motion_group->raise_to_top ();
2458 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2459 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true);
2460 _drag->start_grab(event);
2464 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2466 assert (region_view);
2467 assert (_drag == 0);
2469 if (Config->get_edit_mode() == Splice) {
2473 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2474 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false);
2475 _drag->start_grab (event);
2477 begin_reversible_command (_("Drag region brush"));
2480 /** Start a grab where a time range is selected, track(s) are selected, and the
2481 * user clicks and drags a region with a modifier in order to create a new region containing
2482 * the section of the clicked region that lies within the time range.
2485 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2487 if (clicked_regionview == 0) {
2491 /* lets try to create new Region for the selection */
2493 vector<boost::shared_ptr<Region> > new_regions;
2494 create_region_from_selection (new_regions);
2496 if (new_regions.empty()) {
2500 /* XXX fix me one day to use all new regions */
2502 boost::shared_ptr<Region> region (new_regions.front());
2504 /* add it to the current stream/playlist.
2506 tricky: the streamview for the track will add a new regionview. we will
2507 catch the signal it sends when it creates the regionview to
2508 set the regionview we want to then drag.
2511 latest_regionviews.clear();
2512 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2514 /* A selection grab currently creates two undo/redo operations, one for
2515 creating the new region and another for moving it.
2518 begin_reversible_command (_("selection grab"));
2520 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2522 XMLNode *before = &(playlist->get_state());
2523 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2524 XMLNode *after = &(playlist->get_state());
2525 session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
2527 commit_reversible_command ();
2531 if (latest_regionviews.empty()) {
2532 /* something went wrong */
2536 /* we need to deselect all other regionviews, and select this one
2537 i'm ignoring undo stuff, because the region creation will take care of it
2539 selection->set (latest_regionviews);
2541 assert (_drag == 0);
2542 _drag = new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false);
2543 _drag->start_grab (event);
2547 Editor::break_drag ()
2550 _drag->break_drag ();
2555 Editor::set_internal_edit (bool yn)
2557 _internal_editing = yn;
2560 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2562 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2563 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2565 mtv->start_step_editing ();
2568 start_step_editing ();
2572 mouse_select_button.set_image (*(manage (new Image (::get_xpm("tool_range.xpm")))));
2573 stop_step_editing ();
2575 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2576 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2578 mtv->stop_step_editing ();
2583 set_canvas_cursor ();