20#include "../KeyboardCapture.h"
32#include <wx/dcbuffer.h>
35#include <wx/stattext.h>
36#include <wx/tooltip.h>
37#include <wx/toplevel.h>
39#if wxUSE_ACCESSIBILITY
47 virtual ~ NumericTextCtrlAx();
53 wxAccStatus DoDefaultAction(
int childId)
override;
57 wxAccStatus GetChild(
int childId, wxAccessible **child)
override;
60 wxAccStatus GetChildCount(
int *childCount)
override;
68 wxAccStatus GetDefaultAction(
int childId, wxString *actionName)
override;
71 wxAccStatus GetDescription(
int childId, wxString *description)
override;
77 wxAccStatus GetFocus(
int *childId, wxAccessible **child)
override;
80 wxAccStatus GetHelpText(
int childId, wxString *helpText)
override;
84 wxAccStatus GetKeyboardShortcut(
int childId, wxString *shortcut)
override;
88 wxAccStatus GetLocation(wxRect & rect,
int elementId)
override;
91 wxAccStatus GetName(
int childId, wxString *
name)
override;
94 wxAccStatus GetRole(
int childId, wxAccRole *role)
override;
104 wxAccStatus GetSelections(wxVariant *selections)
override;
107 wxAccStatus GetState(
int childId,
long *state)
override;
111 wxAccStatus GetValue(
int childId, wxString *strValue)
override;
117 wxString mCachedName;
118 wxString mLastCtrlString;
147 wxWindow *parent, wxWindowID
id,
154 wxControl(parent,
id, pos,
size, wxWANTS_CHARS),
159 mAutoPos(options.autoPos)
162 mAllowInvalidValue =
false;
172 mReadOnly = options.readOnly;
173 mMenuEnabled = options.menuEnabled;
174 mButtonWidth = mMenuEnabled ? 16 : 0;
176 SetLayoutDirection(wxLayout_LeftToRight);
187 mScrollRemainder = 0.0;
189#if wxUSE_ACCESSIBILITY
192 SetAccessible(
safenew NumericTextCtrlAx(
this));
195 if (options.hasInvalidValue)
196 SetInvalidValue( options.invalidValue );
198 if (!options.formatSymbol.empty())
199 SetFormatName( options.formatSymbol );
201 if (!options.customFormat.empty())
202 SetCustomFormat( options.customFormat );
204 if (options.hasValue)
205 SetValue( options.value );
214 wxControl::SetName(
name.Translation() );
290 wxString tip(
_(
"(Use context menu to change format.)"));
294 wxToolTip *tt = GetToolTip();
295 if (tt && tt->GetTip() == tip)
333 wxFont pf(wxSize(boxW, boxH), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
334 int fontSize = pf.GetPointSize();
340 dc.GetTextExtent(
wxT(
"0"), &strW, &strH);
341 while (strW > boxW || strH > boxH) {
342 dc.SetFont(wxFont(--fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
343 dc.GetTextExtent(
wxT(
"0"), &strW, &strH);
349 mDigitFont = std::make_unique<wxFont>(fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
357 std::unique_ptr<wxFont> labelFont = std::make_unique<wxFont>(fontSize - 1, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
360 dc.SetFont(*labelFont);
371 dc.GetTextExtent(prefix, &strW, &strH);
378 int pos = prefix.length();
385 for (
int i = 0, fcnt = fields.size(); i < fcnt; ++i) {
387 dc.GetTextExtent(fields[i].
label, &strW, &strH);
393 for (
int j = 0, dcnt = fields[i].digits; j < dcnt; ++j) {
409 pos += fields[i].label.length();
414 for (
int i = 0, fcnt = fields.size(); i < fcnt; ++i) {
416 dc.GetTextExtent(fields[i].
label, &strW, &strH);
419 x += (boxW * fields[i].digits) + strW;
449 memDC.GetTextExtent(prefix, &strW, &strH);
462 memDC.SetBrush(Brush);
463 memDC.SetPen(*wxTRANSPARENT_PEN);
468 memDC.GetTextExtent(
wxT(
"0"), &strW, &strH);
469 int labelTop = numberBottom - strH;
476 memDC.SetBrush(Brush);
478 for(i = 0; i < digits.size(); i++)
479 memDC.DrawRectangle(
GetBox(i));
480 memDC.SetBrush( wxNullBrush );
482 for(i = 0; i < fields.size(); i++)
489 memDC.SetBrush(Brush);
500 wxSize sz = GetSize();
501 wxSize csz = GetClientSize();
504 sz.y =
mHeight + (sz.y - csz.y);
519 wxBufferedPaintDC dc(
this);
529 dc.SetBrush(*wxTRANSPARENT_BRUSH);
531 dc.SetPen( wxNullPen );
538 dc.SetPen(*wxTRANSPARENT_PEN);
540 dc.SetBrush( Brush );
543 auto digitsCount = int(digits.size());
545 for(i = 0; i < digits.size(); i++) {
548 dc.DrawRectangle(box);
552 int pos = digits[i].pos;
556 dc.DrawText(digit, x, y);
562 dc.SetPen( wxNullPen );
563 dc.SetBrush( wxNullBrush );
578 std::vector<NumericFormatID> symbols;
583 [&menu, &symbols,
this, i =
ID_MENU](
auto& item)
mutable
585 const auto ID = item.symbol.Internal();
586 symbols.push_back(ID);
587 menu.AppendRadioItem(i, item.symbol.Translation());
595 menu.Bind(wxEVT_MENU, [](
auto&){});
611 eventType = EVT_TIMETEXTCTRL_UPDATED;
613 eventType = EVT_FREQUENCYTEXTCTRL_UPDATED;
615 eventType = EVT_BANDWIDTHTEXTCTRL_UPDATED;
622 for (
const auto& symbol : symbols)
624 if (!menu.IsChecked(menuIndex++) ||
mFormatID == symbol)
629 wxCommandEvent e(eventType, GetId());
630 e.SetString(symbol.GET());
631 GetParent()->GetEventHandler()->AddPendingEvent(e);
639 if (event.LeftDown() && event.GetX() >=
mWidth) {
640 wxContextMenuEvent e;
643 else if (event.LeftDown()) {
650 for(i = 0; i <
mBoxes.size(); i++) {
651 int dist = abs(event.m_x - (
GetBox(i).x +
653 if (dist < bestDist) {
662 wxContextMenuEvent e;
665 else if(!
mReadOnly && event.m_wheelRotation != 0 ) {
666 double steps =
event.m_wheelRotation /
667 (
event.m_wheelDelta > 0 ? (double)event.m_wheelDelta : 120.0) +
670 steps = floor(steps);
683 if (event.GetEventType() != wxEVT_KILL_FOCUS &&
698 const auto boxesCount =
mBoxes.size();
706 const auto newBoxesCount =
mBoxes.size();
708 if (resetFocus || boxesCount > newBoxesCount)
726 wxKeyEvent *kevent = (wxKeyEvent *)event.GetEventObject();
727 int keyCode = kevent->GetKeyCode();
730 if ((keyCode >= WXK_NUMPAD0) && (keyCode <= WXK_NUMPAD9))
731 keyCode -= WXK_NUMPAD0 -
'0';
744 case WXK_NUMPAD_ENTER:
749 if (keyCode >=
'0' && keyCode <= '9' && !kevent->HasAnyModifiers())
760 int keyCode =
event.GetKeyCode();
764 if ((keyCode >= WXK_NUMPAD0) && (keyCode <= WXK_NUMPAD9))
765 keyCode -= WXK_NUMPAD0 -
'0';
767 if ((keyCode >=
'0' && keyCode <=
'9' && !event.HasAnyModifiers()) ||
768 (keyCode == WXK_DELETE) ||
769 (keyCode == WXK_BACK) ||
770 (keyCode == WXK_UP) ||
771 (keyCode == WXK_DOWN)) {
786 int keyCode =
event.GetKeyCode();
795 if ((keyCode >= WXK_NUMPAD0) && (keyCode <= WXK_NUMPAD9))
796 keyCode -= WXK_NUMPAD0 -
'0';
800 if (!
mReadOnly && (keyCode >=
'0' && keyCode <=
'9' && !event.HasAnyModifiers())) {
816 else if (!
mReadOnly && keyCode == WXK_DELETE) {
821 else if (!
mReadOnly && keyCode == WXK_BACK) {
827 if (theDigit != wxChar(
'-'))
835 else if (keyCode == WXK_LEFT) {
842 else if (keyCode == WXK_RIGHT) {
848 else if (keyCode == WXK_HOME) {
853 else if (keyCode == WXK_END) {
858 else if (!
mReadOnly && keyCode == WXK_UP) {
863 else if (!
mReadOnly && keyCode == WXK_DOWN) {
868 else if (keyCode == WXK_TAB) {
869#if defined(__WXMSW__)
872 wxWindow* parent = GetParent();
873 wxNavigationKeyEvent nevent;
874 nevent.SetWindowChange(event.ControlDown());
875 nevent.SetDirection(!event.ShiftDown());
876 nevent.SetEventObject(parent);
877 nevent.SetCurrentFocus(parent);
878 GetParent()->GetEventHandler()->ProcessEvent(nevent);
880 Navigate(event.ShiftDown()
881 ? wxNavigationKeyEvent::IsBackward
882 : wxNavigationKeyEvent::IsForward);
886 else if (keyCode == WXK_RETURN || keyCode == WXK_NUMPAD_ENTER) {
887 wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(
this), wxTopLevelWindow);
888 wxWindow *def = tlw->GetDefaultItem();
889 if (def && def->IsEnabled()) {
892 cevent.SetEventObject( def );
893 GetParent()->GetEventHandler()->ProcessEvent(cevent);
909#if wxUSE_ACCESSIBILITY
917 GetAccessible()->NotifyEvent(wxACC_EVENT_OBJECT_FOCUS,
932 event.SetEventObject(
this);
933 GetEventHandler()->ProcessEvent(event);
935#if wxUSE_ACCESSIBILITY
945 GetAccessible()->NotifyEvent(wxACC_EVENT_OBJECT_FOCUS,
950 GetAccessible()->NotifyEvent(wxACC_EVENT_OBJECT_NAMECHANGE,
988#if wxUSE_ACCESSIBILITY
998NumericTextCtrlAx::~NumericTextCtrlAx()
1006wxAccStatus NumericTextCtrlAx::DoDefaultAction(
int WXUNUSED(childId))
1008 return wxACC_NOT_SUPPORTED;
1013wxAccStatus NumericTextCtrlAx::GetChild(
int childId, wxAccessible **child)
1015 if (childId == wxACC_SELF) {
1026wxAccStatus NumericTextCtrlAx::GetChildCount(
int *childCount)
1028 *childCount = mCtrl->
mBoxes.size();
1040wxAccStatus NumericTextCtrlAx::GetDefaultAction(
int WXUNUSED(childId), wxString *actionName)
1042 actionName->clear();
1048wxAccStatus NumericTextCtrlAx::GetDescription(
int WXUNUSED(childId), wxString *description)
1050 description->clear();
1059wxAccStatus NumericTextCtrlAx::GetFocus(
int *childId, wxAccessible **child)
1061 *childId = mCtrl->GetFocusedDigit();
1068wxAccStatus NumericTextCtrlAx::GetHelpText(
int WXUNUSED(childId), wxString *helpText)
1072 wxToolTip *pTip = mCtrl->GetToolTip();
1074 *helpText = pTip->GetTip();
1081 return wxACC_NOT_SUPPORTED;
1087wxAccStatus NumericTextCtrlAx::GetKeyboardShortcut(
int WXUNUSED(childId), wxString *shortcut)
1096wxAccStatus NumericTextCtrlAx::GetLocation(wxRect & rect,
int elementId)
1098 if ((elementId != wxACC_SELF) &&
1102 rect = mCtrl->GetBox(elementId - 1);
1103 rect.SetPosition(mCtrl->ClientToScreen(rect.GetPosition()));
1107 rect = mCtrl->GetRect();
1108 rect.SetPosition(mCtrl->GetParent()->ClientToScreen(rect.GetPosition()));
1119 if (result ==
nullptr)
1122 auto& tr = result->fractionLabel;
1129wxAccStatus NumericTextCtrlAx::GetName(
int childId, wxString *
name)
1131 if (!mCtrl->mFormatter)
1135 auto & mFields = mCtrl->mFormatter->GetFields();
1136 auto & mDigits = mCtrl->mFormatter->GetDigitInfos();
1137 auto & mFieldValueStrings = mCtrl->mFieldValueStrings;
1139 wxString ctrlString = mCtrl->GetString();
1140 int field = mDigits[mCtrl->GetFocusedDigit()].field + 1;
1145 int childIdFromEnd = mCtrl->mBoxes.size() - childId + 1;
1150 if ((childId == wxACC_SELF) ||
1155 *
name = mCtrl->GetName();
1156 if (
name->empty()) {
1157 *
name = mCtrl->GetLabel();
1170 else if (childIdFromEnd == mLastDigit && ctrlString.IsSameAs(mLastCtrlString)) {
1171 *
name = mCachedName;
1176 if (mLastField !=
field) {
1178 int cnt = mFields.size();
1179 wxString decimal = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER);
1188 GetFraction( mCtrl->mContext,
label, mCtrl->mType, mCtrl->mFormatID);
1193 else if (
label == decimal &&
field == cnt - 1) {
1201 mCtrl->GetString().at(mDigits[childId - 1].pos);
1203 mLastDigit = childIdFromEnd;
1207 else if (mLastDigit != childIdFromEnd) {
1208 *
name = mCtrl->GetString().at(mDigits[childId - 1].pos);
1209 mLastDigit = childIdFromEnd;
1218 mCachedName = *
name;
1219 mLastCtrlString = ctrlString;
1226wxAccStatus NumericTextCtrlAx::GetRole(
int WXUNUSED(childId), wxAccRole *role)
1228 *role = wxROLE_SYSTEM_STATICTEXT;
1240wxAccStatus NumericTextCtrlAx::GetSelections(wxVariant * WXUNUSED(selections))
1242 return wxACC_NOT_IMPLEMENTED;
1246wxAccStatus NumericTextCtrlAx::GetState(
int WXUNUSED(childId),
long *state)
1248 *state = wxACC_STATE_SYSTEM_FOCUSABLE;
1256wxAccStatus NumericTextCtrlAx::GetValue(
int WXUNUSED(childId), wxString * WXUNUSED(strValue))
1258 return wxACC_NOT_IMPLEMENTED;
wxEVT_COMMAND_BUTTON_CLICKED
DEFINE_EVENT_TYPE(EVT_FREQWINDOW_RECALC)
EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, LabelDialog::OnFreqUpdate) LabelDialog
const NumericConverterType & NumericConverterType_BANDWIDTH()
const NumericConverterType & NumericConverterType_FREQUENCY()
const NumericConverterType & NumericConverterType_TIME()
wxEVT_COMMAND_TEXT_UPDATED
static void Arrow(wxDC &dc, wxCoord x, wxCoord y, int width, bool down=true)
void Popup(const BasicUI::WindowPlacement &window, const Point &pos={})
Display the menu at pos, invoke at most one action, then hide it.
A context in which formatter operates.
An explicitly nonlocalized string, not meant for the user to see.
NumericConverter provides the advanced formatting control used in the selection bar of Audacity.
virtual void ControlsToValue()
virtual void ValueToControls()
virtual void OnFormatUpdated(bool resetFocus)
bool SetCustomFormat(const TranslatableString &customFormat)
void Adjust(int steps, int dir, int focusedDigit)
void SetValue(double newValue)
FormatterContext mContext
bool SetTypeAndFormatName(const NumericConverterType &type, const NumericFormatID &formatName)
std::unique_ptr< NumericConverterFormatter > mFormatter
NumericFormatID mFormatID
bool SetFormatName(const NumericFormatID &formatName)
void EnableMenu(bool enable=true)
void OnMouse(wxMouseEvent &event)
void OnContext(wxContextMenuEvent &event)
void OnFocus(wxFocusEvent &event)
void OnKeyDown(wxKeyEvent &event)
void SetReadOnly(bool readOnly=true)
void Updated(bool keyup=false)
std::unique_ptr< wxFont > mDigitFont
virtual ~NumericTextCtrl()
void SetInvalidValue(double invalidValue)
bool SetCustomFormat(const TranslatableString &customFormat)
wxRect GetBox(size_t ii) const
void OnPaint(wxPaintEvent &event)
bool SetFormatName(const NumericFormatID &formatName)
void OnKeyUp(wxKeyEvent &event)
void HandleFormatterChanged(bool resetFocus)
void OnErase(wxEraseEvent &event)
void OnFormatUpdated(bool resetFocus) override
wxSize ComputeSizing(bool update=true, wxCoord digitW=0, wxCoord digitH=0)
void ValueToControls() override
void SetDigitSize(int width, int height)
std::vector< wxRect > mBoxes
std::unique_ptr< wxBitmap > mBackgroundBitmap
void SetName(const TranslatableString &name)
bool SetTypeAndFormatName(const NumericConverterType &type, const NumericFormatID &formatName)
NumericConverterType mType
void ControlsToValue() override
void SetValue(double newValue)
std::unique_ptr< wxFont > mLabelFont
std::vector< FieldPosition > mFieldPositions
void OnCaptureKey(wxCommandEvent &event)
wxColour & Colour(int iIndex)
void SetBrushColour(wxBrush &Brush, int iIndex)
void SetPenColour(wxPen &Pen, int iIndex)
Holds a msgid for the translation catalog; may also bind format arguments.
wxString Translation() const
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
void SetFocus(const WindowPlacement &focus)
Set the window that accepts keyboard input.
std::unique_ptr< WindowPlacement > FindFocus()
Find the window that is accepting keyboard input, if any.
void OnFocus(wxWindow &window, wxFocusEvent &event)
a function useful to implement a focus event handler The window releases the keyboard if the event is...
static const NumericConverterRegistryItem * Find(const FormatterContext &context, const NumericConverterType &type, const NumericFormatID &symbol)
static void Visit(const FormatterContext &context, const NumericConverterType &type, Visitor visitor)