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"
34 #include "pbd/stateful_diff_command.h"
36 #include "ardour_ui.h"
39 #include "time_axis_view.h"
40 #include "audio_time_axis.h"
41 #include "audio_region_view.h"
42 #include "midi_region_view.h"
44 #include "streamview.h"
45 #include "region_gain_line.h"
46 #include "automation_time_axis.h"
47 #include "control_point.h"
50 #include "selection.h"
53 #include "rgb_macros.h"
54 #include "control_point_dialog.h"
55 #include "editor_drag.h"
57 #include "ardour/types.h"
58 #include "ardour/profile.h"
59 #include "ardour/route.h"
60 #include "ardour/audio_track.h"
61 #include "ardour/audio_diskstream.h"
62 #include "ardour/midi_diskstream.h"
63 #include "ardour/playlist.h"
64 #include "ardour/audioplaylist.h"
65 #include "ardour/audioregion.h"
66 #include "ardour/midi_region.h"
67 #include "ardour/dB.h"
68 #include "ardour/utils.h"
69 #include "ardour/region_factory.h"
70 #include "ardour/source_factory.h"
71 #include "ardour/session.h"
78 using namespace ARDOUR
;
81 using namespace Editing
;
82 using Gtkmm2ext::Keyboard
;
85 Editor::mouse_frame (nframes64_t
& where
, bool& in_track_canvas
) const
89 Gdk::ModifierType mask
;
90 Glib::RefPtr
<Gdk::Window
> canvas_window
= const_cast<Editor
*>(this)->track_canvas
->get_window();
91 Glib::RefPtr
<const Gdk::Window
> pointer_window
;
97 pointer_window
= canvas_window
->get_pointer (x
, y
, mask
);
99 if (pointer_window
== track_canvas
->get_bin_window()) {
102 in_track_canvas
= true;
105 in_track_canvas
= false;
110 event
.type
= GDK_BUTTON_RELEASE
;
114 where
= event_frame (&event
, 0, 0);
119 Editor::event_frame (GdkEvent
const * event
, double* pcx
, double* pcy
) const
133 switch (event
->type
) {
134 case GDK_BUTTON_RELEASE
:
135 case GDK_BUTTON_PRESS
:
136 case GDK_2BUTTON_PRESS
:
137 case GDK_3BUTTON_PRESS
:
138 *pcx
= event
->button
.x
;
139 *pcy
= event
->button
.y
;
140 _trackview_group
->w2i(*pcx
, *pcy
);
142 case GDK_MOTION_NOTIFY
:
143 *pcx
= event
->motion
.x
;
144 *pcy
= event
->motion
.y
;
145 _trackview_group
->w2i(*pcx
, *pcy
);
147 case GDK_ENTER_NOTIFY
:
148 case GDK_LEAVE_NOTIFY
:
149 track_canvas
->w2c(event
->crossing
.x
, event
->crossing
.y
, *pcx
, *pcy
);
152 case GDK_KEY_RELEASE
:
153 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
156 warning
<< string_compose (_("Editor::event_frame() used on unhandled event type %1"), event
->type
) << endmsg
;
160 /* note that pixel_to_frame() never returns less than zero, so even if the pixel
161 position is negative (as can be the case with motion events in particular),
162 the frame location is always positive.
165 return pixel_to_frame (*pcx
);
169 Editor::which_grabber_cursor ()
171 Gdk::Cursor
* c
= grabber_cursor
;
173 if (_internal_editing
) {
174 switch (mouse_mode
) {
176 c
= midi_pencil_cursor
;
184 c
= midi_resize_cursor
;
193 switch (_edit_point
) {
195 c
= grabber_edit_point_cursor
;
206 Editor::set_canvas_cursor ()
208 if (_internal_editing
) {
210 switch (mouse_mode
) {
212 current_canvas_cursor
= midi_pencil_cursor
;
216 current_canvas_cursor
= which_grabber_cursor();
220 current_canvas_cursor
= midi_resize_cursor
;
229 switch (mouse_mode
) {
231 current_canvas_cursor
= selector_cursor
;
235 current_canvas_cursor
= which_grabber_cursor();
239 current_canvas_cursor
= cross_hair_cursor
;
243 current_canvas_cursor
= zoom_cursor
;
247 current_canvas_cursor
= time_fx_cursor
; // just use playhead
251 current_canvas_cursor
= speaker_cursor
;
256 switch (_join_object_range_state
) {
257 case JOIN_OBJECT_RANGE_NONE
:
259 case JOIN_OBJECT_RANGE_OBJECT
:
260 current_canvas_cursor
= which_grabber_cursor ();
262 case JOIN_OBJECT_RANGE_RANGE
:
263 current_canvas_cursor
= selector_cursor
;
268 track_canvas
->get_window()->set_cursor(*current_canvas_cursor
);
273 Editor::set_mouse_mode (MouseMode m
, bool force
)
275 if (_drags
->active ()) {
279 if (!force
&& m
== mouse_mode
) {
283 Glib::RefPtr
<Action
> act
;
287 act
= ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
291 act
= ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
295 act
= ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
299 act
= ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
303 act
= ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
307 act
= ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
313 Glib::RefPtr
<ToggleAction
> tact
= Glib::RefPtr
<ToggleAction
>::cast_dynamic (act
);
316 /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
317 tact
->set_active (false);
318 tact
->set_active (true);
322 Editor::mouse_mode_toggled (MouseMode m
)
328 cerr
<< "Mouse mode toggled to " << m
<< endl
;
330 if (!internal_editing()) {
331 if (mouse_mode
!= MouseRange
&& _join_object_range_state
== JOIN_OBJECT_RANGE_NONE
) {
333 /* in all modes except range and joined object/range, hide the range selection,
334 show the object (region) selection.
337 for (RegionSelection::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
338 (*i
)->set_should_show_selection (true);
340 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
341 (*i
)->hide_selection ();
347 in range or object/range mode, show the range selection.
350 for (TrackSelection::iterator i
= selection
->tracks
.begin(); i
!= selection
->tracks
.end(); ++i
) {
351 (*i
)->show_selection (selection
->time
);
356 set_canvas_cursor ();
360 Editor::step_mouse_mode (bool next
)
362 switch (current_mouse_mode()) {
365 if (Profile
->get_sae()) {
366 set_mouse_mode (MouseZoom
);
368 set_mouse_mode (MouseRange
);
371 set_mouse_mode (MouseTimeFX
);
376 if (next
) set_mouse_mode (MouseZoom
);
377 else set_mouse_mode (MouseObject
);
382 if (Profile
->get_sae()) {
383 set_mouse_mode (MouseTimeFX
);
385 set_mouse_mode (MouseGain
);
388 if (Profile
->get_sae()) {
389 set_mouse_mode (MouseObject
);
391 set_mouse_mode (MouseRange
);
397 if (next
) set_mouse_mode (MouseTimeFX
);
398 else set_mouse_mode (MouseZoom
);
403 set_mouse_mode (MouseAudition
);
405 if (Profile
->get_sae()) {
406 set_mouse_mode (MouseZoom
);
408 set_mouse_mode (MouseGain
);
414 if (next
) set_mouse_mode (MouseObject
);
415 else set_mouse_mode (MouseTimeFX
);
421 Editor::button_selection (ArdourCanvas::Item
* /*item*/, GdkEvent
* event
, ItemType item_type
)
423 /* in object/audition/timefx/gain-automation mode,
424 any button press sets the selection if the object
425 can be selected. this is a bit of hack, because
426 we want to avoid this if the mouse operation is a
429 note: not dbl-click or triple-click
432 if (((mouse_mode
!= MouseObject
) &&
433 (_join_object_range_state
!= JOIN_OBJECT_RANGE_OBJECT
) &&
434 (mouse_mode
!= MouseAudition
|| item_type
!= RegionItem
) &&
435 (mouse_mode
!= MouseTimeFX
|| item_type
!= RegionItem
) &&
436 (mouse_mode
!= MouseGain
) &&
437 (mouse_mode
!= MouseRange
)) ||
439 ((event
->type
!= GDK_BUTTON_PRESS
&& event
->type
!= GDK_BUTTON_RELEASE
) || event
->button
.button
> 3)) {
444 if (event
->type
== GDK_BUTTON_PRESS
|| event
->type
== GDK_BUTTON_RELEASE
) {
446 if ((event
->button
.state
& Keyboard::RelevantModifierKeyMask
) && event
->button
.button
!= 1) {
448 /* almost no selection action on modified button-2 or button-3 events */
450 if (item_type
!= RegionItem
&& event
->button
.button
!= 2) {
456 Selection::Operation op
= ArdourKeyboard::selection_type (event
->button
.state
);
457 bool press
= (event
->type
== GDK_BUTTON_PRESS
);
459 // begin_reversible_command (_("select on click"));
463 if (mouse_mode
!= MouseRange
|| internal_editing() || _join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
464 set_selected_regionview_from_click (press
, op
, true);
465 } else if (event
->type
== GDK_BUTTON_PRESS
) {
466 selection
->clear_tracks ();
467 set_selected_track_as_side_effect (true);
469 if (_join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
&& !selection
->regions
.empty()) {
470 clicked_selection
= select_range_around_region (selection
->regions
.front());
475 case RegionViewNameHighlight
:
477 case LeftFrameHandle
:
478 case RightFrameHandle
:
479 if (mouse_mode
!= MouseRange
|| internal_editing() || _join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
480 set_selected_regionview_from_click (press
, op
, true);
481 } else if (event
->type
== GDK_BUTTON_PRESS
) {
482 set_selected_track_as_side_effect ();
487 case FadeInHandleItem
:
489 case FadeOutHandleItem
:
491 if (mouse_mode
!= MouseRange
|| _join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
492 set_selected_regionview_from_click (press
, op
, true);
493 } else if (event
->type
== GDK_BUTTON_PRESS
) {
494 set_selected_track_as_side_effect ();
498 case ControlPointItem
:
499 set_selected_track_as_side_effect ();
500 if (mouse_mode
!= MouseRange
|| _join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
501 set_selected_control_point_from_click (op
, false);
506 /* for context click, select track */
507 if (event
->button
.button
== 3) {
508 selection
->clear_tracks ();
509 set_selected_track_as_side_effect (true);
513 case AutomationTrackItem
:
514 set_selected_track_as_side_effect (true);
523 Editor::button_press_handler_1 (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
525 if (_drags
->active ()) {
529 /* single mouse clicks on any of these item types operate
530 independent of mouse mode, mostly because they are
531 not on the main track canvas or because we want
536 case PlayheadCursorItem
:
537 _drags
->set (new CursorDrag (this, item
, true), event
);
541 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::ModifierMask(Keyboard::PrimaryModifier
|Keyboard::TertiaryModifier
))) {
542 hide_marker (item
, event
);
544 _drags
->set (new MarkerDrag (this, item
), event
);
548 case TempoMarkerItem
:
550 new TempoMarkerDrag (
553 Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)
559 case MeterMarkerItem
:
561 new MeterMarkerDrag (
564 Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)
573 if (!Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
574 _drags
->set (new CursorDrag (this, &playhead_cursor
->canvas_item
, false), event
);
580 case RangeMarkerBarItem
:
581 if (!Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
582 _drags
->set (new CursorDrag (this, &playhead_cursor
->canvas_item
, false), event
);
584 _drags
->set (new RangeMarkerBarDrag (this, item
, RangeMarkerBarDrag::CreateRangeMarker
), event
);
589 case CdMarkerBarItem
:
590 if (!Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
591 _drags
->set (new CursorDrag (this, &playhead_cursor
->canvas_item
, false), event
);
593 _drags
->set (new RangeMarkerBarDrag (this, item
, RangeMarkerBarDrag::CreateCDMarker
), event
);
598 case TransportMarkerBarItem
:
599 if (!Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
600 _drags
->set (new CursorDrag (this, &playhead_cursor
->canvas_item
, false), event
);
602 _drags
->set (new RangeMarkerBarDrag (this, item
, RangeMarkerBarDrag::CreateTransportMarker
), event
);
611 if (_join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
612 /* special case: allow trim of range selections in joined object mode;
613 in theory eff should equal MouseRange in this case, but it doesn't
614 because entering the range selection canvas item results in entered_regionview
615 being set to 0, so update_join_object_range_location acts as if we aren't
618 if (item_type
== StartSelectionTrimItem
) {
619 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::SelectionStartTrim
), event
);
620 } else if (item_type
== EndSelectionTrimItem
) {
621 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::SelectionEndTrim
), event
);
625 Editing::MouseMode eff
= effective_mouse_mode ();
627 /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
628 if (item_type
== FadeInHandleItem
|| item_type
== FadeOutHandleItem
) {
635 case StartSelectionTrimItem
:
636 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::SelectionStartTrim
), event
);
639 case EndSelectionTrimItem
:
640 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::SelectionEndTrim
), event
);
644 if (Keyboard::modifier_state_contains
645 (event
->button
.state
, Keyboard::ModifierMask(Keyboard::PrimaryModifier
))) {
646 // contains and not equals because I can't use alt as a modifier alone.
647 start_selection_grab (item
, event
);
648 } else if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::SecondaryModifier
)) {
649 /* grab selection for moving */
650 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::SelectionMove
), event
);
652 double const y
= event
->button
.y
+ vertical_adjustment
.get_value() - canvas_timebars_vsize
;
653 pair
<TimeAxisView
*, int> tvp
= trackview_by_y_position (y
);
655 AutomationTimeAxisView
* atv
= dynamic_cast<AutomationTimeAxisView
*> (tvp
.first
);
656 if (join_object_range_button
.get_active() && atv
) {
657 /* smart "join" mode: drag automation */
658 _drags
->set (new AutomationRangeDrag (this, atv
->base_item(), selection
->time
), event
);
660 /* this was debated, but decided the more common action was to
661 make a new selection */
662 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::CreateSelection
), event
);
669 if (internal_editing()) {
670 /* trim notes if we're in internal edit mode and near the ends of the note */
671 _drags
->set (new NoteResizeDrag (this, item
), event
);
676 if (internal_editing()) {
677 _drags
->set (new RegionCreateDrag (this, item
, clicked_axisview
), event
);
680 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::CreateSelection
), event
);
685 case RegionViewNameHighlight
:
686 case LeftFrameHandle
:
687 case RightFrameHandle
:
689 RegionSelection s
= get_equivalent_regions (selection
->regions
, Properties::edit
.property_id
);
690 _drags
->set (new TrimDrag (this, item
, clicked_regionview
, s
.by_layer()), event
);
696 if (!internal_editing()) {
697 _drags
->set (new SelectionDrag (this, item
, SelectionDrag::CreateSelection
), event
);
706 if (internal_editing()) {
707 _drags
->set (new NoteDrag (this, item
), event
);
716 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::ModifierMask(Keyboard::PrimaryModifier
|Keyboard::SecondaryModifier
)) &&
717 event
->type
== GDK_BUTTON_PRESS
) {
719 _drags
->set (new RubberbandSelectDrag (this, item
), event
);
721 } else if (event
->type
== GDK_BUTTON_PRESS
) {
724 case FadeInHandleItem
:
726 RegionSelection s
= get_equivalent_regions (selection
->regions
, Properties::edit
.property_id
);
727 _drags
->set (new FadeInDrag (this, item
, reinterpret_cast<RegionView
*> (item
->get_data("regionview")), s
), event
);
731 case FadeOutHandleItem
:
733 RegionSelection s
= get_equivalent_regions (selection
->regions
, Properties::edit
.property_id
);
734 _drags
->set (new FadeOutDrag (this, item
, reinterpret_cast<RegionView
*> (item
->get_data("regionview")), s
), event
);
739 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)) {
740 add_region_copy_drag (item
, event
, clicked_regionview
);
741 } else if (Keyboard::the_keyboard().key_is_down (GDK_b
)) {
742 add_region_brush_drag (item
, event
, clicked_regionview
);
744 add_region_drag (item
, event
, clicked_regionview
);
747 if (_join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
&& !selection
->regions
.empty()) {
748 _drags
->add (new SelectionDrag (this, clicked_axisview
->get_selection_rect (clicked_selection
)->rect
, SelectionDrag::SelectionMove
));
751 _drags
->start_grab (event
);
754 case RegionViewNameHighlight
:
755 case LeftFrameHandle
:
756 case RightFrameHandle
:
758 RegionSelection s
= get_equivalent_regions (selection
->regions
, Properties::edit
.property_id
);
759 _drags
->set (new TrimDrag (this, item
, clicked_regionview
, s
.by_layer()), event
);
766 /* rename happens on edit clicks */
767 RegionSelection s
= get_equivalent_regions (selection
->regions
, Properties::edit
.property_id
);
768 _drags
->set (new TrimDrag (this, clicked_regionview
->get_name_highlight(), clicked_regionview
, s
.by_layer()), event
);
773 case ControlPointItem
:
774 _drags
->set (new ControlPointDrag (this, item
), event
);
778 case AutomationLineItem
:
779 _drags
->set (new LineDrag (this, item
), event
);
784 if (internal_editing()) {
785 _drags
->set (new RegionCreateDrag (this, item
, clicked_axisview
), event
);
788 _drags
->set (new RubberbandSelectDrag (this, item
), event
);
792 case AutomationTrackItem
:
793 /* rubberband drag to select automation points */
794 _drags
->set (new RubberbandSelectDrag (this, item
), event
);
799 if (join_object_range_button
.get_active()) {
800 /* we're in "smart" joined mode, and we've clicked on a Selection */
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 /* if we're over an automation track, start a drag of its data */
805 AutomationTimeAxisView
* atv
= dynamic_cast<AutomationTimeAxisView
*> (tvp
.first
);
807 _drags
->set (new AutomationRangeDrag (this, atv
->base_item(), selection
->time
), event
);
810 /* if we're over a track and a region, and in the `object' part of a region,
811 put a selection around the region and drag both
813 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (tvp
.first
);
814 if (rtv
&& _join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
815 boost::shared_ptr
<Track
> t
= boost::dynamic_pointer_cast
<Track
> (rtv
->route ());
817 boost::shared_ptr
<Playlist
> pl
= t
->playlist ();
820 boost::shared_ptr
<Region
> r
= pl
->top_region_at (event_frame (event
));
822 RegionView
* rv
= rtv
->view()->find_view (r
);
823 clicked_selection
= select_range_around_region (rv
);
824 _drags
->add (new SelectionDrag (this, item
, SelectionDrag::SelectionMove
));
825 list
<RegionView
*> rvs
;
827 _drags
->add (new RegionMoveDrag (this, item
, rv
, rvs
, false, false));
828 _drags
->start_grab (event
);
839 case ImageFrameHandleStartItem
:
840 imageframe_start_handle_op(item
, event
) ;
843 case ImageFrameHandleEndItem
:
844 imageframe_end_handle_op(item
, event
) ;
847 case MarkerViewHandleStartItem
:
848 markerview_item_start_handle_op(item
, event
) ;
851 case MarkerViewHandleEndItem
:
852 markerview_item_end_handle_op(item
, event
) ;
856 start_markerview_grab(item
, event
) ;
859 start_imageframe_grab(item
, event
) ;
877 /* start a grab so that if we finish after moving
878 we can tell what happened.
880 _drags
->set (new RegionGainDrag (this, item
), event
, current_canvas_cursor
);
884 _drags
->set (new LineDrag (this, item
), event
);
887 case ControlPointItem
:
888 _drags
->set (new ControlPointDrag (this, item
), event
);
899 case ControlPointItem
:
900 _drags
->set (new ControlPointDrag (this, item
), event
);
903 case AutomationLineItem
:
904 _drags
->set (new LineDrag (this, item
), event
);
908 // XXX need automation mode to identify which
910 // start_line_grab_from_regionview (item, event);
920 if (event
->type
== GDK_BUTTON_PRESS
) {
921 _drags
->set (new MouseZoomDrag (this, item
), event
);
928 if (internal_editing() && item_type
== NoteItem
) {
929 /* drag notes if we're in internal edit mode */
930 _drags
->set (new NoteResizeDrag (this, item
), event
);
932 } else if ((!internal_editing() || dynamic_cast<AudioRegionView
*> (clicked_regionview
)) && clicked_regionview
) {
933 /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
934 _drags
->set (new TimeFXDrag (this, item
, clicked_regionview
, selection
->regions
.by_layer()), event
);
940 _drags
->set (new ScrubDrag (this, item
), event
);
942 scrub_reverse_distance
= 0;
943 last_scrub_x
= event
->button
.x
;
944 scrubbing_direction
= 0;
945 track_canvas
->get_window()->set_cursor (*transparent_cursor
);
957 Editor::button_press_handler_2 (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
959 Editing::MouseMode
const eff
= effective_mouse_mode ();
964 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::CopyModifier
)) {
965 add_region_copy_drag (item
, event
, clicked_regionview
);
967 add_region_drag (item
, event
, clicked_regionview
);
969 _drags
->start_grab (event
);
972 case ControlPointItem
:
973 _drags
->set (new ControlPointDrag (this, item
), event
);
982 case RegionViewNameHighlight
:
983 case LeftFrameHandle
:
984 case RightFrameHandle
:
985 _drags
->set (new TrimDrag (this, item
, clicked_regionview
, selection
->regions
.by_layer()), event
);
990 _drags
->set (new TrimDrag (this, clicked_regionview
->get_name_highlight(), clicked_regionview
, selection
->regions
.by_layer()), event
);
1001 /* relax till release */
1007 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
1008 temporal_zoom_session();
1010 temporal_zoom_to_frame (true, event_frame(event
));
1023 Editor::button_press_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
1025 if (event
->type
!= GDK_BUTTON_PRESS
) {
1029 Glib::RefPtr
<Gdk::Window
> canvas_window
= const_cast<Editor
*>(this)->track_canvas
->get_window();
1031 if (canvas_window
) {
1032 Glib::RefPtr
<const Gdk::Window
> pointer_window
;
1035 Gdk::ModifierType mask
;
1037 pointer_window
= canvas_window
->get_pointer (x
, y
, mask
);
1039 if (pointer_window
== track_canvas
->get_bin_window()) {
1040 track_canvas
->window_to_world (x
, y
, wx
, wy
);
1041 allow_vertical_scroll
= true;
1043 allow_vertical_scroll
= false;
1047 track_canvas
->grab_focus();
1049 if (_session
&& _session
->actively_recording()) {
1053 button_selection (item
, event
, item_type
);
1055 if (!_drags
->active () &&
1056 (Keyboard::is_delete_event (&event
->button
) ||
1057 Keyboard::is_context_menu_event (&event
->button
) ||
1058 Keyboard::is_edit_event (&event
->button
))) {
1060 /* handled by button release */
1064 switch (event
->button
.button
) {
1066 return button_press_handler_1 (item
, event
, item_type
);
1070 return button_press_handler_2 (item
, event
, item_type
);
1085 Editor::button_release_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
1087 nframes64_t where
= event_frame (event
, 0, 0);
1088 AutomationTimeAxisView
* atv
= 0;
1090 /* no action if we're recording */
1092 if (_session
&& _session
->actively_recording()) {
1096 /* first, see if we're finishing a drag ... */
1098 bool were_dragging
= false;
1099 if (_drags
->active ()) {
1100 bool const r
= _drags
->end_grab (event
);
1102 /* grab dragged, so do nothing else */
1106 were_dragging
= true;
1109 button_selection (item
, event
, item_type
);
1111 /* edit events get handled here */
1113 if (!_drags
->active () && Keyboard::is_edit_event (&event
->button
)) {
1114 switch (item_type
) {
1119 case TempoMarkerItem
:
1120 edit_tempo_marker (item
);
1123 case MeterMarkerItem
:
1124 edit_meter_marker (item
);
1127 case RegionViewName
:
1128 if (clicked_regionview
->name_active()) {
1129 return mouse_rename_region (item
, event
);
1133 case ControlPointItem
:
1134 edit_control_point (item
);
1143 /* context menu events get handled here */
1145 if (Keyboard::is_context_menu_event (&event
->button
)) {
1147 if (!_drags
->active ()) {
1149 /* no matter which button pops up the context menu, tell the menu
1150 widget to use button 1 to drive menu selection.
1153 switch (item_type
) {
1155 case FadeInHandleItem
:
1157 case FadeOutHandleItem
:
1158 popup_fade_context_menu (1, event
->button
.time
, item
, item_type
);
1162 popup_track_context_menu (1, event
->button
.time
, item_type
, false, where
);
1166 case RegionViewNameHighlight
:
1167 case LeftFrameHandle
:
1168 case RightFrameHandle
:
1169 case RegionViewName
:
1170 popup_track_context_menu (1, event
->button
.time
, item_type
, false, where
);
1174 popup_track_context_menu (1, event
->button
.time
, item_type
, true, where
);
1177 case AutomationTrackItem
:
1178 popup_track_context_menu (1, event
->button
.time
, item_type
, false, where
);
1182 case RangeMarkerBarItem
:
1183 case TransportMarkerBarItem
:
1184 case CdMarkerBarItem
:
1187 popup_ruler_menu (where
, item_type
);
1191 marker_context_menu (&event
->button
, item
);
1194 case TempoMarkerItem
:
1195 tempo_or_meter_marker_context_menu (&event
->button
, item
);
1198 case MeterMarkerItem
:
1199 tempo_or_meter_marker_context_menu (&event
->button
, item
);
1202 case CrossfadeViewItem
:
1203 popup_track_context_menu (1, event
->button
.time
, item_type
, false, where
);
1207 case ImageFrameItem
:
1208 popup_imageframe_edit_menu(1, event
->button
.time
, item
, true) ;
1210 case ImageFrameTimeAxisItem
:
1211 popup_imageframe_edit_menu(1, event
->button
.time
, item
, false) ;
1213 case MarkerViewItem
:
1214 popup_marker_time_axis_edit_menu(1, event
->button
.time
, item
, true) ;
1216 case MarkerTimeAxisItem
:
1217 popup_marker_time_axis_edit_menu(1, event
->button
.time
, item
, false) ;
1229 /* delete events get handled here */
1231 Editing::MouseMode
const eff
= effective_mouse_mode ();
1233 if (!_drags
->active () && Keyboard::is_delete_event (&event
->button
)) {
1235 switch (item_type
) {
1236 case TempoMarkerItem
:
1237 remove_tempo_marker (item
);
1240 case MeterMarkerItem
:
1241 remove_meter_marker (item
);
1245 remove_marker (*item
, event
);
1249 if (eff
== MouseObject
) {
1250 remove_clicked_region ();
1254 case ControlPointItem
:
1255 if (eff
== MouseGain
) {
1256 remove_gain_control_point (item
, event
);
1258 remove_control_point (item
, event
);
1263 remove_midi_note (item
, event
);
1272 switch (event
->button
.button
) {
1275 switch (item_type
) {
1276 /* see comments in button_press_handler */
1277 case PlayheadCursorItem
:
1280 case AutomationLineItem
:
1281 case StartSelectionTrimItem
:
1282 case EndSelectionTrimItem
:
1286 if (!_dragging_playhead
) {
1287 snap_to_with_modifier (where
, event
, 0, true);
1288 mouse_add_new_marker (where
);
1292 case CdMarkerBarItem
:
1293 if (!_dragging_playhead
) {
1294 // if we get here then a dragged range wasn't done
1295 snap_to_with_modifier (where
, event
, 0, true);
1296 mouse_add_new_marker (where
, true);
1301 if (!_dragging_playhead
) {
1302 snap_to_with_modifier (where
, event
);
1303 mouse_add_new_tempo_event (where
);
1308 if (!_dragging_playhead
) {
1309 mouse_add_new_meter_event (pixel_to_frame (event
->button
.x
));
1320 switch (item_type
) {
1321 case AutomationTrackItem
:
1322 atv
= dynamic_cast<AutomationTimeAxisView
*>(clicked_axisview
);
1324 atv
->add_automation_event (item
, event
, where
, event
->button
.y
);
1335 switch (item_type
) {
1338 /* check that we didn't drag before releasing, since
1339 its really annoying to create new control
1340 points when doing this.
1342 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*> (clicked_regionview
);
1343 if (were_dragging
&& arv
) {
1344 arv
->add_gain_point_event (item
, event
);
1350 case AutomationTrackItem
:
1351 dynamic_cast<AutomationTimeAxisView
*>(clicked_axisview
)->
1352 add_automation_event (item
, event
, where
, event
->button
.y
);
1361 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1362 if (scrubbing_direction
== 0) {
1363 /* no drag, just a click */
1364 switch (item_type
) {
1366 play_selected_region ();
1372 /* make sure we stop */
1373 _session
->request_transport_speed (0.0);
1390 switch (item_type
) {
1392 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
1394 } else if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::ModifierMask (Keyboard::TertiaryModifier
|Keyboard::SecondaryModifier
))) {
1397 // Button2 click is unused
1410 // x_style_paste (where, 1.0);
1430 Editor::enter_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
1437 if (last_item_entered
!= item
) {
1438 last_item_entered
= item
;
1439 last_item_entered_n
= 0;
1442 switch (item_type
) {
1443 case ControlPointItem
:
1444 if (mouse_mode
== MouseGain
|| mouse_mode
== MouseObject
) {
1445 cp
= static_cast<ControlPoint
*>(item
->get_data ("control_point"));
1446 cp
->set_visible (true);
1450 at_y
= cp
->get_y ();
1451 cp
->i2w (at_x
, at_y
);
1455 fraction
= 1.0 - (cp
->get_y() / cp
->line().height());
1457 if (is_drawable() && !_drags
->active ()) {
1458 track_canvas
->get_window()->set_cursor (*fader_cursor
);
1461 last_item_entered_n
++;
1462 set_verbose_canvas_cursor (cp
->line().get_verbose_cursor_string (fraction
), at_x
, at_y
);
1463 if (last_item_entered_n
< 10) {
1464 show_verbose_canvas_cursor ();
1470 if (mouse_mode
== MouseGain
) {
1471 ArdourCanvas::Line
*line
= dynamic_cast<ArdourCanvas::Line
*> (item
);
1473 line
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine
.get();
1474 if (is_drawable()) {
1475 track_canvas
->get_window()->set_cursor (*fader_cursor
);
1480 case AutomationLineItem
:
1481 if (mouse_mode
== MouseGain
|| mouse_mode
== MouseObject
) {
1483 ArdourCanvas::Line
*line
= dynamic_cast<ArdourCanvas::Line
*> (item
);
1485 line
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine
.get();
1487 if (is_drawable()) {
1488 track_canvas
->get_window()->set_cursor (*fader_cursor
);
1493 case RegionViewNameHighlight
:
1494 if (is_drawable() && (mouse_mode
== MouseObject
|| (internal_editing() && mouse_mode
== MouseRange
))) {
1495 track_canvas
->get_window()->set_cursor (*trimmer_cursor
);
1499 case LeftFrameHandle
:
1500 if (is_drawable() && (mouse_mode
== MouseObject
|| (internal_editing() && mouse_mode
== MouseRange
))) {
1501 track_canvas
->get_window()->set_cursor (*left_side_trim_cursor
);
1505 case RightFrameHandle
:
1506 if (is_drawable() && (mouse_mode
== MouseObject
|| (internal_editing() && mouse_mode
== MouseRange
))) {
1507 track_canvas
->get_window()->set_cursor (*right_side_trim_cursor
);
1511 case StartSelectionTrimItem
:
1512 case EndSelectionTrimItem
:
1515 case ImageFrameHandleStartItem
:
1516 case ImageFrameHandleEndItem
:
1517 case MarkerViewHandleStartItem
:
1518 case MarkerViewHandleEndItem
:
1521 if (is_drawable()) {
1522 track_canvas
->get_window()->set_cursor (*trimmer_cursor
);
1526 case PlayheadCursorItem
:
1527 if (is_drawable()) {
1528 switch (_edit_point
) {
1530 track_canvas
->get_window()->set_cursor (*grabber_edit_point_cursor
);
1533 track_canvas
->get_window()->set_cursor (*grabber_cursor
);
1539 case RegionViewName
:
1541 /* when the name is not an active item, the entire name highlight is for trimming */
1543 if (!reinterpret_cast<RegionView
*> (item
->get_data ("regionview"))->name_active()) {
1544 if (mouse_mode
== MouseObject
&& is_drawable()) {
1545 track_canvas
->get_window()->set_cursor (*trimmer_cursor
);
1551 case AutomationTrackItem
:
1552 if (is_drawable()) {
1553 Gdk::Cursor
*cursor
;
1554 switch (mouse_mode
) {
1556 cursor
= selector_cursor
;
1559 cursor
= zoom_cursor
;
1562 cursor
= cross_hair_cursor
;
1566 track_canvas
->get_window()->set_cursor (*cursor
);
1568 AutomationTimeAxisView
* atv
;
1569 if ((atv
= static_cast<AutomationTimeAxisView
*>(item
->get_data ("trackview"))) != 0) {
1570 clear_entered_track
= false;
1571 set_entered_track (atv
);
1577 case RangeMarkerBarItem
:
1578 case TransportMarkerBarItem
:
1579 case CdMarkerBarItem
:
1582 if (is_drawable()) {
1583 track_canvas
->get_window()->set_cursor (*timebar_cursor
);
1588 if ((marker
= static_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
1591 entered_marker
= marker
;
1592 marker
->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker
.get());
1594 case MeterMarkerItem
:
1595 case TempoMarkerItem
:
1596 if (is_drawable()) {
1597 track_canvas
->get_window()->set_cursor (*timebar_cursor
);
1601 case FadeInHandleItem
:
1602 if (mouse_mode
== MouseObject
) {
1603 ArdourCanvas::SimpleRect
*rect
= dynamic_cast<ArdourCanvas::SimpleRect
*> (item
);
1605 rect
->property_fill_color_rgba() = 0;
1606 rect
->property_outline_pixels() = 1;
1608 track_canvas
->get_window()->set_cursor (*fade_in_cursor
);
1612 case FadeOutHandleItem
:
1613 if (mouse_mode
== MouseObject
) {
1614 ArdourCanvas::SimpleRect
*rect
= dynamic_cast<ArdourCanvas::SimpleRect
*> (item
);
1616 rect
->property_fill_color_rgba() = 0;
1617 rect
->property_outline_pixels() = 1;
1619 track_canvas
->get_window()->set_cursor (*fade_out_cursor
);
1627 /* second pass to handle entered track status in a comprehensible way.
1630 switch (item_type
) {
1632 case AutomationLineItem
:
1633 case ControlPointItem
:
1634 /* these do not affect the current entered track state */
1635 clear_entered_track
= false;
1638 case AutomationTrackItem
:
1639 /* handled above already */
1643 set_entered_track (0);
1651 Editor::leave_handler (ArdourCanvas::Item
* item
, GdkEvent
* event
, ItemType item_type
)
1661 switch (item_type
) {
1662 case ControlPointItem
:
1663 cp
= reinterpret_cast<ControlPoint
*>(item
->get_data ("control_point"));
1664 if (cp
->line().the_list()->interpolation() != AutomationList::Discrete
) {
1665 if (cp
->line().npoints() > 1 && !cp
->selected()) {
1666 cp
->set_visible (false);
1670 if (is_drawable()) {
1671 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1674 hide_verbose_canvas_cursor ();
1677 case RegionViewNameHighlight
:
1678 case LeftFrameHandle
:
1679 case RightFrameHandle
:
1680 case StartSelectionTrimItem
:
1681 case EndSelectionTrimItem
:
1682 case PlayheadCursorItem
:
1685 case ImageFrameHandleStartItem
:
1686 case ImageFrameHandleEndItem
:
1687 case MarkerViewHandleStartItem
:
1688 case MarkerViewHandleEndItem
:
1691 if (is_drawable()) {
1692 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1697 case AutomationLineItem
:
1698 al
= reinterpret_cast<AutomationLine
*> (item
->get_data ("line"));
1700 ArdourCanvas::Line
*line
= dynamic_cast<ArdourCanvas::Line
*> (item
);
1702 line
->property_fill_color_rgba() = al
->get_line_color();
1704 if (is_drawable()) {
1705 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1709 case RegionViewName
:
1710 /* see enter_handler() for notes */
1711 if (!reinterpret_cast<RegionView
*> (item
->get_data ("regionview"))->name_active()) {
1712 if (is_drawable() && mouse_mode
== MouseObject
) {
1713 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1718 case RangeMarkerBarItem
:
1719 case TransportMarkerBarItem
:
1720 case CdMarkerBarItem
:
1724 if (is_drawable()) {
1725 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1730 if ((marker
= static_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
1734 if ((loc
= find_location_from_marker (marker
, is_start
)) != 0) {
1735 location_flags_changed (loc
, this);
1738 case MeterMarkerItem
:
1739 case TempoMarkerItem
:
1741 if (is_drawable()) {
1742 track_canvas
->get_window()->set_cursor (*timebar_cursor
);
1747 case FadeInHandleItem
:
1748 case FadeOutHandleItem
:
1749 rv
= static_cast<RegionView
*>(item
->get_data ("regionview"));
1751 ArdourCanvas::SimpleRect
*rect
= dynamic_cast<ArdourCanvas::SimpleRect
*> (item
);
1753 rect
->property_fill_color_rgba() = rv
->get_fill_color();
1754 rect
->property_outline_pixels() = 0;
1757 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1760 case AutomationTrackItem
:
1761 if (is_drawable()) {
1762 track_canvas
->get_window()->set_cursor (*current_canvas_cursor
);
1763 clear_entered_track
= true;
1764 Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track
));
1776 Editor::left_automation_track ()
1778 if (clear_entered_track
) {
1779 set_entered_track (0);
1780 clear_entered_track
= false;
1786 Editor::scrub (nframes64_t frame
, double current_x
)
1790 if (scrubbing_direction
== 0) {
1792 _session
->request_locate (frame
, false);
1793 _session
->request_transport_speed (0.1);
1794 scrubbing_direction
= 1;
1798 if (last_scrub_x
> current_x
) {
1800 /* pointer moved to the left */
1802 if (scrubbing_direction
> 0) {
1804 /* we reversed direction to go backwards */
1807 scrub_reverse_distance
+= (int) (last_scrub_x
- current_x
);
1811 /* still moving to the left (backwards) */
1813 scrub_reversals
= 0;
1814 scrub_reverse_distance
= 0;
1816 delta
= 0.01 * (last_scrub_x
- current_x
);
1817 _session
->request_transport_speed (_session
->transport_speed() - delta
);
1821 /* pointer moved to the right */
1823 if (scrubbing_direction
< 0) {
1824 /* we reversed direction to go forward */
1827 scrub_reverse_distance
+= (int) (current_x
- last_scrub_x
);
1830 /* still moving to the right */
1832 scrub_reversals
= 0;
1833 scrub_reverse_distance
= 0;
1835 delta
= 0.01 * (current_x
- last_scrub_x
);
1836 _session
->request_transport_speed (_session
->transport_speed() + delta
);
1840 /* if there have been more than 2 opposite motion moves detected, or one that moves
1841 back more than 10 pixels, reverse direction
1844 if (scrub_reversals
>= 2 || scrub_reverse_distance
> 10) {
1846 if (scrubbing_direction
> 0) {
1847 /* was forwards, go backwards */
1848 _session
->request_transport_speed (-0.1);
1849 scrubbing_direction
= -1;
1851 /* was backwards, go forwards */
1852 _session
->request_transport_speed (0.1);
1853 scrubbing_direction
= 1;
1856 scrub_reverse_distance
= 0;
1857 scrub_reversals
= 0;
1861 last_scrub_x
= current_x
;
1865 Editor::motion_handler (ArdourCanvas::Item
* /*item*/, GdkEvent
* event
, bool from_autoscroll
)
1867 if (event
->motion
.is_hint
) {
1870 /* We call this so that MOTION_NOTIFY events continue to be
1871 delivered to the canvas. We need to do this because we set
1872 Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1873 the density of the events, at the expense of a round-trip
1874 to the server. Given that this will mostly occur on cases
1875 where DISPLAY = :0.0, and given the cost of what the motion
1876 event might do, its a good tradeoff.
1879 track_canvas
->get_pointer (x
, y
);
1882 if (current_stepping_trackview
) {
1883 /* don't keep the persistent stepped trackview if the mouse moves */
1884 current_stepping_trackview
= 0;
1885 step_timeout
.disconnect ();
1888 if (_session
&& _session
->actively_recording()) {
1889 /* Sorry. no dragging stuff around while we record */
1893 JoinObjectRangeState
const old
= _join_object_range_state
;
1894 update_join_object_range_location (event
->motion
.x
, event
->motion
.y
);
1895 if (_join_object_range_state
!= old
) {
1896 set_canvas_cursor ();
1899 bool handled
= false;
1900 if (_drags
->active ()) {
1901 handled
= _drags
->motion_handler (event
, from_autoscroll
);
1907 track_canvas_motion (event
);
1912 Editor::remove_gain_control_point (ArdourCanvas::Item
*item
, GdkEvent
* /*event*/)
1914 ControlPoint
* control_point
;
1916 if ((control_point
= reinterpret_cast<ControlPoint
*> (item
->get_data ("control_point"))) == 0) {
1917 fatal
<< _("programming error: control point canvas item has no control point object pointer!") << endmsg
;
1921 // We shouldn't remove the first or last gain point
1922 if (control_point
->line().is_last_point(*control_point
) ||
1923 control_point
->line().is_first_point(*control_point
)) {
1927 control_point
->line().remove_point (*control_point
);
1931 Editor::remove_control_point (ArdourCanvas::Item
* item
, GdkEvent
* /*event*/)
1933 ControlPoint
* control_point
;
1935 if ((control_point
= reinterpret_cast<ControlPoint
*> (item
->get_data ("control_point"))) == 0) {
1936 fatal
<< _("programming error: control point canvas item has no control point object pointer!") << endmsg
;
1940 control_point
->line().remove_point (*control_point
);
1944 Editor::edit_control_point (ArdourCanvas::Item
* item
)
1946 ControlPoint
* p
= reinterpret_cast<ControlPoint
*> (item
->get_data ("control_point"));
1949 fatal
<< _("programming error: control point canvas item has no control point object pointer!") << endmsg
;
1953 ControlPointDialog
d (p
);
1954 d
.set_position (Gtk::WIN_POS_MOUSE
);
1957 if (d
.run () != RESPONSE_ACCEPT
) {
1961 p
->line().modify_point_y (*p
, d
.get_y_fraction ());
1966 Editor::visible_order_range (int* low
, int* high
) const
1968 *low
= TimeAxisView::max_order ();
1971 for (TrackViewList::const_iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
1973 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (*i
);
1975 if (!rtv
->hidden()) {
1977 if (*high
< rtv
->order()) {
1978 *high
= rtv
->order ();
1981 if (*low
> rtv
->order()) {
1982 *low
= rtv
->order ();
1989 Editor::region_view_item_click (AudioRegionView
& rv
, GdkEventButton
* event
)
1991 /* Either add to or set the set the region selection, unless
1992 this is an alignment click (control used)
1995 if (Keyboard::modifier_state_contains (event
->state
, Keyboard::PrimaryModifier
)) {
1996 TimeAxisView
* tv
= &rv
.get_time_axis_view();
1997 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*>(tv
);
1999 if (rtv
&& rtv
->is_track()) {
2000 speed
= rtv
->track()->speed();
2003 nframes64_t where
= get_preferred_edit_position();
2007 if (Keyboard::modifier_state_equals (event
->state
, Keyboard::ModifierMask (Keyboard::PrimaryModifier
|Keyboard::SecondaryModifier
))) {
2009 align_region (rv
.region(), SyncPoint
, (nframes64_t
) (where
* speed
));
2011 } else if (Keyboard::modifier_state_equals (event
->state
, Keyboard::ModifierMask (Keyboard::PrimaryModifier
|Keyboard::TertiaryModifier
))) {
2013 align_region (rv
.region(), End
, (nframes64_t
) (where
* speed
));
2017 align_region (rv
.region(), Start
, (nframes64_t
) (where
* speed
));
2024 Editor::show_verbose_time_cursor (nframes64_t frame
, double offset
, double xpos
, double ypos
)
2027 Timecode::Time timecode
;
2030 nframes64_t frame_rate
;
2033 if (_session
== 0) {
2039 if (Profile
->get_sae() || Profile
->get_small_screen()) {
2040 m
= ARDOUR_UI::instance()->primary_clock
.mode();
2042 m
= ARDOUR_UI::instance()->secondary_clock
.mode();
2046 case AudioClock::BBT
:
2047 _session
->bbt_time (frame
, bbt
);
2048 snprintf (buf
, sizeof (buf
), "%02" PRIu32
"|%02" PRIu32
"|%02" PRIu32
, bbt
.bars
, bbt
.beats
, bbt
.ticks
);
2051 case AudioClock::Timecode
:
2052 _session
->timecode_time (frame
, timecode
);
2053 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%02" PRId32
":%02" PRId32
, timecode
.hours
, timecode
.minutes
, timecode
.seconds
, timecode
.frames
);
2056 case AudioClock::MinSec
:
2057 /* XXX this is copied from show_verbose_duration_cursor() */
2058 frame_rate
= _session
->frame_rate();
2059 hours
= frame
/ (frame_rate
* 3600);
2060 frame
= frame
% (frame_rate
* 3600);
2061 mins
= frame
/ (frame_rate
* 60);
2062 frame
= frame
% (frame_rate
* 60);
2063 secs
= (float) frame
/ (float) frame_rate
;
2064 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%07.4f", hours
, mins
, secs
);
2068 snprintf (buf
, sizeof(buf
), "%" PRIi64
, frame
);
2072 if (xpos
>= 0 && ypos
>=0) {
2073 set_verbose_canvas_cursor (buf
, xpos
+ offset
, ypos
+ offset
);
2075 set_verbose_canvas_cursor (buf
, _drags
->current_pointer_x() + offset
- horizontal_position(), _drags
->current_pointer_y() + offset
- vertical_adjustment
.get_value() + canvas_timebars_vsize
);
2077 show_verbose_canvas_cursor ();
2081 Editor::show_verbose_duration_cursor (nframes64_t start
, nframes64_t end
, double offset
, double xpos
, double ypos
)
2084 Timecode::Time timecode
;
2088 nframes64_t distance
, frame_rate
;
2090 Meter
meter_at_start(_session
->tempo_map().meter_at(start
));
2092 if (_session
== 0) {
2098 if (Profile
->get_sae() || Profile
->get_small_screen()) {
2099 m
= ARDOUR_UI::instance()->primary_clock
.mode ();
2101 m
= ARDOUR_UI::instance()->secondary_clock
.mode ();
2105 case AudioClock::BBT
:
2106 _session
->bbt_time (start
, sbbt
);
2107 _session
->bbt_time (end
, ebbt
);
2110 /* XXX this computation won't work well if the
2111 user makes a selection that spans any meter changes.
2114 ebbt
.bars
-= sbbt
.bars
;
2115 if (ebbt
.beats
>= sbbt
.beats
) {
2116 ebbt
.beats
-= sbbt
.beats
;
2119 ebbt
.beats
= int(meter_at_start
.beats_per_bar()) + ebbt
.beats
- sbbt
.beats
;
2121 if (ebbt
.ticks
>= sbbt
.ticks
) {
2122 ebbt
.ticks
-= sbbt
.ticks
;
2125 ebbt
.ticks
= int(Meter::ticks_per_beat
) + ebbt
.ticks
- sbbt
.ticks
;
2128 snprintf (buf
, sizeof (buf
), "%02" PRIu32
"|%02" PRIu32
"|%02" PRIu32
, ebbt
.bars
, ebbt
.beats
, ebbt
.ticks
);
2131 case AudioClock::Timecode
:
2132 _session
->timecode_duration (end
- start
, timecode
);
2133 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%02" PRId32
":%02" PRId32
, timecode
.hours
, timecode
.minutes
, timecode
.seconds
, timecode
.frames
);
2136 case AudioClock::MinSec
:
2137 /* XXX this stuff should be elsewhere.. */
2138 distance
= end
- start
;
2139 frame_rate
= _session
->frame_rate();
2140 hours
= distance
/ (frame_rate
* 3600);
2141 distance
= distance
% (frame_rate
* 3600);
2142 mins
= distance
/ (frame_rate
* 60);
2143 distance
= distance
% (frame_rate
* 60);
2144 secs
= (float) distance
/ (float) frame_rate
;
2145 snprintf (buf
, sizeof (buf
), "%02" PRId32
":%02" PRId32
":%07.4f", hours
, mins
, secs
);
2149 snprintf (buf
, sizeof(buf
), "%" PRIi64
, end
- start
);
2153 if (xpos
>= 0 && ypos
>=0) {
2154 set_verbose_canvas_cursor (buf
, xpos
+ offset
, ypos
+ offset
);
2157 set_verbose_canvas_cursor (buf
, _drags
->current_pointer_x() + offset
, _drags
->current_pointer_y() + offset
);
2160 show_verbose_canvas_cursor ();
2164 Editor::collect_new_region_view (RegionView
* rv
)
2166 latest_regionviews
.push_back (rv
);
2170 Editor::collect_and_select_new_region_view (RegionView
* rv
)
2173 latest_regionviews
.push_back (rv
);
2177 Editor::cancel_selection ()
2179 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
2180 (*i
)->hide_selection ();
2183 selection
->clear ();
2184 clicked_selection
= 0;
2189 Editor::single_contents_trim (RegionView
& rv
, nframes64_t frame_delta
, bool left_direction
, bool swap_direction
)
2191 boost::shared_ptr
<Region
> region (rv
.region());
2193 if (region
->locked()) {
2197 nframes64_t new_bound
;
2200 TimeAxisView
* tvp
= clicked_axisview
;
2201 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
2203 if (tv
&& tv
->is_track()) {
2204 speed
= tv
->track()->speed();
2207 if (left_direction
) {
2208 if (swap_direction
) {
2209 new_bound
= (nframes64_t
) (region
->position()/speed
) + frame_delta
;
2211 new_bound
= (nframes64_t
) (region
->position()/speed
) - frame_delta
;
2214 if (swap_direction
) {
2215 new_bound
= (nframes64_t
) (region
->position()/speed
) - frame_delta
;
2217 new_bound
= (nframes64_t
) (region
->position()/speed
) + frame_delta
;
2221 region
->trim_start ((nframes64_t
) (new_bound
* speed
), this);
2222 rv
.region_changed (PropertyChange (ARDOUR::Properties::start
));
2226 Editor::single_start_trim (RegionView
& rv
, nframes64_t new_bound
, bool no_overlap
)
2228 boost::shared_ptr
<Region
> region (rv
.region());
2230 if (region
->locked()) {
2235 TimeAxisView
* tvp
= clicked_axisview
;
2236 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
2238 if (tv
&& tv
->is_track()) {
2239 speed
= tv
->track()->speed();
2242 nframes64_t pre_trim_first_frame
= region
->first_frame();
2244 region
->trim_front ((nframes64_t
) (new_bound
* speed
), this);
2247 //Get the next region on the left of this region and shrink/expand it.
2248 boost::shared_ptr
<Playlist
> playlist (region
->playlist());
2249 boost::shared_ptr
<Region
> region_left
= playlist
->find_next_region (pre_trim_first_frame
, End
, 0);
2251 bool regions_touching
= false;
2253 if (region_left
!= 0 && (pre_trim_first_frame
== region_left
->last_frame() + 1)){
2254 regions_touching
= true;
2257 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2258 if (region_left
!= 0 &&
2259 (region_left
->last_frame() > region
->first_frame() || regions_touching
))
2261 region_left
->trim_end(region
->first_frame() - 1, this);
2265 rv
.region_changed (ARDOUR::bounds_change
);
2269 Editor::single_end_trim (RegionView
& rv
, nframes64_t new_bound
, bool no_overlap
)
2271 boost::shared_ptr
<Region
> region (rv
.region());
2273 if (region
->locked()) {
2278 TimeAxisView
* tvp
= clicked_axisview
;
2279 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
2281 if (tv
&& tv
->is_track()) {
2282 speed
= tv
->track()->speed();
2285 nframes64_t pre_trim_last_frame
= region
->last_frame();
2287 region
->trim_end ((nframes64_t
) (new_bound
* speed
), this);
2290 //Get the next region on the right of this region and shrink/expand it.
2291 boost::shared_ptr
<Playlist
> playlist (region
->playlist());
2292 boost::shared_ptr
<Region
> region_right
= playlist
->find_next_region (pre_trim_last_frame
, Start
, 1);
2294 bool regions_touching
= false;
2296 if (region_right
!= 0 && (pre_trim_last_frame
== region_right
->first_frame() - 1)) {
2297 regions_touching
= true;
2300 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2301 if (region_right
!= 0 &&
2302 (region_right
->first_frame() < region
->last_frame() || regions_touching
))
2304 region_right
->trim_front(region
->last_frame() + 1, this);
2307 rv
.region_changed (ARDOUR::bounds_change
);
2310 rv
.region_changed (PropertyChange (ARDOUR::Properties::length
));
2316 Editor::point_trim (GdkEvent
* event
, nframes64_t new_bound
)
2318 RegionView
* rv
= clicked_regionview
;
2320 /* Choose action dependant on which button was pressed */
2321 switch (event
->button
.button
) {
2323 begin_reversible_command (_("Start point trim"));
2325 if (selection
->selected (rv
)) {
2326 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin();
2327 i
!= selection
->regions
.by_layer().end(); ++i
)
2330 cerr
<< "region view contains null region" << endl
;
2333 if (!(*i
)->region()->locked()) {
2334 (*i
)->region()->clear_history ();
2335 (*i
)->region()->trim_front (new_bound
, this);
2336 _session
->add_command(new StatefulDiffCommand ((*i
)->region()));
2341 if (!rv
->region()->locked()) {
2342 rv
->region()->clear_history ();
2343 rv
->region()->trim_front (new_bound
, this);
2344 _session
->add_command(new StatefulDiffCommand (rv
->region()));
2348 commit_reversible_command();
2352 begin_reversible_command (_("End point trim"));
2354 if (selection
->selected (rv
)) {
2356 for (list
<RegionView
*>::const_iterator i
= selection
->regions
.by_layer().begin(); i
!= selection
->regions
.by_layer().end(); ++i
)
2358 if (!(*i
)->region()->locked()) {
2359 (*i
)->region()->clear_history();
2360 (*i
)->region()->trim_end (new_bound
, this);
2361 _session
->add_command(new StatefulDiffCommand ((*i
)->region()));
2367 if (!rv
->region()->locked()) {
2368 rv
->region()->clear_history ();
2369 rv
->region()->trim_end (new_bound
, this);
2370 _session
->add_command (new StatefulDiffCommand (rv
->region()));
2374 commit_reversible_command();
2383 Editor::thaw_region_after_trim (RegionView
& rv
)
2385 boost::shared_ptr
<Region
> region (rv
.region());
2387 if (region
->locked()) {
2391 region
->resume_property_changes ();
2393 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*>(&rv
);
2396 arv
->unhide_envelope ();
2401 Editor::hide_marker (ArdourCanvas::Item
* item
, GdkEvent
* /*event*/)
2406 if ((marker
= static_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
2407 fatal
<< _("programming error: marker canvas item has no marker object pointer!") << endmsg
;
2411 Location
* location
= find_location_from_marker (marker
, is_start
);
2412 location
->set_hidden (true, this);
2417 Editor::reposition_zoom_rect (nframes64_t start
, nframes64_t end
)
2419 double x1
= frame_to_pixel (start
);
2420 double x2
= frame_to_pixel (end
);
2421 double y2
= full_canvas_height
- 1.0;
2423 zoom_rect
->property_x1() = x1
;
2424 zoom_rect
->property_y1() = 1.0;
2425 zoom_rect
->property_x2() = x2
;
2426 zoom_rect
->property_y2() = y2
;
2431 Editor::mouse_rename_region (ArdourCanvas::Item
* /*item*/, GdkEvent
* /*event*/)
2433 using namespace Gtkmm2ext
;
2435 ArdourPrompter
prompter (false);
2437 prompter
.set_prompt (_("Name for region:"));
2438 prompter
.set_initial_text (clicked_regionview
->region()->name());
2439 prompter
.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT
);
2440 prompter
.set_response_sensitive (Gtk::RESPONSE_ACCEPT
, false);
2441 prompter
.show_all ();
2442 switch (prompter
.run ()) {
2443 case Gtk::RESPONSE_ACCEPT
:
2445 prompter
.get_result(str
);
2447 clicked_regionview
->region()->set_name (str
);
2456 Editor::mouse_brush_insert_region (RegionView
* rv
, nframes64_t pos
)
2458 /* no brushing without a useful snap setting */
2460 switch (_snap_mode
) {
2462 return; /* can't work because it allows region to be placed anywhere */
2467 switch (_snap_type
) {
2475 /* don't brush a copy over the original */
2477 if (pos
== rv
->region()->position()) {
2481 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*>(&rv
->get_time_axis_view());
2483 if (rtv
== 0 || !rtv
->is_track()) {
2487 boost::shared_ptr
<Playlist
> playlist
= rtv
->playlist();
2488 double speed
= rtv
->track()->speed();
2490 playlist
->clear_history ();
2491 boost::shared_ptr
<Region
> new_region (RegionFactory::create (rv
->region()));
2492 playlist
->add_region (new_region
, (nframes64_t
) (pos
* speed
));
2493 _session
->add_command (new StatefulDiffCommand (playlist
));
2495 // playlist is frozen, so we have to update manually XXX this is disgusting
2497 playlist
->RegionAdded (new_region
); /* EMIT SIGNAL */
2501 Editor::track_height_step_timeout ()
2503 if (get_microseconds() - last_track_height_step_timestamp
< 250000) {
2504 current_stepping_trackview
= 0;
2511 Editor::add_region_drag (ArdourCanvas::Item
* item
, GdkEvent
* event
, RegionView
* region_view
)
2513 assert (region_view
);
2515 _region_motion_group
->raise_to_top ();
2517 if (Config
->get_edit_mode() == Splice
) {
2518 _drags
->add (new RegionSpliceDrag (this, item
, region_view
, selection
->regions
.by_layer()));
2520 RegionSelection s
= get_equivalent_regions (selection
->regions
, ARDOUR::Properties::edit
.property_id
);
2521 _drags
->add (new RegionMoveDrag (this, item
, region_view
, s
.by_layer(), false, false));
2524 /* sync the canvas to what we think is its current state */
2525 update_canvas_now();
2529 Editor::add_region_copy_drag (ArdourCanvas::Item
* item
, GdkEvent
* event
, RegionView
* region_view
)
2531 assert (region_view
);
2533 _region_motion_group
->raise_to_top ();
2535 RegionSelection s
= get_equivalent_regions (selection
->regions
, ARDOUR::Properties::edit
.property_id
);
2536 _drags
->add (new RegionMoveDrag (this, item
, region_view
, s
.by_layer(), false, true));
2540 Editor::add_region_brush_drag (ArdourCanvas::Item
* item
, GdkEvent
* event
, RegionView
* region_view
)
2542 assert (region_view
);
2544 if (Config
->get_edit_mode() == Splice
) {
2548 RegionSelection s
= get_equivalent_regions (selection
->regions
, ARDOUR::Properties::edit
.property_id
);
2549 _drags
->add (new RegionMoveDrag (this, item
, region_view
, s
.by_layer(), true, false));
2551 begin_reversible_command (_("Drag region brush"));
2554 /** Start a grab where a time range is selected, track(s) are selected, and the
2555 * user clicks and drags a region with a modifier in order to create a new region containing
2556 * the section of the clicked region that lies within the time range.
2559 Editor::start_selection_grab (ArdourCanvas::Item
* /*item*/, GdkEvent
* event
)
2561 if (clicked_regionview
== 0) {
2565 /* lets try to create new Region for the selection */
2567 vector
<boost::shared_ptr
<Region
> > new_regions
;
2568 create_region_from_selection (new_regions
);
2570 if (new_regions
.empty()) {
2574 /* XXX fix me one day to use all new regions */
2576 boost::shared_ptr
<Region
> region (new_regions
.front());
2578 /* add it to the current stream/playlist.
2580 tricky: the streamview for the track will add a new regionview. we will
2581 catch the signal it sends when it creates the regionview to
2582 set the regionview we want to then drag.
2585 latest_regionviews
.clear();
2586 sigc::connection c
= clicked_routeview
->view()->RegionViewAdded
.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view
));
2588 /* A selection grab currently creates two undo/redo operations, one for
2589 creating the new region and another for moving it.
2592 begin_reversible_command (_("selection grab"));
2594 boost::shared_ptr
<Playlist
> playlist
= clicked_axisview
->playlist();
2596 playlist
->clear_history ();
2597 clicked_routeview
->playlist()->add_region (region
, selection
->time
[clicked_selection
].start
);
2598 _session
->add_command(new StatefulDiffCommand (playlist
));
2600 commit_reversible_command ();
2604 if (latest_regionviews
.empty()) {
2605 /* something went wrong */
2609 /* we need to deselect all other regionviews, and select this one
2610 i'm ignoring undo stuff, because the region creation will take care of it
2612 selection
->set (latest_regionviews
);
2614 _drags
->set (new RegionMoveDrag (this, latest_regionviews
.front()->get_canvas_group(), latest_regionviews
.front(), latest_regionviews
, false, false), event
);
2620 if (_drags
->active ()) {
2623 selection
->clear ();
2628 Editor::set_internal_edit (bool yn
)
2630 _internal_editing
= yn
;
2633 mouse_select_button
.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2634 mouse_select_button
.get_image ()->show ();
2635 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button
, _("Draw/Edit MIDI Notes"));
2637 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
2638 MidiTimeAxisView
* mtv
= dynamic_cast<MidiTimeAxisView
*> (*i
);
2640 mtv
->start_step_editing ();
2644 for (TrackSelection::iterator i
= selection
->tracks
.begin(); i
!= selection
->tracks
.end(); ++i
) {
2645 (*i
)->hide_selection ();
2648 start_step_editing ();
2649 set_canvas_cursor ();
2653 mouse_select_button
.set_image (*(manage (new Image (::get_icon("tool_range")))));
2654 mouse_select_button
.get_image ()->show ();
2655 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button
, _("Select/Move Ranges"));
2656 stop_step_editing ();
2658 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
2659 MidiTimeAxisView
* mtv
= dynamic_cast<MidiTimeAxisView
*> (*i
);
2661 mtv
->stop_step_editing ();
2665 mouse_mode_toggled (mouse_mode
);
2669 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2670 * used by the `join object/range' tool mode.
2673 Editor::update_join_object_range_location (double x
, double y
)
2675 /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2676 entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2677 that we're over requires searching the playlist.
2680 if (join_object_range_button
.get_active() == false || (mouse_mode
!= MouseRange
&& mouse_mode
!= MouseObject
)) {
2681 _join_object_range_state
= JOIN_OBJECT_RANGE_NONE
;
2685 if (mouse_mode
== MouseObject
) {
2686 _join_object_range_state
= JOIN_OBJECT_RANGE_OBJECT
;
2687 } else if (mouse_mode
== MouseRange
) {
2688 _join_object_range_state
= JOIN_OBJECT_RANGE_RANGE
;
2691 /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2692 pair
<TimeAxisView
*, int> tvp
= trackview_by_y_position (y
+ vertical_adjustment
.get_value() - canvas_timebars_vsize
);
2696 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (tvp
.first
);
2701 rtv
->canvas_display()->w2i (cx
, cy
);
2703 bool const top_half
= cy
< rtv
->current_height () / 2;
2705 _join_object_range_state
= top_half
? JOIN_OBJECT_RANGE_RANGE
: JOIN_OBJECT_RANGE_OBJECT
;
2711 Editor::effective_mouse_mode () const
2713 if (_join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
) {
2715 } else if (_join_object_range_state
== JOIN_OBJECT_RANGE_RANGE
) {
2723 Editor::remove_midi_note (ArdourCanvas::Item
* item
, GdkEvent
*)
2725 ArdourCanvas::CanvasNoteEvent
* e
= dynamic_cast<ArdourCanvas::CanvasNoteEvent
*> (item
);
2728 e
->region_view().delete_note (e
->note ());