2 Copyright (C) 2000-2001 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include "pbd/error.h"
29 #include "pbd/enumwriter.h"
30 #include <gtkmm2ext/utils.h>
31 #include <gtkmm2ext/tearoff.h>
32 #include "pbd/memento_command.h"
33 #include "pbd/basename.h"
35 #include "ardour_ui.h"
38 #include "time_axis_view.h"
39 #include "audio_time_axis.h"
40 #include "audio_region_view.h"
41 #include "midi_region_view.h"
43 #include "streamview.h"
44 #include "region_gain_line.h"
45 #include "automation_time_axis.h"
46 #include "control_point.h"
49 #include "selection.h"
52 #include "rgb_macros.h"
53 #include "control_point_dialog.h"
54 #include "editor_drag.h"
56 #include "ardour/types.h"
57 #include "ardour/profile.h"
58 #include "ardour/route.h"
59 #include "ardour/audio_track.h"
60 #include "ardour/audio_diskstream.h"
61 #include "ardour/midi_diskstream.h"
62 #include "ardour/playlist.h"
63 #include "ardour/audioplaylist.h"
64 #include "ardour/audioregion.h"
65 #include "ardour/midi_region.h"
66 #include "ardour/dB.h"
67 #include "ardour/utils.h"
68 #include "ardour/region_factory.h"
69 #include "ardour/source_factory.h"
70 #include "ardour/session.h"
77 using namespace ARDOUR;
80 using namespace Editing;
81 using Gtkmm2ext::Keyboard;
84 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
88 Gdk::ModifierType mask;
89 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
90 Glib::RefPtr<const Gdk::Window> pointer_window;
96 pointer_window = canvas_window->get_pointer (x, y, mask);
98 if (pointer_window == track_canvas->get_bin_window()) {
101 in_track_canvas = true;
104 in_track_canvas = false;
109 event.type = GDK_BUTTON_RELEASE;
113 where = event_frame (&event, 0, 0);
118 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
132 switch (event->type) {
133 case GDK_BUTTON_RELEASE:
134 case GDK_BUTTON_PRESS:
135 case GDK_2BUTTON_PRESS:
136 case GDK_3BUTTON_PRESS:
137 *pcx = event->button.x;
138 *pcy = event->button.y;
139 _trackview_group->w2i(*pcx, *pcy);
141 case GDK_MOTION_NOTIFY:
142 *pcx = event->motion.x;
143 *pcy = event->motion.y;
144 _trackview_group->w2i(*pcx, *pcy);
146 case GDK_ENTER_NOTIFY:
147 case GDK_LEAVE_NOTIFY:
148 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
151 case GDK_KEY_RELEASE:
152 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
155 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
159 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
160 position is negative (as can be the case with motion events in particular),
161 the frame location is always positive.
164 return pixel_to_frame (*pcx);
168 Editor::which_grabber_cursor ()
170 Gdk::Cursor* c = grabber_cursor;
172 if (_internal_editing) {
173 switch (mouse_mode) {
175 c = midi_pencil_cursor;
183 c = midi_resize_cursor;
192 switch (_edit_point) {
194 c = grabber_edit_point_cursor;
205 Editor::set_canvas_cursor ()
207 if (_internal_editing) {
209 switch (mouse_mode) {
211 current_canvas_cursor = midi_pencil_cursor;
215 current_canvas_cursor = which_grabber_cursor();
219 current_canvas_cursor = midi_resize_cursor;
228 switch (mouse_mode) {
230 current_canvas_cursor = selector_cursor;
234 current_canvas_cursor = which_grabber_cursor();
238 current_canvas_cursor = cross_hair_cursor;
242 current_canvas_cursor = zoom_cursor;
246 current_canvas_cursor = time_fx_cursor; // just use playhead
250 current_canvas_cursor = speaker_cursor;
255 switch (_join_object_range_state) {
256 case JOIN_OBJECT_RANGE_NONE:
258 case JOIN_OBJECT_RANGE_OBJECT:
259 current_canvas_cursor = which_grabber_cursor ();
261 case JOIN_OBJECT_RANGE_RANGE:
262 current_canvas_cursor = selector_cursor;
267 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
272 Editor::set_mouse_mode (MouseMode m, bool force)
278 if (!force && m == mouse_mode) {
282 Glib::RefPtr<Action> act;
286 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
290 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
294 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
298 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
302 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
306 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
312 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
315 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
316 tact->set_active (false);
317 tact->set_active (true);
321 Editor::mouse_mode_toggled (MouseMode m)
327 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
329 /* in all modes except range and joined object/range, hide the range selection,
330 show the object (region) selection.
333 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
334 (*i)->set_should_show_selection (true);
336 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
337 (*i)->hide_selection ();
343 in range or object/range mode, show the range selection.
346 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
347 (*i)->show_selection (selection->time);
351 set_canvas_cursor ();
355 Editor::step_mouse_mode (bool next)
357 switch (current_mouse_mode()) {
360 if (Profile->get_sae()) {
361 set_mouse_mode (MouseZoom);
363 set_mouse_mode (MouseRange);
366 set_mouse_mode (MouseTimeFX);
371 if (next) set_mouse_mode (MouseZoom);
372 else set_mouse_mode (MouseObject);
377 if (Profile->get_sae()) {
378 set_mouse_mode (MouseTimeFX);
380 set_mouse_mode (MouseGain);
383 if (Profile->get_sae()) {
384 set_mouse_mode (MouseObject);
386 set_mouse_mode (MouseRange);
392 if (next) set_mouse_mode (MouseTimeFX);
393 else set_mouse_mode (MouseZoom);
398 set_mouse_mode (MouseAudition);
400 if (Profile->get_sae()) {
401 set_mouse_mode (MouseZoom);
403 set_mouse_mode (MouseGain);
409 if (next) set_mouse_mode (MouseObject);
410 else set_mouse_mode (MouseTimeFX);
416 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
418 /* in object/audition/timefx/gain-automation mode,
419 any button press sets the selection if the object
420 can be selected. this is a bit of hack, because
421 we want to avoid this if the mouse operation is a
424 note: not dbl-click or triple-click
427 if (((mouse_mode != MouseObject) &&
428 (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
429 (mouse_mode != MouseAudition || item_type != RegionItem) &&
430 (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
431 (mouse_mode != MouseGain) &&
432 (mouse_mode != MouseRange)) ||
434 ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
439 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
441 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
443 /* almost no selection action on modified button-2 or button-3 events */
445 if (item_type != RegionItem && event->button.button != 2) {
451 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
452 bool press = (event->type == GDK_BUTTON_PRESS);
454 // begin_reversible_command (_("select on click"));
458 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
459 set_selected_regionview_from_click (press, op, true);
460 } else if (event->type == GDK_BUTTON_PRESS) {
461 selection->clear_tracks ();
462 set_selected_track_as_side_effect (true);
464 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
465 select_range_around_region (selection->regions.front());
470 case RegionViewNameHighlight:
472 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
473 set_selected_regionview_from_click (press, op, true);
474 } else if (event->type == GDK_BUTTON_PRESS) {
475 set_selected_track_as_side_effect ();
480 case FadeInHandleItem:
482 case FadeOutHandleItem:
484 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
485 set_selected_regionview_from_click (press, op, true);
486 } else if (event->type == GDK_BUTTON_PRESS) {
487 set_selected_track_as_side_effect ();
491 case ControlPointItem:
492 set_selected_track_as_side_effect ();
493 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
494 set_selected_control_point_from_click (op, false);
499 /* for context click, select track */
500 if (event->button.button == 3) {
501 set_selected_track_as_side_effect ();
505 case AutomationTrackItem:
506 set_selected_track_as_side_effect (true);
515 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
518 _drag->item()->ungrab (event->button.time);
523 /* single mouse clicks on any of these item types operate
524 independent of mouse mode, mostly because they are
525 not on the main track canvas or because we want
530 case PlayheadCursorItem:
532 _drag = new CursorDrag (this, item, true);
533 _drag->start_grab (event);
537 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
538 hide_marker (item, event);
541 _drag = new MarkerDrag (this, item);
542 _drag->start_grab (event);
546 case TempoMarkerItem:
548 _drag = new TempoMarkerDrag (
551 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
553 _drag->start_grab (event);
556 case MeterMarkerItem:
558 _drag = new MeterMarkerDrag (
561 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
563 _drag->start_grab (event);
569 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
571 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
572 _drag->start_grab (event);
578 case RangeMarkerBarItem:
580 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
581 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
583 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker);
585 _drag->start_grab (event);
589 case CdMarkerBarItem:
591 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
592 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
594 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker);
596 _drag->start_grab (event);
600 case TransportMarkerBarItem:
602 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
603 _drag = new CursorDrag (this, &playhead_cursor->canvas_item, false);
605 _drag = new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker);
607 _drag->start_grab (event);
615 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
616 /* special case: allow trim of range selections in joined object mode;
617 in theory eff should equal MouseRange in this case, but it doesn't
618 because entering the range selection canvas item results in entered_regionview
619 being set to 0, so update_join_object_range_location acts as if we aren't
622 if (item_type == StartSelectionTrimItem) {
624 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim);
625 _drag->start_grab (event);
626 } else if (item_type == EndSelectionTrimItem) {
628 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim);
629 _drag->start_grab (event);
633 Editing::MouseMode eff = effective_mouse_mode ();
638 case StartSelectionTrimItem:
640 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim);
641 _drag->start_grab (event);
644 case EndSelectionTrimItem:
646 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim);
647 _drag->start_grab (event);
651 if (Keyboard::modifier_state_contains
652 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
653 // contains and not equals because I can't use alt as a modifier alone.
654 start_selection_grab (item, event);
655 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
656 /* grab selection for moving */
658 _drag = new SelectionDrag (this, item, SelectionDrag::SelectionMove);
659 _drag->start_grab (event);
661 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
662 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
664 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
666 if (join_object_range_button.get_active() && atv) {
667 /* smart "join" mode: drag automation */
668 _drag = new AutomationRangeDrag (this, atv->base_item(), selection->time);
670 /* this was debated, but decided the more common action was to
671 make a new selection */
672 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
674 _drag->start_grab (event);
681 _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
682 _drag->start_grab (event);
690 if (internal_editing()) {
691 /* Note: we don't get here if not in internal_editing() mode */
693 _drag = new NoteDrag (this, item);
694 _drag->start_grab (event);
703 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
704 event->type == GDK_BUTTON_PRESS) {
707 _drag = new RubberbandSelectDrag (this, item);
708 _drag->start_grab (event);
710 } else if (event->type == GDK_BUTTON_PRESS) {
713 case FadeInHandleItem:
716 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
717 _drag = new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
718 _drag->start_grab (event);
722 case FadeOutHandleItem:
725 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
726 _drag = new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s);
727 _drag->start_grab (event);
732 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
733 start_region_copy_grab (item, event, clicked_regionview);
734 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
735 start_region_brush_grab (item, event, clicked_regionview);
737 start_region_grab (item, event, clicked_regionview);
741 case RegionViewNameHighlight:
744 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
745 _drag = new TrimDrag (this, item, clicked_regionview, s.by_layer());
746 _drag->start_grab (event);
753 /* rename happens on edit clicks */
755 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
756 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer());
757 _drag->start_grab (event);
762 case ControlPointItem:
764 _drag = new ControlPointDrag (this, item);
765 _drag->start_grab (event);
769 case AutomationLineItem:
771 _drag = new LineDrag (this, item);
772 _drag->start_grab (event);
777 if (internal_editing()) {
779 _drag = new RegionCreateDrag (this, item, clicked_axisview);
780 _drag->start_grab (event);
784 _drag = new RubberbandSelectDrag (this, item);
785 _drag->start_grab (event);
789 case AutomationTrackItem:
791 /* rubberband drag to select automation points */
792 _drag = new RubberbandSelectDrag (this, item);
793 _drag->start_grab (event);
798 if (join_object_range_button.get_active()) {
799 /* we're in "smart" joined mode, and we've clicked on a Selection; if we're
800 * over an automation track, start a drag of its data */
801 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
802 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
804 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
807 _drag = new AutomationRangeDrag (this, atv->base_item(), selection->time);
808 _drag->start_grab (event);
816 case ImageFrameHandleStartItem:
817 imageframe_start_handle_op(item, event) ;
820 case ImageFrameHandleEndItem:
821 imageframe_end_handle_op(item, event) ;
824 case MarkerViewHandleStartItem:
825 markerview_item_start_handle_op(item, event) ;
828 case MarkerViewHandleEndItem:
829 markerview_item_end_handle_op(item, event) ;
833 start_markerview_grab(item, event) ;
836 start_imageframe_grab(item, event) ;
854 /* start a grab so that if we finish after moving
855 we can tell what happened.
858 _drag = new RegionGainDrag (this, item);
859 _drag->start_grab (event, current_canvas_cursor);
864 _drag = new LineDrag (this, item);
865 _drag->start_grab (event);
868 case ControlPointItem:
870 _drag = new ControlPointDrag (this, item);
871 _drag->start_grab (event);
882 case ControlPointItem:
884 _drag = new ControlPointDrag (this, item);
885 _drag->start_grab (event);
888 case AutomationLineItem:
890 _drag = new LineDrag (this, item);
891 _drag->start_grab (event);
895 // XXX need automation mode to identify which
897 // start_line_grab_from_regionview (item, event);
907 if (event->type == GDK_BUTTON_PRESS) {
909 _drag = new MouseZoomDrag (this, item);
910 _drag->start_grab (event);
917 if (internal_editing() && item_type == NoteItem) {
919 _drag = new NoteResizeDrag (this, item);
920 _drag->start_grab (event);
922 } else if (!internal_editing() && item_type == RegionItem) {
924 _drag = new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer());
925 _drag->start_grab (event);
931 _drag = new ScrubDrag (this, item);
932 _drag->start_grab (event);
934 scrub_reverse_distance = 0;
935 last_scrub_x = event->button.x;
936 scrubbing_direction = 0;
937 track_canvas->get_window()->set_cursor (*transparent_cursor);
949 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
951 Editing::MouseMode const eff = effective_mouse_mode ();
956 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
957 start_region_copy_grab (item, event, clicked_regionview);
959 start_region_grab (item, event, clicked_regionview);
963 case ControlPointItem:
965 _drag = new ControlPointDrag (this, item);
966 _drag->start_grab (event);
975 case RegionViewNameHighlight:
977 _drag = new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer());
978 _drag->start_grab (event);
984 _drag = new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer());
985 _drag->start_grab (event);
996 /* relax till release */
1002 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1003 temporal_zoom_session();
1005 temporal_zoom_to_frame (true, event_frame(event));
1018 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1020 if (event->type != GDK_BUTTON_PRESS) {
1024 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1026 if (canvas_window) {
1027 Glib::RefPtr<const Gdk::Window> pointer_window;
1030 Gdk::ModifierType mask;
1032 pointer_window = canvas_window->get_pointer (x, y, mask);
1034 if (pointer_window == track_canvas->get_bin_window()) {
1035 track_canvas->window_to_world (x, y, wx, wy);
1036 allow_vertical_scroll = true;
1038 allow_vertical_scroll = false;
1042 track_canvas->grab_focus();
1044 if (_session && _session->actively_recording()) {
1048 button_selection (item, event, item_type);
1051 (Keyboard::is_delete_event (&event->button) ||
1052 Keyboard::is_context_menu_event (&event->button) ||
1053 Keyboard::is_edit_event (&event->button))) {
1055 /* handled by button release */
1059 switch (event->button.button) {
1061 return button_press_handler_1 (item, event, item_type);
1065 return button_press_handler_2 (item, event, item_type);
1080 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1082 nframes64_t where = event_frame (event, 0, 0);
1083 AutomationTimeAxisView* atv = 0;
1085 /* no action if we're recording */
1087 if (_session && _session->actively_recording()) {
1091 /* first, see if we're finishing a drag ... */
1093 bool were_dragging = false;
1095 bool const r = _drag->end_grab (event);
1099 /* grab dragged, so do nothing else */
1103 were_dragging = true;
1106 button_selection (item, event, item_type);
1108 /* edit events get handled here */
1110 if (_drag == 0 && Keyboard::is_edit_event (&event->button)) {
1111 switch (item_type) {
1116 case TempoMarkerItem:
1117 edit_tempo_marker (item);
1120 case MeterMarkerItem:
1121 edit_meter_marker (item);
1124 case RegionViewName:
1125 if (clicked_regionview->name_active()) {
1126 return mouse_rename_region (item, event);
1130 case ControlPointItem:
1131 edit_control_point (item);
1140 /* context menu events get handled here */
1142 if (Keyboard::is_context_menu_event (&event->button)) {
1146 /* no matter which button pops up the context menu, tell the menu
1147 widget to use button 1 to drive menu selection.
1150 switch (item_type) {
1152 case FadeInHandleItem:
1154 case FadeOutHandleItem:
1155 popup_fade_context_menu (1, event->button.time, item, item_type);
1159 popup_track_context_menu (1, event->button.time, item_type, false, where);
1163 case RegionViewNameHighlight:
1164 case RegionViewName:
1165 popup_track_context_menu (1, event->button.time, item_type, false, where);
1169 popup_track_context_menu (1, event->button.time, item_type, true, where);
1172 case AutomationTrackItem:
1173 popup_track_context_menu (1, event->button.time, item_type, false, where);
1177 case RangeMarkerBarItem:
1178 case TransportMarkerBarItem:
1179 case CdMarkerBarItem:
1182 popup_ruler_menu (where, item_type);
1186 marker_context_menu (&event->button, item);
1189 case TempoMarkerItem:
1190 tm_marker_context_menu (&event->button, item);
1193 case MeterMarkerItem:
1194 tm_marker_context_menu (&event->button, item);
1197 case CrossfadeViewItem:
1198 popup_track_context_menu (1, event->button.time, item_type, false, where);
1202 case ImageFrameItem:
1203 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1205 case ImageFrameTimeAxisItem:
1206 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1208 case MarkerViewItem:
1209 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1211 case MarkerTimeAxisItem:
1212 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1224 /* delete events get handled here */
1226 Editing::MouseMode const eff = effective_mouse_mode ();
1228 if (_drag == 0 && Keyboard::is_delete_event (&event->button)) {
1230 switch (item_type) {
1231 case TempoMarkerItem:
1232 remove_tempo_marker (item);
1235 case MeterMarkerItem:
1236 remove_meter_marker (item);
1240 remove_marker (*item, event);
1244 if (eff == MouseObject) {
1245 remove_clicked_region ();
1249 case ControlPointItem:
1250 if (eff == MouseGain) {
1251 remove_gain_control_point (item, event);
1253 remove_control_point (item, event);
1263 switch (event->button.button) {
1266 switch (item_type) {
1267 /* see comments in button_press_handler */
1268 case PlayheadCursorItem:
1271 case AutomationLineItem:
1272 case StartSelectionTrimItem:
1273 case EndSelectionTrimItem:
1277 if (!_dragging_playhead) {
1278 snap_to_with_modifier (where, event, 0, true);
1279 mouse_add_new_marker (where);
1283 case CdMarkerBarItem:
1284 if (!_dragging_playhead) {
1285 // if we get here then a dragged range wasn't done
1286 snap_to_with_modifier (where, event, 0, true);
1287 mouse_add_new_marker (where, true);
1292 if (!_dragging_playhead) {
1293 snap_to_with_modifier (where, event);
1294 mouse_add_new_tempo_event (where);
1299 if (!_dragging_playhead) {
1300 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1311 switch (item_type) {
1312 case AutomationTrackItem:
1313 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1315 atv->add_automation_event (item, event, where, event->button.y);
1326 // Gain only makes sense for audio regions
1328 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1332 switch (item_type) {
1334 /* check that we didn't drag before releasing, since
1335 its really annoying to create new control
1336 points when doing this.
1338 if (were_dragging) {
1339 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1344 case AutomationTrackItem:
1345 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1346 add_automation_event (item, event, where, event->button.y);
1355 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1356 if (scrubbing_direction == 0) {
1357 /* no drag, just a click */
1358 switch (item_type) {
1360 play_selected_region ();
1366 /* make sure we stop */
1367 _session->request_transport_speed (0.0);
1384 switch (item_type) {
1386 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1388 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1391 // Button2 click is unused
1404 // x_style_paste (where, 1.0);
1424 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1430 if (last_item_entered != item) {
1431 last_item_entered = item;
1432 last_item_entered_n = 0;
1435 switch (item_type) {
1436 case ControlPointItem:
1437 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1438 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1439 cp->set_visible (true);
1443 at_y = cp->get_y ();
1444 cp->item()->i2w (at_x, at_y);
1448 fraction = 1.0 - (cp->get_y() / cp->line().height());
1450 if (is_drawable() && dynamic_cast<ScrubDrag*> (_drag) == 0) {
1451 track_canvas->get_window()->set_cursor (*fader_cursor);
1454 last_item_entered_n++;
1455 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1456 if (last_item_entered_n < 10) {
1457 show_verbose_canvas_cursor ();
1463 if (mouse_mode == MouseGain) {
1464 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1466 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1467 if (is_drawable()) {
1468 track_canvas->get_window()->set_cursor (*fader_cursor);
1473 case AutomationLineItem:
1474 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1476 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1478 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1480 if (is_drawable()) {
1481 track_canvas->get_window()->set_cursor (*fader_cursor);
1486 case RegionViewNameHighlight:
1487 if (is_drawable() && mouse_mode == MouseObject) {
1488 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1492 case StartSelectionTrimItem:
1493 case EndSelectionTrimItem:
1496 case ImageFrameHandleStartItem:
1497 case ImageFrameHandleEndItem:
1498 case MarkerViewHandleStartItem:
1499 case MarkerViewHandleEndItem:
1502 if (is_drawable()) {
1503 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1507 case PlayheadCursorItem:
1508 if (is_drawable()) {
1509 switch (_edit_point) {
1511 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1514 track_canvas->get_window()->set_cursor (*grabber_cursor);
1520 case RegionViewName:
1522 /* when the name is not an active item, the entire name highlight is for trimming */
1524 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1525 if (mouse_mode == MouseObject && is_drawable()) {
1526 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1532 case AutomationTrackItem:
1533 if (is_drawable()) {
1534 Gdk::Cursor *cursor;
1535 switch (mouse_mode) {
1537 cursor = selector_cursor;
1540 cursor = zoom_cursor;
1543 cursor = cross_hair_cursor;
1547 track_canvas->get_window()->set_cursor (*cursor);
1549 AutomationTimeAxisView* atv;
1550 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1551 clear_entered_track = false;
1552 set_entered_track (atv);
1558 case RangeMarkerBarItem:
1559 case TransportMarkerBarItem:
1560 case CdMarkerBarItem:
1563 if (is_drawable()) {
1564 track_canvas->get_window()->set_cursor (*timebar_cursor);
1569 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1572 entered_marker = marker;
1573 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1575 case MeterMarkerItem:
1576 case TempoMarkerItem:
1577 if (is_drawable()) {
1578 track_canvas->get_window()->set_cursor (*timebar_cursor);
1581 case FadeInHandleItem:
1582 case FadeOutHandleItem:
1583 if (mouse_mode == MouseObject) {
1584 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1586 rect->property_fill_color_rgba() = 0;
1587 rect->property_outline_pixels() = 1;
1596 /* second pass to handle entered track status in a comprehensible way.
1599 switch (item_type) {
1601 case AutomationLineItem:
1602 case ControlPointItem:
1603 /* these do not affect the current entered track state */
1604 clear_entered_track = false;
1607 case AutomationTrackItem:
1608 /* handled above already */
1612 set_entered_track (0);
1620 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1629 switch (item_type) {
1630 case ControlPointItem:
1631 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1632 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1633 if (cp->line().npoints() > 1 && !cp->selected()) {
1634 cp->set_visible (false);
1638 if (is_drawable()) {
1639 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1642 hide_verbose_canvas_cursor ();
1645 case RegionViewNameHighlight:
1646 case StartSelectionTrimItem:
1647 case EndSelectionTrimItem:
1648 case PlayheadCursorItem:
1651 case ImageFrameHandleStartItem:
1652 case ImageFrameHandleEndItem:
1653 case MarkerViewHandleStartItem:
1654 case MarkerViewHandleEndItem:
1657 if (is_drawable()) {
1658 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1663 case AutomationLineItem:
1664 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1666 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1668 line->property_fill_color_rgba() = al->get_line_color();
1670 if (is_drawable()) {
1671 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1675 case RegionViewName:
1676 /* see enter_handler() for notes */
1677 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1678 if (is_drawable() && mouse_mode == MouseObject) {
1679 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1684 case RangeMarkerBarItem:
1685 case TransportMarkerBarItem:
1686 case CdMarkerBarItem:
1690 if (is_drawable()) {
1691 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1696 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1700 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1701 location_flags_changed (loc, this);
1704 case MeterMarkerItem:
1705 case TempoMarkerItem:
1707 if (is_drawable()) {
1708 track_canvas->get_window()->set_cursor (*timebar_cursor);
1713 case FadeInHandleItem:
1714 case FadeOutHandleItem:
1715 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1717 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1719 rect->property_fill_color_rgba() = rv->get_fill_color();
1720 rect->property_outline_pixels() = 0;
1725 case AutomationTrackItem:
1726 if (is_drawable()) {
1727 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1728 clear_entered_track = true;
1729 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1737 if (item_type == RegionItem) {
1738 update_join_object_range_location (event->crossing.x, event->crossing.y);
1745 Editor::left_automation_track ()
1747 if (clear_entered_track) {
1748 set_entered_track (0);
1749 clear_entered_track = false;
1759 if (scrubbing_direction == 0) {
1761 _session->request_locate (_drag->adjusted_current_frame (0), false);
1762 _session->request_transport_speed (0.1);
1763 scrubbing_direction = 1;
1767 if (last_scrub_x > _drag->current_pointer_x()) {
1769 /* pointer moved to the left */
1771 if (scrubbing_direction > 0) {
1773 /* we reversed direction to go backwards */
1776 scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x());
1780 /* still moving to the left (backwards) */
1782 scrub_reversals = 0;
1783 scrub_reverse_distance = 0;
1785 delta = 0.01 * (last_scrub_x - _drag->current_pointer_x());
1786 _session->request_transport_speed (_session->transport_speed() - delta);
1790 /* pointer moved to the right */
1792 if (scrubbing_direction < 0) {
1793 /* we reversed direction to go forward */
1796 scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x);
1799 /* still moving to the right */
1801 scrub_reversals = 0;
1802 scrub_reverse_distance = 0;
1804 delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x);
1805 _session->request_transport_speed (_session->transport_speed() + delta);
1809 /* if there have been more than 2 opposite motion moves detected, or one that moves
1810 back more than 10 pixels, reverse direction
1813 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1815 if (scrubbing_direction > 0) {
1816 /* was forwards, go backwards */
1817 _session->request_transport_speed (-0.1);
1818 scrubbing_direction = -1;
1820 /* was backwards, go forwards */
1821 _session->request_transport_speed (0.1);
1822 scrubbing_direction = 1;
1825 scrub_reverse_distance = 0;
1826 scrub_reversals = 0;
1830 last_scrub_x = _drag->current_pointer_x();
1834 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1836 if (event->motion.is_hint) {
1839 /* We call this so that MOTION_NOTIFY events continue to be
1840 delivered to the canvas. We need to do this because we set
1841 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1842 the density of the events, at the expense of a round-trip
1843 to the server. Given that this will mostly occur on cases
1844 where DISPLAY = :0.0, and given the cost of what the motion
1845 event might do, its a good tradeoff.
1848 track_canvas->get_pointer (x, y);
1851 if (current_stepping_trackview) {
1852 /* don't keep the persistent stepped trackview if the mouse moves */
1853 current_stepping_trackview = 0;
1854 step_timeout.disconnect ();
1857 if (_session && _session->actively_recording()) {
1858 /* Sorry. no dragging stuff around while we record */
1862 JoinObjectRangeState const old = _join_object_range_state;
1863 update_join_object_range_location (event->motion.x, event->motion.y);
1864 if (_join_object_range_state != old) {
1865 set_canvas_cursor ();
1868 bool handled = false;
1870 handled = _drag->motion_handler (event, from_autoscroll);
1877 track_canvas_motion (event);
1882 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1884 ControlPoint* control_point;
1886 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1887 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1891 // We shouldn't remove the first or last gain point
1892 if (control_point->line().is_last_point(*control_point) ||
1893 control_point->line().is_first_point(*control_point)) {
1897 control_point->line().remove_point (*control_point);
1901 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1903 ControlPoint* control_point;
1905 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1906 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1910 control_point->line().remove_point (*control_point);
1914 Editor::edit_control_point (ArdourCanvas::Item* item)
1916 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1919 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1923 ControlPointDialog d (p);
1924 d.set_position (Gtk::WIN_POS_MOUSE);
1927 if (d.run () != RESPONSE_ACCEPT) {
1931 p->line().modify_point_y (*p, d.get_y_fraction ());
1936 Editor::visible_order_range (int* low, int* high) const
1938 *low = TimeAxisView::max_order ();
1941 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1943 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1945 if (!rtv->hidden()) {
1947 if (*high < rtv->order()) {
1948 *high = rtv->order ();
1951 if (*low > rtv->order()) {
1952 *low = rtv->order ();
1959 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1961 /* Either add to or set the set the region selection, unless
1962 this is an alignment click (control used)
1965 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1966 TimeAxisView* tv = &rv.get_time_axis_view();
1967 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1969 if (rtv && rtv->is_track()) {
1970 speed = rtv->get_diskstream()->speed();
1973 nframes64_t where = get_preferred_edit_position();
1977 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1979 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1981 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1983 align_region (rv.region(), End, (nframes64_t) (where * speed));
1987 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1994 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
1997 Timecode::Time timecode;
2000 nframes64_t frame_rate;
2003 if (_session == 0) {
2009 if (Profile->get_sae() || Profile->get_small_screen()) {
2010 m = ARDOUR_UI::instance()->primary_clock.mode();
2012 m = ARDOUR_UI::instance()->secondary_clock.mode();
2016 case AudioClock::BBT:
2017 _session->bbt_time (frame, bbt);
2018 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2021 case AudioClock::Timecode:
2022 _session->timecode_time (frame, timecode);
2023 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2026 case AudioClock::MinSec:
2027 /* XXX this is copied from show_verbose_duration_cursor() */
2028 frame_rate = _session->frame_rate();
2029 hours = frame / (frame_rate * 3600);
2030 frame = frame % (frame_rate * 3600);
2031 mins = frame / (frame_rate * 60);
2032 frame = frame % (frame_rate * 60);
2033 secs = (float) frame / (float) frame_rate;
2034 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2038 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2042 if (xpos >= 0 && ypos >=0) {
2043 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2045 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);
2047 show_verbose_canvas_cursor ();
2051 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2054 Timecode::Time timecode;
2058 nframes64_t distance, frame_rate;
2060 Meter meter_at_start(_session->tempo_map().meter_at(start));
2062 if (_session == 0) {
2068 if (Profile->get_sae() || Profile->get_small_screen()) {
2069 m = ARDOUR_UI::instance()->primary_clock.mode ();
2071 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2075 case AudioClock::BBT:
2076 _session->bbt_time (start, sbbt);
2077 _session->bbt_time (end, ebbt);
2080 /* XXX this computation won't work well if the
2081 user makes a selection that spans any meter changes.
2084 ebbt.bars -= sbbt.bars;
2085 if (ebbt.beats >= sbbt.beats) {
2086 ebbt.beats -= sbbt.beats;
2089 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2091 if (ebbt.ticks >= sbbt.ticks) {
2092 ebbt.ticks -= sbbt.ticks;
2095 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2098 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2101 case AudioClock::Timecode:
2102 _session->timecode_duration (end - start, timecode);
2103 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2106 case AudioClock::MinSec:
2107 /* XXX this stuff should be elsewhere.. */
2108 distance = end - start;
2109 frame_rate = _session->frame_rate();
2110 hours = distance / (frame_rate * 3600);
2111 distance = distance % (frame_rate * 3600);
2112 mins = distance / (frame_rate * 60);
2113 distance = distance % (frame_rate * 60);
2114 secs = (float) distance / (float) frame_rate;
2115 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2119 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2123 if (xpos >= 0 && ypos >=0) {
2124 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2127 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset, _drag->current_pointer_y() + offset);
2130 show_verbose_canvas_cursor ();
2134 Editor::collect_new_region_view (RegionView* rv)
2136 latest_regionviews.push_back (rv);
2140 Editor::collect_and_select_new_region_view (RegionView* rv)
2143 latest_regionviews.push_back (rv);
2147 Editor::cancel_selection ()
2149 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2150 (*i)->hide_selection ();
2153 selection->clear ();
2154 clicked_selection = 0;
2159 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
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 if (swap_direction) {
2179 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2181 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2184 if (swap_direction) {
2185 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2187 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2192 snap_to (new_bound);
2194 region->trim_start ((nframes64_t) (new_bound * speed), this);
2195 rv.region_changed (StartChanged);
2199 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2201 boost::shared_ptr<Region> region (rv.region());
2203 if (region->locked()) {
2207 nframes64_t new_bound;
2210 TimeAxisView* tvp = clicked_axisview;
2211 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2213 if (tv && tv->is_track()) {
2214 speed = tv->get_diskstream()->speed();
2217 if (left_direction) {
2218 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2220 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2224 snap_to (new_bound, (left_direction ? 0 : 1));
2227 nframes64_t pre_trim_first_frame = region->first_frame();
2229 region->trim_front ((nframes64_t) (new_bound * speed), this);
2232 //Get the next region on the left of this region and shrink/expand it.
2233 boost::shared_ptr<Playlist> playlist (region->playlist());
2234 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2236 bool regions_touching = false;
2238 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2239 regions_touching = true;
2242 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2243 if (region_left != 0 &&
2244 (region_left->last_frame() > region->first_frame() || regions_touching))
2246 region_left->trim_end(region->first_frame() - 1, this);
2250 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2254 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2256 boost::shared_ptr<Region> region (rv.region());
2258 if (region->locked()) {
2262 nframes64_t new_bound;
2265 TimeAxisView* tvp = clicked_axisview;
2266 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2268 if (tv && tv->is_track()) {
2269 speed = tv->get_diskstream()->speed();
2272 if (left_direction) {
2273 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
2275 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
2279 snap_to (new_bound);
2282 nframes64_t pre_trim_last_frame = region->last_frame();
2284 region->trim_end ((nframes64_t) (new_bound * speed), this);
2287 //Get the next region on the right of this region and shrink/expand it.
2288 boost::shared_ptr<Playlist> playlist (region->playlist());
2289 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2291 bool regions_touching = false;
2293 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)){
2294 regions_touching = true;
2297 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2298 if (region_right != 0 &&
2299 (region_right->first_frame() < region->last_frame() || regions_touching))
2301 region_right->trim_front(region->last_frame() + 1, this);
2304 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2307 rv.region_changed (LengthChanged);
2313 Editor::point_trim (GdkEvent* event)
2315 RegionView* rv = clicked_regionview;
2317 nframes64_t new_bound = _drag->adjusted_current_frame (event);
2319 snap_to_with_modifier (new_bound, event);
2321 /* Choose action dependant on which button was pressed */
2322 switch (event->button.button) {
2324 begin_reversible_command (_("Start point trim"));
2326 if (selection->selected (rv)) {
2327 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2328 i != selection->regions.by_layer().end(); ++i)
2331 cerr << "region view contains null region" << endl;
2334 if (!(*i)->region()->locked()) {
2335 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2336 XMLNode &before = pl->get_state();
2338 (*i)->region()->trim_front (new_bound, this);
2340 XMLNode &after = pl->get_state();
2341 _session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2346 if (!rv->region()->locked()) {
2347 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2348 XMLNode &before = pl->get_state();
2349 rv->region()->trim_front (new_bound, this);
2350 XMLNode &after = pl->get_state();
2351 _session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2355 commit_reversible_command();
2359 begin_reversible_command (_("End point trim"));
2361 if (selection->selected (rv)) {
2363 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2365 if (!(*i)->region()->locked()) {
2366 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2367 XMLNode &before = pl->get_state();
2368 (*i)->region()->trim_end (new_bound, this);
2369 XMLNode &after = pl->get_state();
2370 _session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2376 if (!rv->region()->locked()) {
2377 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2378 XMLNode &before = pl->get_state();
2379 rv->region()->trim_end (new_bound, this);
2380 XMLNode &after = pl->get_state();
2381 _session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
2385 commit_reversible_command();
2394 Editor::thaw_region_after_trim (RegionView& rv)
2396 boost::shared_ptr<Region> region (rv.region());
2398 if (region->locked()) {
2402 region->thaw (_("trimmed region"));
2404 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2407 arv->unhide_envelope ();
2412 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2417 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2418 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2422 Location* location = find_location_from_marker (marker, is_start);
2423 location->set_hidden (true, this);
2428 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2430 double x1 = frame_to_pixel (start);
2431 double x2 = frame_to_pixel (end);
2432 double y2 = full_canvas_height - 1.0;
2434 zoom_rect->property_x1() = x1;
2435 zoom_rect->property_y1() = 1.0;
2436 zoom_rect->property_x2() = x2;
2437 zoom_rect->property_y2() = y2;
2442 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2444 using namespace Gtkmm2ext;
2446 ArdourPrompter prompter (false);
2448 prompter.set_prompt (_("Name for region:"));
2449 prompter.set_initial_text (clicked_regionview->region()->name());
2450 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2451 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2452 prompter.show_all ();
2453 switch (prompter.run ()) {
2454 case Gtk::RESPONSE_ACCEPT:
2456 prompter.get_result(str);
2458 clicked_regionview->region()->set_name (str);
2467 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2469 /* no brushing without a useful snap setting */
2471 switch (_snap_mode) {
2473 return; /* can't work because it allows region to be placed anywhere */
2478 switch (_snap_type) {
2486 /* don't brush a copy over the original */
2488 if (pos == rv->region()->position()) {
2492 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2494 if (rtv == 0 || !rtv->is_track()) {
2498 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2499 double speed = rtv->get_diskstream()->speed();
2501 XMLNode &before = playlist->get_state();
2502 playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
2503 XMLNode &after = playlist->get_state();
2504 _session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
2506 // playlist is frozen, so we have to update manually
2508 playlist->Modified(); /* EMIT SIGNAL */
2512 Editor::track_height_step_timeout ()
2514 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2515 current_stepping_trackview = 0;
2522 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2524 assert (region_view);
2526 _region_motion_group->raise_to_top ();
2528 assert (_drag == 0);
2530 if (Config->get_edit_mode() == Splice) {
2531 _drag = new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer());
2533 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2534 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false);
2537 _drag->start_grab (event);
2539 begin_reversible_command (_("move region(s)"));
2541 /* sync the canvas to what we think is its current state */
2542 update_canvas_now();
2546 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2548 assert (region_view);
2549 assert (_drag == 0);
2551 _region_motion_group->raise_to_top ();
2553 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2554 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true);
2555 _drag->start_grab(event);
2559 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2561 assert (region_view);
2562 assert (_drag == 0);
2564 if (Config->get_edit_mode() == Splice) {
2568 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2569 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false);
2570 _drag->start_grab (event);
2572 begin_reversible_command (_("Drag region brush"));
2575 /** Start a grab where a time range is selected, track(s) are selected, and the
2576 * user clicks and drags a region with a modifier in order to create a new region containing
2577 * the section of the clicked region that lies within the time range.
2580 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2582 if (clicked_regionview == 0) {
2586 /* lets try to create new Region for the selection */
2588 vector<boost::shared_ptr<Region> > new_regions;
2589 create_region_from_selection (new_regions);
2591 if (new_regions.empty()) {
2595 /* XXX fix me one day to use all new regions */
2597 boost::shared_ptr<Region> region (new_regions.front());
2599 /* add it to the current stream/playlist.
2601 tricky: the streamview for the track will add a new regionview. we will
2602 catch the signal it sends when it creates the regionview to
2603 set the regionview we want to then drag.
2606 latest_regionviews.clear();
2607 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2609 /* A selection grab currently creates two undo/redo operations, one for
2610 creating the new region and another for moving it.
2613 begin_reversible_command (_("selection grab"));
2615 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2617 XMLNode *before = &(playlist->get_state());
2618 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2619 XMLNode *after = &(playlist->get_state());
2620 _session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
2622 commit_reversible_command ();
2626 if (latest_regionviews.empty()) {
2627 /* something went wrong */
2631 /* we need to deselect all other regionviews, and select this one
2632 i'm ignoring undo stuff, because the region creation will take care of it
2634 selection->set (latest_regionviews);
2636 assert (_drag == 0);
2637 _drag = new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false);
2638 _drag->start_grab (event);
2642 Editor::break_drag ()
2645 _drag->break_drag ();
2650 Editor::set_internal_edit (bool yn)
2652 _internal_editing = yn;
2655 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2656 mouse_select_button.get_image ()->show ();
2658 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2659 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2661 mtv->start_step_editing ();
2664 start_step_editing ();
2668 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2669 mouse_select_button.get_image ()->show ();
2670 stop_step_editing ();
2672 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2673 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2675 mtv->stop_step_editing ();
2680 set_canvas_cursor ();
2685 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2686 * used by the `join object/range' tool mode.
2689 Editor::update_join_object_range_location (double x, double y)
2691 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2692 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2696 if (entered_regionview) {
2700 entered_regionview->get_canvas_group()->w2i (cx, cy);
2706 entered_regionview->get_canvas_group()->get_bounds (x1, y1, x2, y2);
2708 bool const top_half = cy < (y2 - y1) / 2;
2710 _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2714 if (mouse_mode == MouseObject) {
2715 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2716 } else if (mouse_mode == MouseRange) {
2717 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2723 Editor::effective_mouse_mode () const
2725 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2727 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {