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)
274 if (_drags->active ()) {
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 clicked_selection = 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)
517 if (_drags->active ()) {
521 /* single mouse clicks on any of these item types operate
522 independent of mouse mode, mostly because they are
523 not on the main track canvas or because we want
528 case PlayheadCursorItem:
529 _drags->set (new CursorDrag (this, item, true), event);
533 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
534 hide_marker (item, event);
536 _drags->set (new MarkerDrag (this, item), event);
540 case TempoMarkerItem:
542 new TempoMarkerDrag (
545 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
551 case MeterMarkerItem:
553 new MeterMarkerDrag (
556 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
565 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
566 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
572 case RangeMarkerBarItem:
573 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
574 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
576 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
581 case CdMarkerBarItem:
582 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
583 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
585 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
590 case TransportMarkerBarItem:
591 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
592 _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
594 _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
603 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
604 /* special case: allow trim of range selections in joined object mode;
605 in theory eff should equal MouseRange in this case, but it doesn't
606 because entering the range selection canvas item results in entered_regionview
607 being set to 0, so update_join_object_range_location acts as if we aren't
610 if (item_type == StartSelectionTrimItem) {
611 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
612 } else if (item_type == EndSelectionTrimItem) {
613 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
617 Editing::MouseMode eff = effective_mouse_mode ();
619 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
620 if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
627 case StartSelectionTrimItem:
628 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
631 case EndSelectionTrimItem:
632 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
636 if (Keyboard::modifier_state_contains
637 (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
638 // contains and not equals because I can't use alt as a modifier alone.
639 start_selection_grab (item, event);
640 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
641 /* grab selection for moving */
642 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
644 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
645 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
647 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
648 if (join_object_range_button.get_active() && atv) {
649 /* smart "join" mode: drag automation */
650 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
652 /* this was debated, but decided the more common action was to
653 make a new selection */
654 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
661 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
669 if (internal_editing()) {
670 /* Note: we don't get here if not in internal_editing() mode */
671 _drags->set (new NoteDrag (this, item), event);
680 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
681 event->type == GDK_BUTTON_PRESS) {
683 _drags->set (new RubberbandSelectDrag (this, item), event);
685 } else if (event->type == GDK_BUTTON_PRESS) {
688 case FadeInHandleItem:
690 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.id);
691 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
695 case FadeOutHandleItem:
697 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.id);
698 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event);
703 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
704 add_region_copy_drag (item, event, clicked_regionview);
705 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
706 add_region_brush_drag (item, event, clicked_regionview);
708 add_region_drag (item, event, clicked_regionview);
711 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
712 _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
715 _drags->start_grab (event);
718 case RegionViewNameHighlight:
720 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.id);
721 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
728 /* rename happens on edit clicks */
729 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.id);
730 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
735 case ControlPointItem:
736 _drags->set (new ControlPointDrag (this, item), event);
740 case AutomationLineItem:
741 _drags->set (new LineDrag (this, item), event);
746 if (internal_editing()) {
747 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
750 _drags->set (new RubberbandSelectDrag (this, item), event);
754 case AutomationTrackItem:
755 /* rubberband drag to select automation points */
756 _drags->set (new RubberbandSelectDrag (this, item), event);
761 if (join_object_range_button.get_active()) {
762 /* we're in "smart" joined mode, and we've clicked on a Selection */
763 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
764 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
766 /* if we're over an automation track, start a drag of its data */
767 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
769 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
772 /* if we're over a track and a region, and in the `object' part of a region,
773 put a selection around the region and drag both
775 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
776 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
777 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
779 boost::shared_ptr<Playlist> pl = t->diskstream()->playlist ();
782 boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
784 RegionView* rv = rtv->view()->find_view (r);
785 clicked_selection = select_range_around_region (rv);
786 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
787 list<RegionView*> rvs;
789 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
790 _drags->start_grab (event);
801 case ImageFrameHandleStartItem:
802 imageframe_start_handle_op(item, event) ;
805 case ImageFrameHandleEndItem:
806 imageframe_end_handle_op(item, event) ;
809 case MarkerViewHandleStartItem:
810 markerview_item_start_handle_op(item, event) ;
813 case MarkerViewHandleEndItem:
814 markerview_item_end_handle_op(item, event) ;
818 start_markerview_grab(item, event) ;
821 start_imageframe_grab(item, event) ;
839 /* start a grab so that if we finish after moving
840 we can tell what happened.
842 _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
846 _drags->set (new LineDrag (this, item), event);
849 case ControlPointItem:
850 _drags->set (new ControlPointDrag (this, item), event);
861 case ControlPointItem:
862 _drags->set (new ControlPointDrag (this, item), event);
865 case AutomationLineItem:
866 _drags->set (new LineDrag (this, item), event);
870 // XXX need automation mode to identify which
872 // start_line_grab_from_regionview (item, event);
882 if (event->type == GDK_BUTTON_PRESS) {
883 _drags->set (new MouseZoomDrag (this, item), event);
890 if (internal_editing() && item_type == NoteItem) {
891 _drags->set (new NoteResizeDrag (this, item), event);
893 } else if (!internal_editing() && item_type == RegionItem) {
894 _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
900 _drags->set (new ScrubDrag (this, item), event);
902 scrub_reverse_distance = 0;
903 last_scrub_x = event->button.x;
904 scrubbing_direction = 0;
905 track_canvas->get_window()->set_cursor (*transparent_cursor);
917 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
919 Editing::MouseMode const eff = effective_mouse_mode ();
924 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
925 add_region_copy_drag (item, event, clicked_regionview);
927 add_region_drag (item, event, clicked_regionview);
929 _drags->start_grab (event);
932 case ControlPointItem:
933 _drags->set (new ControlPointDrag (this, item), event);
942 case RegionViewNameHighlight:
943 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
948 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
959 /* relax till release */
965 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
966 temporal_zoom_session();
968 temporal_zoom_to_frame (true, event_frame(event));
981 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
983 if (event->type != GDK_BUTTON_PRESS) {
987 Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
990 Glib::RefPtr<const Gdk::Window> pointer_window;
993 Gdk::ModifierType mask;
995 pointer_window = canvas_window->get_pointer (x, y, mask);
997 if (pointer_window == track_canvas->get_bin_window()) {
998 track_canvas->window_to_world (x, y, wx, wy);
999 allow_vertical_scroll = true;
1001 allow_vertical_scroll = false;
1005 track_canvas->grab_focus();
1007 if (_session && _session->actively_recording()) {
1011 button_selection (item, event, item_type);
1013 if (!_drags->active () &&
1014 (Keyboard::is_delete_event (&event->button) ||
1015 Keyboard::is_context_menu_event (&event->button) ||
1016 Keyboard::is_edit_event (&event->button))) {
1018 /* handled by button release */
1022 switch (event->button.button) {
1024 return button_press_handler_1 (item, event, item_type);
1028 return button_press_handler_2 (item, event, item_type);
1043 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1045 nframes64_t where = event_frame (event, 0, 0);
1046 AutomationTimeAxisView* atv = 0;
1048 /* no action if we're recording */
1050 if (_session && _session->actively_recording()) {
1054 /* first, see if we're finishing a drag ... */
1056 bool were_dragging = false;
1057 if (_drags->active ()) {
1058 bool const r = _drags->end_grab (event);
1060 /* grab dragged, so do nothing else */
1064 were_dragging = true;
1067 button_selection (item, event, item_type);
1069 /* edit events get handled here */
1071 if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1072 switch (item_type) {
1077 case TempoMarkerItem:
1078 edit_tempo_marker (item);
1081 case MeterMarkerItem:
1082 edit_meter_marker (item);
1085 case RegionViewName:
1086 if (clicked_regionview->name_active()) {
1087 return mouse_rename_region (item, event);
1091 case ControlPointItem:
1092 edit_control_point (item);
1101 /* context menu events get handled here */
1103 if (Keyboard::is_context_menu_event (&event->button)) {
1105 if (!_drags->active ()) {
1107 /* no matter which button pops up the context menu, tell the menu
1108 widget to use button 1 to drive menu selection.
1111 switch (item_type) {
1113 case FadeInHandleItem:
1115 case FadeOutHandleItem:
1116 popup_fade_context_menu (1, event->button.time, item, item_type);
1120 popup_track_context_menu (1, event->button.time, item_type, false, where);
1124 case RegionViewNameHighlight:
1125 case RegionViewName:
1126 popup_track_context_menu (1, event->button.time, item_type, false, where);
1130 popup_track_context_menu (1, event->button.time, item_type, true, where);
1133 case AutomationTrackItem:
1134 popup_track_context_menu (1, event->button.time, item_type, false, where);
1138 case RangeMarkerBarItem:
1139 case TransportMarkerBarItem:
1140 case CdMarkerBarItem:
1143 popup_ruler_menu (where, item_type);
1147 marker_context_menu (&event->button, item);
1150 case TempoMarkerItem:
1151 tm_marker_context_menu (&event->button, item);
1154 case MeterMarkerItem:
1155 tm_marker_context_menu (&event->button, item);
1158 case CrossfadeViewItem:
1159 popup_track_context_menu (1, event->button.time, item_type, false, where);
1163 case ImageFrameItem:
1164 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1166 case ImageFrameTimeAxisItem:
1167 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1169 case MarkerViewItem:
1170 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1172 case MarkerTimeAxisItem:
1173 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1185 /* delete events get handled here */
1187 Editing::MouseMode const eff = effective_mouse_mode ();
1189 if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1191 switch (item_type) {
1192 case TempoMarkerItem:
1193 remove_tempo_marker (item);
1196 case MeterMarkerItem:
1197 remove_meter_marker (item);
1201 remove_marker (*item, event);
1205 if (eff == MouseObject) {
1206 remove_clicked_region ();
1210 case ControlPointItem:
1211 if (eff == MouseGain) {
1212 remove_gain_control_point (item, event);
1214 remove_control_point (item, event);
1224 switch (event->button.button) {
1227 switch (item_type) {
1228 /* see comments in button_press_handler */
1229 case PlayheadCursorItem:
1232 case AutomationLineItem:
1233 case StartSelectionTrimItem:
1234 case EndSelectionTrimItem:
1238 if (!_dragging_playhead) {
1239 snap_to_with_modifier (where, event, 0, true);
1240 mouse_add_new_marker (where);
1244 case CdMarkerBarItem:
1245 if (!_dragging_playhead) {
1246 // if we get here then a dragged range wasn't done
1247 snap_to_with_modifier (where, event, 0, true);
1248 mouse_add_new_marker (where, true);
1253 if (!_dragging_playhead) {
1254 snap_to_with_modifier (where, event);
1255 mouse_add_new_tempo_event (where);
1260 if (!_dragging_playhead) {
1261 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1272 switch (item_type) {
1273 case AutomationTrackItem:
1274 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1276 atv->add_automation_event (item, event, where, event->button.y);
1287 // Gain only makes sense for audio regions
1289 if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1293 switch (item_type) {
1295 /* check that we didn't drag before releasing, since
1296 its really annoying to create new control
1297 points when doing this.
1299 if (were_dragging) {
1300 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1305 case AutomationTrackItem:
1306 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1307 add_automation_event (item, event, where, event->button.y);
1316 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1317 if (scrubbing_direction == 0) {
1318 /* no drag, just a click */
1319 switch (item_type) {
1321 play_selected_region ();
1327 /* make sure we stop */
1328 _session->request_transport_speed (0.0);
1345 switch (item_type) {
1347 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1349 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1352 // Button2 click is unused
1365 // x_style_paste (where, 1.0);
1385 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1391 if (last_item_entered != item) {
1392 last_item_entered = item;
1393 last_item_entered_n = 0;
1396 switch (item_type) {
1397 case ControlPointItem:
1398 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1399 cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1400 cp->set_visible (true);
1404 at_y = cp->get_y ();
1405 cp->i2w (at_x, at_y);
1409 fraction = 1.0 - (cp->get_y() / cp->line().height());
1411 if (is_drawable() && !_drags->active ()) {
1412 track_canvas->get_window()->set_cursor (*fader_cursor);
1415 last_item_entered_n++;
1416 set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1417 if (last_item_entered_n < 10) {
1418 show_verbose_canvas_cursor ();
1424 if (mouse_mode == MouseGain) {
1425 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1427 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1428 if (is_drawable()) {
1429 track_canvas->get_window()->set_cursor (*fader_cursor);
1434 case AutomationLineItem:
1435 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1437 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1439 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1441 if (is_drawable()) {
1442 track_canvas->get_window()->set_cursor (*fader_cursor);
1447 case RegionViewNameHighlight:
1448 if (is_drawable() && mouse_mode == MouseObject) {
1449 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1453 case StartSelectionTrimItem:
1454 case EndSelectionTrimItem:
1457 case ImageFrameHandleStartItem:
1458 case ImageFrameHandleEndItem:
1459 case MarkerViewHandleStartItem:
1460 case MarkerViewHandleEndItem:
1463 if (is_drawable()) {
1464 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1468 case PlayheadCursorItem:
1469 if (is_drawable()) {
1470 switch (_edit_point) {
1472 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1475 track_canvas->get_window()->set_cursor (*grabber_cursor);
1481 case RegionViewName:
1483 /* when the name is not an active item, the entire name highlight is for trimming */
1485 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1486 if (mouse_mode == MouseObject && is_drawable()) {
1487 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1493 case AutomationTrackItem:
1494 if (is_drawable()) {
1495 Gdk::Cursor *cursor;
1496 switch (mouse_mode) {
1498 cursor = selector_cursor;
1501 cursor = zoom_cursor;
1504 cursor = cross_hair_cursor;
1508 track_canvas->get_window()->set_cursor (*cursor);
1510 AutomationTimeAxisView* atv;
1511 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1512 clear_entered_track = false;
1513 set_entered_track (atv);
1519 case RangeMarkerBarItem:
1520 case TransportMarkerBarItem:
1521 case CdMarkerBarItem:
1524 if (is_drawable()) {
1525 track_canvas->get_window()->set_cursor (*timebar_cursor);
1530 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1533 entered_marker = marker;
1534 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1536 case MeterMarkerItem:
1537 case TempoMarkerItem:
1538 if (is_drawable()) {
1539 track_canvas->get_window()->set_cursor (*timebar_cursor);
1542 case FadeInHandleItem:
1543 case FadeOutHandleItem:
1544 if (mouse_mode == MouseObject) {
1545 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1547 rect->property_fill_color_rgba() = 0;
1548 rect->property_outline_pixels() = 1;
1550 track_canvas->get_window()->set_cursor (*grabber_cursor);
1558 /* second pass to handle entered track status in a comprehensible way.
1561 switch (item_type) {
1563 case AutomationLineItem:
1564 case ControlPointItem:
1565 /* these do not affect the current entered track state */
1566 clear_entered_track = false;
1569 case AutomationTrackItem:
1570 /* handled above already */
1574 set_entered_track (0);
1582 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1591 switch (item_type) {
1592 case ControlPointItem:
1593 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1594 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1595 if (cp->line().npoints() > 1 && !cp->selected()) {
1596 cp->set_visible (false);
1600 if (is_drawable()) {
1601 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1604 hide_verbose_canvas_cursor ();
1607 case RegionViewNameHighlight:
1608 case StartSelectionTrimItem:
1609 case EndSelectionTrimItem:
1610 case PlayheadCursorItem:
1613 case ImageFrameHandleStartItem:
1614 case ImageFrameHandleEndItem:
1615 case MarkerViewHandleStartItem:
1616 case MarkerViewHandleEndItem:
1619 if (is_drawable()) {
1620 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1625 case AutomationLineItem:
1626 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1628 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1630 line->property_fill_color_rgba() = al->get_line_color();
1632 if (is_drawable()) {
1633 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1637 case RegionViewName:
1638 /* see enter_handler() for notes */
1639 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1640 if (is_drawable() && mouse_mode == MouseObject) {
1641 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1646 case RangeMarkerBarItem:
1647 case TransportMarkerBarItem:
1648 case CdMarkerBarItem:
1652 if (is_drawable()) {
1653 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1658 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1662 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1663 location_flags_changed (loc, this);
1666 case MeterMarkerItem:
1667 case TempoMarkerItem:
1669 if (is_drawable()) {
1670 track_canvas->get_window()->set_cursor (*timebar_cursor);
1675 case FadeInHandleItem:
1676 case FadeOutHandleItem:
1677 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1679 ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1681 rect->property_fill_color_rgba() = rv->get_fill_color();
1682 rect->property_outline_pixels() = 0;
1685 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1688 case AutomationTrackItem:
1689 if (is_drawable()) {
1690 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1691 clear_entered_track = true;
1692 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1704 Editor::left_automation_track ()
1706 if (clear_entered_track) {
1707 set_entered_track (0);
1708 clear_entered_track = false;
1714 Editor::scrub (nframes64_t frame, double current_x)
1718 if (scrubbing_direction == 0) {
1720 _session->request_locate (frame, false);
1721 _session->request_transport_speed (0.1);
1722 scrubbing_direction = 1;
1726 if (last_scrub_x > current_x) {
1728 /* pointer moved to the left */
1730 if (scrubbing_direction > 0) {
1732 /* we reversed direction to go backwards */
1735 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1739 /* still moving to the left (backwards) */
1741 scrub_reversals = 0;
1742 scrub_reverse_distance = 0;
1744 delta = 0.01 * (last_scrub_x - current_x);
1745 _session->request_transport_speed (_session->transport_speed() - delta);
1749 /* pointer moved to the right */
1751 if (scrubbing_direction < 0) {
1752 /* we reversed direction to go forward */
1755 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1758 /* still moving to the right */
1760 scrub_reversals = 0;
1761 scrub_reverse_distance = 0;
1763 delta = 0.01 * (current_x - last_scrub_x);
1764 _session->request_transport_speed (_session->transport_speed() + delta);
1768 /* if there have been more than 2 opposite motion moves detected, or one that moves
1769 back more than 10 pixels, reverse direction
1772 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1774 if (scrubbing_direction > 0) {
1775 /* was forwards, go backwards */
1776 _session->request_transport_speed (-0.1);
1777 scrubbing_direction = -1;
1779 /* was backwards, go forwards */
1780 _session->request_transport_speed (0.1);
1781 scrubbing_direction = 1;
1784 scrub_reverse_distance = 0;
1785 scrub_reversals = 0;
1789 last_scrub_x = current_x;
1793 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1795 if (event->motion.is_hint) {
1798 /* We call this so that MOTION_NOTIFY events continue to be
1799 delivered to the canvas. We need to do this because we set
1800 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1801 the density of the events, at the expense of a round-trip
1802 to the server. Given that this will mostly occur on cases
1803 where DISPLAY = :0.0, and given the cost of what the motion
1804 event might do, its a good tradeoff.
1807 track_canvas->get_pointer (x, y);
1810 if (current_stepping_trackview) {
1811 /* don't keep the persistent stepped trackview if the mouse moves */
1812 current_stepping_trackview = 0;
1813 step_timeout.disconnect ();
1816 if (_session && _session->actively_recording()) {
1817 /* Sorry. no dragging stuff around while we record */
1821 JoinObjectRangeState const old = _join_object_range_state;
1822 update_join_object_range_location (event->motion.x, event->motion.y);
1823 if (_join_object_range_state != old) {
1824 set_canvas_cursor ();
1827 bool handled = false;
1828 if (_drags->active ()) {
1829 handled = _drags->motion_handler (event, from_autoscroll);
1836 track_canvas_motion (event);
1841 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1843 ControlPoint* control_point;
1845 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1846 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1850 // We shouldn't remove the first or last gain point
1851 if (control_point->line().is_last_point(*control_point) ||
1852 control_point->line().is_first_point(*control_point)) {
1856 control_point->line().remove_point (*control_point);
1860 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1862 ControlPoint* control_point;
1864 if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1865 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1869 control_point->line().remove_point (*control_point);
1873 Editor::edit_control_point (ArdourCanvas::Item* item)
1875 ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1878 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1882 ControlPointDialog d (p);
1883 d.set_position (Gtk::WIN_POS_MOUSE);
1886 if (d.run () != RESPONSE_ACCEPT) {
1890 p->line().modify_point_y (*p, d.get_y_fraction ());
1895 Editor::visible_order_range (int* low, int* high) const
1897 *low = TimeAxisView::max_order ();
1900 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1902 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1904 if (!rtv->hidden()) {
1906 if (*high < rtv->order()) {
1907 *high = rtv->order ();
1910 if (*low > rtv->order()) {
1911 *low = rtv->order ();
1918 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1920 /* Either add to or set the set the region selection, unless
1921 this is an alignment click (control used)
1924 if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1925 TimeAxisView* tv = &rv.get_time_axis_view();
1926 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1928 if (rtv && rtv->is_track()) {
1929 speed = rtv->get_diskstream()->speed();
1932 nframes64_t where = get_preferred_edit_position();
1936 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1938 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1940 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1942 align_region (rv.region(), End, (nframes64_t) (where * speed));
1946 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1953 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
1956 Timecode::Time timecode;
1959 nframes64_t frame_rate;
1962 if (_session == 0) {
1968 if (Profile->get_sae() || Profile->get_small_screen()) {
1969 m = ARDOUR_UI::instance()->primary_clock.mode();
1971 m = ARDOUR_UI::instance()->secondary_clock.mode();
1975 case AudioClock::BBT:
1976 _session->bbt_time (frame, bbt);
1977 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
1980 case AudioClock::Timecode:
1981 _session->timecode_time (frame, timecode);
1982 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1985 case AudioClock::MinSec:
1986 /* XXX this is copied from show_verbose_duration_cursor() */
1987 frame_rate = _session->frame_rate();
1988 hours = frame / (frame_rate * 3600);
1989 frame = frame % (frame_rate * 3600);
1990 mins = frame / (frame_rate * 60);
1991 frame = frame % (frame_rate * 60);
1992 secs = (float) frame / (float) frame_rate;
1993 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
1997 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2001 if (xpos >= 0 && ypos >=0) {
2002 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2004 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_adjustment.get_value(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2006 show_verbose_canvas_cursor ();
2010 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
2013 Timecode::Time timecode;
2017 nframes64_t distance, frame_rate;
2019 Meter meter_at_start(_session->tempo_map().meter_at(start));
2021 if (_session == 0) {
2027 if (Profile->get_sae() || Profile->get_small_screen()) {
2028 m = ARDOUR_UI::instance()->primary_clock.mode ();
2030 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2034 case AudioClock::BBT:
2035 _session->bbt_time (start, sbbt);
2036 _session->bbt_time (end, ebbt);
2039 /* XXX this computation won't work well if the
2040 user makes a selection that spans any meter changes.
2043 ebbt.bars -= sbbt.bars;
2044 if (ebbt.beats >= sbbt.beats) {
2045 ebbt.beats -= sbbt.beats;
2048 ebbt.beats = int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2050 if (ebbt.ticks >= sbbt.ticks) {
2051 ebbt.ticks -= sbbt.ticks;
2054 ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2057 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2060 case AudioClock::Timecode:
2061 _session->timecode_duration (end - start, timecode);
2062 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2065 case AudioClock::MinSec:
2066 /* XXX this stuff should be elsewhere.. */
2067 distance = end - start;
2068 frame_rate = _session->frame_rate();
2069 hours = distance / (frame_rate * 3600);
2070 distance = distance % (frame_rate * 3600);
2071 mins = distance / (frame_rate * 60);
2072 distance = distance % (frame_rate * 60);
2073 secs = (float) distance / (float) frame_rate;
2074 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2078 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2082 if (xpos >= 0 && ypos >=0) {
2083 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2086 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2089 show_verbose_canvas_cursor ();
2093 Editor::collect_new_region_view (RegionView* rv)
2095 latest_regionviews.push_back (rv);
2099 Editor::collect_and_select_new_region_view (RegionView* rv)
2102 latest_regionviews.push_back (rv);
2106 Editor::cancel_selection ()
2108 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2109 (*i)->hide_selection ();
2112 selection->clear ();
2113 clicked_selection = 0;
2118 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
2120 boost::shared_ptr<Region> region (rv.region());
2122 if (region->locked()) {
2126 nframes64_t new_bound;
2129 TimeAxisView* tvp = clicked_axisview;
2130 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2132 if (tv && tv->is_track()) {
2133 speed = tv->get_diskstream()->speed();
2136 if (left_direction) {
2137 if (swap_direction) {
2138 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2140 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2143 if (swap_direction) {
2144 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2146 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2151 snap_to (new_bound);
2153 region->trim_start ((nframes64_t) (new_bound * speed), this);
2154 rv.region_changed (StartChanged);
2158 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2160 boost::shared_ptr<Region> region (rv.region());
2162 if (region->locked()) {
2166 nframes64_t new_bound;
2169 TimeAxisView* tvp = clicked_axisview;
2170 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2172 if (tv && tv->is_track()) {
2173 speed = tv->get_diskstream()->speed();
2176 if (left_direction) {
2177 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2179 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2183 snap_to (new_bound, (left_direction ? 0 : 1));
2186 nframes64_t pre_trim_first_frame = region->first_frame();
2188 region->trim_front ((nframes64_t) (new_bound * speed), this);
2191 //Get the next region on the left of this region and shrink/expand it.
2192 boost::shared_ptr<Playlist> playlist (region->playlist());
2193 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2195 bool regions_touching = false;
2197 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2198 regions_touching = true;
2201 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2202 if (region_left != 0 &&
2203 (region_left->last_frame() > region->first_frame() || regions_touching))
2205 region_left->trim_end(region->first_frame() - 1, this);
2209 rv.region_changed (PropertyChange (LengthChanged|PositionChanged|StartChanged));
2213 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2215 boost::shared_ptr<Region> region (rv.region());
2217 if (region->locked()) {
2221 nframes64_t new_bound;
2224 TimeAxisView* tvp = clicked_axisview;
2225 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2227 if (tv && tv->is_track()) {
2228 speed = tv->get_diskstream()->speed();
2231 if (left_direction) {
2232 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
2234 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
2238 snap_to (new_bound);
2241 nframes64_t pre_trim_last_frame = region->last_frame();
2243 region->trim_end ((nframes64_t) (new_bound * speed), this);
2246 //Get the next region on the right of this region and shrink/expand it.
2247 boost::shared_ptr<Playlist> playlist (region->playlist());
2248 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2250 bool regions_touching = false;
2252 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)){
2253 regions_touching = true;
2256 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2257 if (region_right != 0 &&
2258 (region_right->first_frame() < region->last_frame() || regions_touching))
2260 region_right->trim_front(region->last_frame() + 1, this);
2263 rv.region_changed (PropertyChange (LengthChanged|PositionChanged|StartChanged));
2266 rv.region_changed (LengthChanged);
2272 Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
2274 RegionView* rv = clicked_regionview;
2276 /* Choose action dependant on which button was pressed */
2277 switch (event->button.button) {
2279 begin_reversible_command (_("Start point trim"));
2281 if (selection->selected (rv)) {
2282 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2283 i != selection->regions.by_layer().end(); ++i)
2286 cerr << "region view contains null region" << endl;
2289 if (!(*i)->region()->locked()) {
2290 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2291 XMLNode &before = pl->get_state();
2293 (*i)->region()->trim_front (new_bound, this);
2295 XMLNode &after = pl->get_state();
2296 _session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2301 if (!rv->region()->locked()) {
2302 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2303 XMLNode &before = pl->get_state();
2304 rv->region()->trim_front (new_bound, this);
2305 XMLNode &after = pl->get_state();
2306 _session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2310 commit_reversible_command();
2314 begin_reversible_command (_("End point trim"));
2316 if (selection->selected (rv)) {
2318 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2320 if (!(*i)->region()->locked()) {
2321 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2322 XMLNode &before = pl->get_state();
2323 (*i)->region()->trim_end (new_bound, this);
2324 XMLNode &after = pl->get_state();
2325 _session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2331 if (!rv->region()->locked()) {
2332 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2333 XMLNode &before = pl->get_state();
2334 rv->region()->trim_end (new_bound, this);
2335 XMLNode &after = pl->get_state();
2336 _session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
2340 commit_reversible_command();
2349 Editor::thaw_region_after_trim (RegionView& rv)
2351 boost::shared_ptr<Region> region (rv.region());
2353 if (region->locked()) {
2359 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2362 arv->unhide_envelope ();
2367 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2372 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2373 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2377 Location* location = find_location_from_marker (marker, is_start);
2378 location->set_hidden (true, this);
2383 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2385 double x1 = frame_to_pixel (start);
2386 double x2 = frame_to_pixel (end);
2387 double y2 = full_canvas_height - 1.0;
2389 zoom_rect->property_x1() = x1;
2390 zoom_rect->property_y1() = 1.0;
2391 zoom_rect->property_x2() = x2;
2392 zoom_rect->property_y2() = y2;
2397 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2399 using namespace Gtkmm2ext;
2401 ArdourPrompter prompter (false);
2403 prompter.set_prompt (_("Name for region:"));
2404 prompter.set_initial_text (clicked_regionview->region()->name());
2405 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2406 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2407 prompter.show_all ();
2408 switch (prompter.run ()) {
2409 case Gtk::RESPONSE_ACCEPT:
2411 prompter.get_result(str);
2413 clicked_regionview->region()->set_name (str);
2422 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2424 /* no brushing without a useful snap setting */
2426 switch (_snap_mode) {
2428 return; /* can't work because it allows region to be placed anywhere */
2433 switch (_snap_type) {
2441 /* don't brush a copy over the original */
2443 if (pos == rv->region()->position()) {
2447 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2449 if (rtv == 0 || !rtv->is_track()) {
2453 boost::shared_ptr<Playlist> playlist = rtv->playlist();
2454 double speed = rtv->get_diskstream()->speed();
2456 XMLNode &before = playlist->get_state();
2457 boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
2458 playlist->add_region (new_region, (nframes64_t) (pos * speed));
2459 XMLNode &after = playlist->get_state();
2460 _session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
2462 // playlist is frozen, so we have to update manually XXX this is disgusting
2464 playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2468 Editor::track_height_step_timeout ()
2470 if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2471 current_stepping_trackview = 0;
2478 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2480 assert (region_view);
2482 _region_motion_group->raise_to_top ();
2484 if (Config->get_edit_mode() == Splice) {
2485 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2487 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.id);
2488 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2491 /* sync the canvas to what we think is its current state */
2492 update_canvas_now();
2496 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2498 assert (region_view);
2500 _region_motion_group->raise_to_top ();
2502 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.id);
2503 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2507 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2509 assert (region_view);
2511 if (Config->get_edit_mode() == Splice) {
2515 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.id);
2516 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2518 begin_reversible_command (_("Drag region brush"));
2521 /** Start a grab where a time range is selected, track(s) are selected, and the
2522 * user clicks and drags a region with a modifier in order to create a new region containing
2523 * the section of the clicked region that lies within the time range.
2526 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2528 if (clicked_regionview == 0) {
2532 /* lets try to create new Region for the selection */
2534 vector<boost::shared_ptr<Region> > new_regions;
2535 create_region_from_selection (new_regions);
2537 if (new_regions.empty()) {
2541 /* XXX fix me one day to use all new regions */
2543 boost::shared_ptr<Region> region (new_regions.front());
2545 /* add it to the current stream/playlist.
2547 tricky: the streamview for the track will add a new regionview. we will
2548 catch the signal it sends when it creates the regionview to
2549 set the regionview we want to then drag.
2552 latest_regionviews.clear();
2553 sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2555 /* A selection grab currently creates two undo/redo operations, one for
2556 creating the new region and another for moving it.
2559 begin_reversible_command (_("selection grab"));
2561 boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2563 XMLNode *before = &(playlist->get_state());
2564 clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2565 XMLNode *after = &(playlist->get_state());
2566 _session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
2568 commit_reversible_command ();
2572 if (latest_regionviews.empty()) {
2573 /* something went wrong */
2577 /* we need to deselect all other regionviews, and select this one
2578 i'm ignoring undo stuff, because the region creation will take care of it
2580 selection->set (latest_regionviews);
2582 _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2588 if (_drags->active ()) {
2589 _drags->break_drag ();
2591 selection->clear ();
2596 Editor::set_internal_edit (bool yn)
2598 _internal_editing = yn;
2601 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2602 mouse_select_button.get_image ()->show ();
2604 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2605 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2607 mtv->start_step_editing ();
2610 start_step_editing ();
2614 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2615 mouse_select_button.get_image ()->show ();
2616 stop_step_editing ();
2618 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2619 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
2621 mtv->stop_step_editing ();
2626 set_canvas_cursor ();
2631 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2632 * used by the `join object/range' tool mode.
2635 Editor::update_join_object_range_location (double x, double y)
2637 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2638 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2639 that we're over requires searching the playlist.
2642 if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2643 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2647 if (mouse_mode == MouseObject) {
2648 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2649 } else if (mouse_mode == MouseRange) {
2650 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2653 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2654 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2658 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2663 rtv->canvas_display()->w2i (cx, cy);
2665 bool const top_half = cy < rtv->current_height () / 2;
2667 _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2673 Editor::effective_mouse_mode () const
2675 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2677 } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {