33#include <wx/eventfilter.h>
48 wxEvtHandler::AddFilter(
this );
53 wxEvtHandler::RemoveFilter(
this );
63 const auto type =
event.GetEventType();
64 if (type == wxEVT_KEY_DOWN &&
65 static_cast< wxKeyEvent&
>( event ).GetKeyCode() == WXK_ESCAPE ) {
66 bool eatEvent =
false;
73 pPanel->HandleEscapeKey(
true );
77 return Event_Processed;
79 else if ((type == wxEVT_LEFT_DOWN ||
80 type == wxEVT_RIGHT_DOWN ||
81 type == wxEVT_MIDDLE_DOWN)) {
131 wxWindow * parent, wxWindowID
id,
132 const wxPoint & pos, const wxSize &
size,
136, mViewInfo( viewInfo )
137,
mState{ std::make_unique<State>() }
150 if (state.mUIHandle &&
151 state.mUIHandle->StopsOnKeystroke() ) {
155 const int idBogusUp = 2;
156 wxMouseEvent evt { wxEVT_LEFT_UP };
157 evt.SetId( idBogusUp );
158 evt.SetPosition(this->ScreenToClient(::wxGetMousePosition()));
159 this->ProcessEvent(evt);
165 auto state = ::wxGetMouseState();
168 state.SetPosition(this->ScreenToClient(state.GetPosition()));
176 if ( escaping || !AcceptsFocus() )
183 if (state.mUIHandle) {
185 auto handle = state.mUIHandle;
188 auto pClickedCell = state.mpClickedCell.lock();
191 pClickedCell.get(), {},
192 refreshResult | state.mMouseOverUpdateFlags );
193 state.mpClickedCell.reset();
194 state.mUIHandle.reset(), handle.reset(),
ClearTargets();
209 if (target && target->HasEscape(pProject) && target->Escape(pProject)) {
216 if (state.mUIHandle) {
235 if (!state.ButtonIsDown(wxMOUSE_BTN_ANY)) {
237 if (state.RawControlDown())
262 auto state = ::wxGetMouseState();
264 state.SetPosition(this->ScreenToClient(state.GetPosition()));
280 const auto foundCell =
FindCell( inState.m_x, inState.m_y );
281 auto &rect = foundCell.rect;
282 auto &pCell = foundCell.pCell;
291 auto handle = state.mUIHandle;
293 auto newCell = tpmState.
pCell;
294 auto oldCell = state.mLastCell.lock();
295 auto oldHandle =
Target();
299 unsigned refreshCode = 0;
305 refreshCode = state.mMouseOverUpdateFlags;
306 state.mMouseOverUpdateFlags = 0;
308 else if ( !state.mUIHandle ) {
311 unsigned updateFlags = state.mMouseOverUpdateFlags;
314 if ( newCell == oldCell )
322 oldCell.get(), oldCell.get(), updateFlags);
326 auto oldPosition = state.mTarget;
330 state.mTargets.clear();
332 state.mTargets = newCell->HitTest(tpmState,
GetProject());
337 auto begin = state.mTargets.begin(),
end = state.mTargets.end(),
338 iter = std::find(
begin,
end, oldHandle);
340 size_t newPosition = iter -
begin;
341 if (newPosition <= oldPosition)
342 state.mTarget = newPosition;
349 state.mLastCell = newCell;
358 if (!oldCell && oldHandle != handle)
360 refreshCode = updateFlags;
363 if (handle && handle != oldHandle)
366 if (oldHandle == handle)
373 auto preview = handle->Preview( tpmState,
GetProject() );
374 status = preview.message;
375 tooltip = preview.tooltip;
376 pCursor = preview.cursor;
377 auto code = handle->GetChangeHighlight();
380 state.mMouseOverUpdateFlags |= code;
383 (!pCursor || status.
empty() || tooltip.
empty())) {
386 const auto preview = newCell->DefaultPreview( tpmState,
GetProject() );
388 pCursor = preview.cursor;
390 status = preview.message;
392 tooltip = preview.tooltip;
396 static wxCursor defaultCursor{ wxCURSOR_DEFAULT };
397 pCursor = &defaultCursor;
402 if (handle || (newCell && !wxWindow::GetCapture())) {
409 if (handle != oldHandle)
415 SetCursor( *pCursor );
417 else if ( oldCell || oldHandle )
428 auto state = ::wxGetMouseState();
430 std::shared_ptr<TrackPanelCell> pCell;
439 if ( state.mTargets.size() > 1 )
442 return target && target->HasRotation();
451 if (state.mTarget + 1 == state.mTargets.size() &&
456 return state.mTargets.size() > 0;
462 auto size = state.mTargets.size();
465 if (target && target->HasRotation()) {
477 (!
forward && state.mTarget == 0)))
484 state.mTarget +=
size - 1;
485 state.mTarget %=
size;
498 return state.mUIHandle != NULL;
509 auto pCell = tpmEvent.
pCell;
513 auto &
event = tpmEvent.
event;
515#if defined(__WXMAC__) && defined(EVT_MAGNIFY)
519 if (event.Magnify()) {
520 event.SetControlDown(
true);
521 steps = 2 *
event.GetMagnification();
526 steps =
event.m_wheelRotation /
527 (
event.m_wheelDelta > 0 ? (double)event.m_wheelDelta : 120.0);
530 if(event.GetWheelAxis() == wxMOUSE_WHEEL_HORIZONTAL) {
532 event.SetShiftDown(
true);
538 tpmEvent.
steps = steps;
540 if(!event.HasAnyModifiers()) {
544 event.ResumePropagation(wxEVENT_PROPAGATE_MAX);
548 pCell->HandleWheelRotation( tpmEvent,
GetProject() );
550 pCell.get(), pCell.get(), result);
556 state.mEnableTab =
false;
557 wxKeyEvent *kevent =
static_cast<wxKeyEvent *
>(
event.GetEventObject());
558 const auto code = kevent->GetKeyCode();
559 if ( WXK_ESCAPE != code )
565 const unsigned refreshResult =
568 event.Skip(kevent->GetSkipped());
573 if ( !(t && !kevent->GetSkipped()) &&
588 switch (event.GetKeyCode())
603 case WXK_RAW_CONTROL:
622 const unsigned refreshResult =
632 switch (event.GetKeyCode())
645 const unsigned refreshResult =
655 bool didSomething =
false;
656 switch (event.GetKeyCode())
666 case WXK_RAW_CONTROL:
677 const unsigned refreshResult =
690 state.mUIHandle.reset();
694 wxMouseEvent e(wxEVT_LEFT_UP);
697 e.m_x = state.mMouseMostRecentX;
698 e.m_y = state.mMouseMostRecentY;
709 const auto foundCell =
FindCell( event.m_x, event.m_y );
710 auto &rect = foundCell.rect;
711 auto &pCell = foundCell.pCell;
713 const auto size = GetSize();
716#if defined(__WXMAC__) && defined(EVT_MAGNIFY)
720 if (event.Magnify()) {
730 if (event.GetPosition() == wxDefaultPosition && (event.RightDown() || event.RightUp())) {
735 if (event.m_wheelRotation != 0)
738 if (event.LeftDown() || event.LeftIsDown() || event.Moving()) {
742 event.ResumePropagation(wxEVENT_PROPAGATE_MAX);
746 state.mMouseMostRecentX =
event.m_x;
747 state.mMouseMostRecentY =
event.m_y;
749 if (event.LeftDown()) {
753 GetParent()->GetEventHandler()->ProcessEvent(e);
756 if (event.Entering())
760 else if (event.Leaving())
770 ::wxGetMouseState().ButtonIsDown(wxMOUSE_BTN_ANY);
775#if defined(__WXMAC__)
780 wxSTANDARD_CURSOR->MacInstall();
785 if (state.mUIHandle) {
786 auto pClickedCell = state.mpClickedCell.lock();
787 if (event.Dragging()) {
790 auto handle = state.mUIHandle;
794 (pClickedCell.get(), pCell.get(), refreshResult);
795 state.mMouseOverUpdateFlags |= refreshResult;
798 state.mUIHandle.reset(), handle.reset(),
ClearTargets();
799 state.mpClickedCell.reset();
808 else if (event.ButtonUp()) {
811 auto handle = state.mUIHandle;
812 unsigned moreFlags = state.mMouseOverUpdateFlags;
814 handle->Release( tpmEvent,
GetProject(),
this );
816 (pClickedCell.get(), pCell.get(),
817 refreshResult | moreFlags);
818 state.mUIHandle.reset(), handle.reset(),
ClearTargets();
819 state.mpClickedCell.reset();
823 else if ( event.GetEventType() == wxEVT_MOTION )
829 else if ( event.ButtonDown() || event.ButtonDClick() )
837 if (event.ButtonUp())
843 if ( CancelDragging(
true ) )
857 const std::shared_ptr<TrackPanelCell> &pCell )
886 wxWindow *pParent)
override
888 if (
auto pCell = mwCell.lock()) {
889 auto point =
event.event.GetPosition();
890 return pCell->DoContextMenu(event.
rect, pParent, &point, pProject);
904DefaultRightButtonHandler::~DefaultRightButtonHandler()
912 auto pCell = tpmEvent.
pCell;
925 state.mUIHandle =
Target();
926 if (tpmEvent.
event.RightDown() &&
927 !(state.mUIHandle && state.mUIHandle->HandlesRightClick())) {
928 if (
auto pCell = state.mLastCell.lock())
929 state.mUIHandle = std::make_shared<DefaultRightButtonHandler>(pCell);
932 if (state.mUIHandle) {
936 auto handle = state.mUIHandle;
940 state.mUIHandle.reset(), handle.reset(),
ClearTargets();
949 if( !HasFocus() && AcceptsFocus() )
950 SetFocusIgnoringChildren();
952 state.mpClickedCell = pCell;
964 pCell.get(), pCell.get(), refreshResult);
965 state.mMouseOverUpdateFlags |= refreshResult;
977 std::weak_ptr<TrackPanelCell> wCell = pCell;
979 const auto delegate = pCell->ContextMenuDelegate();
985 delegate->DoContextMenu(rect,
this,
nullptr,
GetProject());
1000 auto refreshResult = pCell->LoseFocus(
GetProject());
1002 auto pClickedCell = state.mpClickedCell.lock();
1030 Visit( GetClientRect(),
Root(), visitor );
1049 : function{ function_ }, pre{ pre_ }, post{ ! pre_ } {}
1052 {
return function( rect, cell ); }
1054 {
if (pre)
return function( rect, group ); }
1056 {
if (post)
return function( rect, group ); }
1059 const bool pre{
false }, post{
false };
1067 Adaptor adaptor{ visitor };
1073 Adaptor adaptor{ visitor,
true };
1079 Adaptor adaptor{ visitor,
false };
1085 const wxRect &rect,
bool divideX,
1087 const TrackPanelGroup::Refinement::const_iterator iter)
1089 const auto next = iter + 1;
1090 const auto end = children.end();
1093 nextCoord = std::max( iter->first,
1094 divideX ? rect.GetRight() : rect.GetBottom() );
1096 nextCoord = next->first - 1;
1098 auto lesser = iter->first;
1099 auto greater = nextCoord;
1103 result.SetLeft(lesser), result.SetRight(greater);
1105 result.SetTop(lesser), result.SetBottom(greater);
1113 const wxRect &rect,
const std::shared_ptr<TrackPanelNode> &node,
1122 const auto results = pGroup->Children( rect );
1124 const auto &children = results.second;
1125 const auto begin = children.begin(),
end = children.end();
1126 for (
auto iter =
begin; iter !=
end; ++iter)
1128 Subdivide(rect, divideX, children, iter), iter->second, visitor );
1138 auto rect = this->GetClientRect();
1141 if (
auto pCell = std::dynamic_pointer_cast< TrackPanelCell >( node ) )
1143 return { pCell, rect };
1144 else if (
auto pGroup =
dynamic_cast< TrackPanelGroup*
>( node.get() ) ) {
1146 const auto results = pGroup->Children( rect );
1148 const auto &children = results.second;
1151 const auto begin = children.begin(),
end = children.end();
1152 auto iter = std::upper_bound(
begin,
end,
1153 (divideX ? mouseX : mouseY),
1155 return coord < child.first;
1163 rect =
Subdivide(rect, divideX, children, iter);
1164 node = iter->second;
1181 if ( &visited == &cell )
1182 result = rect,
throw Stop{};
1184 catch (
const Stop& ) {}
1196 if ( pred( visited ) )
1197 result = rect,
throw Stop{};
1199 catch (
const Stop& ) {}
1207 if (state.mTargets.size())
1208 return state.mTargets[state.mTarget];
1216 return state.mMouseMostRecentX;
1224 state.mLastCell.reset();
1225 state.mTargets.clear();
1227 state.mMouseOverUpdateFlags = 0;
1233 return state.mLastCell.lock();
1238 const auto panelRect = GetClientRect();
1240 for (
unsigned iPass = 0; iPass < nPasses; ++iPass ) {
1246 context, rect, panelRect, iPass );
1247 if ( newRect.Intersects( panelRect ) )
1248 node.
Draw( context, newRect, iPass );
1251 if ( &node == lastCell.get() ) {
1252 auto target = Target();
1254 const auto targetRect =
1255 target->DrawingArea( context, rect, panelRect, iPass );
1256 if ( targetRect.Intersects( panelRect ) )
1257 target->Draw( context, targetRect, iPass );
std::shared_ptr< UIHandle > UIHandlePtr
EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, LabelDialog::OnFreqUpdate) LabelDialog
const int kCaptureLostEventId
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Formerly part of TrackPanel, this abstract base class has no special knowledge of Track objects and i...
void Draw(TrackPanelDrawingContext &context, unsigned nPasses)
bool IsMouseCaptured()
Determines if a modal tool is active.
void UpdateMouseState(const wxMouseState &state)
virtual std::shared_ptr< TrackPanelNode > Root()=0
virtual AudacityProject * GetProject() const =0
std::unique_ptr< State > mState
void VisitCells(const SimpleCellVisitor &visitor)
virtual std::shared_ptr< TrackPanelCell > GetFocusedCell()=0
void OnKeyDown(wxKeyEvent &event)
void VisitPreorder(const SimpleNodeVisitor &visitor)
FoundCell FindCell(int mouseX, int mouseY)
void HandleWheelRotation(TrackPanelMouseEvent &tpmEvent)
Handle mouse wheel rotation (for zoom in/out, vertical and horizontal scrolling)
wxRect FindRect(const TrackPanelCell &cell)
~CellularPanel() override
void OnCaptureKey(wxCommandEvent &event)
bool HandleEscapeKey(bool down)
bool CancelDragging(bool escaping)
void OnMouseEvent(wxMouseEvent &event)
void OnKeyUp(wxKeyEvent &event)
void OnChar(wxKeyEvent &event)
std::shared_ptr< TrackPanelCell > LastCell() const
void VisitPostorder(const SimpleNodeVisitor &visitor)
void HandleMotion(wxMouseState &state, bool doHit=true)
virtual void UpdateStatusMessage(const TranslatableString &)=0
void HandleClick(const TrackPanelMouseEvent &tpmEvent)
void OnSetFocus(wxFocusEvent &event)
void OnCaptureLost(wxMouseCaptureLostEvent &event)
Should handle the case when the mouse capture is lost. (MSW only)
std::function< void(const wxRect &rect, TrackPanelCell &cell) > SimpleCellVisitor
virtual void SetFocusedCell()=0
void HandleInterruptedDrag()
void OnKillFocus(wxFocusEvent &event)
wxMouseState mLastMouseState
void OnContextMenu(wxContextMenuEvent &event)
std::function< void(const wxRect &rect, TrackPanelNode &node) > SimpleNodeVisitor
void DoContextMenu(std::shared_ptr< TrackPanelCell > pCell)
bool ChangeTarget(bool forward, bool cycle)
void Uncapture(bool escaping, wxMouseState *pState=nullptr)
void Visit(Visitor &visitor)
wxCoord MostRecentXCoord() const
virtual void ProcessUIHandleResult(TrackPanelCell *pClickedCell, TrackPanelCell *pLatestCell, unsigned refreshResult)=0
void HandleCursorForPresentMouseState(bool doHit=true)
virtual void Draw(TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass)
virtual wxRect DrawingArea(TrackPanelDrawingContext &context, const wxRect &rect, const wxRect &panelRect, unsigned iPass)
std::vector< Child > Refinement
std::pair< wxCoord, std::shared_ptr< TrackPanelNode > > Child
The TrackPanel is built up of nodes, subtrees of the CellularPanel's area Common base class for Track...
Holds a msgid for the translation catalog; may also bind format arguments.
wxString Translation() const
Short-lived drawing and event-handling object associated with a TrackPanelCell.
void SetToolTip(const TranslatableString &toolTip)
bool IsHandler(const wxWindow *handler)
void Release(wxWindow *handler)
wxRect Subdivide(const wxRect &rect, bool divideX, const TrackPanelGroup::Refinement &children, const TrackPanelGroup::Refinement::const_iterator iter)
const char * end(const char *str) noexcept
const char * begin(const char *str) noexcept
int FilterEvent(wxEvent &event) override
static wxWeakRef< CellularPanel > spClickedPanel
static wxWeakRef< CellularPanel > spEnteredPanel
unsigned mMouseOverUpdateFlags
std::weak_ptr< TrackPanelCell > mLastCell
std::vector< UIHandlePtr > mTargets
std::weak_ptr< TrackPanelCell > mpClickedCell
virtual void EndGroup(const wxRect &rect, TrackPanelGroup &group)
virtual void BeginGroup(const wxRect &rect, TrackPanelGroup &group)
virtual void VisitCell(const wxRect &rect, TrackPanelCell &cell)
std::shared_ptr< TrackPanelCell > pCell
std::shared_ptr< TrackPanelCell > pCell
void EndGroup(const wxRect &rect, TrackPanelGroup &group) override
void VisitCell(const wxRect &rect, TrackPanelCell &cell) override
CellularPanel::SimpleCellVisitor SimpleCellVisitor
Adaptor(const SimpleCellVisitor &function_)
CellularPanel::SimpleNodeVisitor SimpleNodeVisitor
SimpleNodeVisitor function
void BeginGroup(const wxRect &rect, TrackPanelGroup &group) override
Adaptor(const SimpleNodeVisitor &function_, bool pre_)