/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
// how to proceed from START
{
StateTransitions& rRow = m_aTransitions[ START ];
rRow.insert( Transition( '_', NUM_START ) ); // if we encounter the normalizing character, we want to proceed with the number
}
// how to proceed from NUM_START
{
StateTransitions& rRow = m_aTransitions[ NUM_START ];
// a sign is allowed
lcl_insertSignTransitions( rRow, DIGIT_PRE_COMMA );
// common transitions for the two pre-comma states
lcl_insertCommonPreCommaTransitions( rRow, _cThSep, _cDecSep );
// the exponent may start here // (this would mean string like "_+e10_", but this is a valid fragment, though no valid number)
lcl_insertStartExponentTransition( rRow );
}
// how to proceed from DIGIT_PRE_COMMA
{
StateTransitions& rRow = m_aTransitions[ DIGIT_PRE_COMMA ];
// common transitions for the two pre-comma states
lcl_insertCommonPreCommaTransitions( rRow, _cThSep, _cDecSep );
// the exponent may start here
lcl_insertStartExponentTransition( rRow );
// the final transition indicating the end of the string // (if there is no comma and no post-comma, then the string may end here)
lcl_insertStopTransition( rRow );
}
// how to proceed from DIGIT_POST_COMMA
{
StateTransitions& rRow = m_aTransitions[ DIGIT_POST_COMMA ];
// there might be digits, which would keep the state at DIGIT_POST_COMMA
lcl_insertDigitTransitions( rRow, DIGIT_POST_COMMA );
// the exponent may start here
lcl_insertStartExponentTransition( rRow );
// the string may end here
lcl_insertStopTransition( rRow );
}
// how to proceed from EXPONENT_START
{
StateTransitions& rRow = m_aTransitions[ EXPONENT_START ];
// there may be a sign
lcl_insertSignTransitions( rRow, EXPONENT_DIGIT );
// there may be digits
lcl_insertDigitTransitions( rRow, EXPONENT_DIGIT );
// the string may end here
lcl_insertStopTransition( rRow );
}
// how to proceed from EXPONENT_DIGIT
{
StateTransitions& rRow = m_aTransitions[ EXPONENT_DIGIT ];
// there may be digits
lcl_insertDigitTransitions( rRow, EXPONENT_DIGIT );
// the string may end here
lcl_insertStopTransition( rRow );
}
// how to proceed from END
{ /*StateTransitions& rRow =*/ m_aTransitions[ EXPONENT_DIGIT ]; // no valid transition to leave this state // (note that we, for consistency, nevertheless want to have a row in the table)
}
}
while ( END != eCurrentState )
{ // look up the transition row for the current state
TransitionTable::const_iterator aRow = m_aTransitions.find( eCurrentState );
DBG_ASSERT( m_aTransitions.end() != aRow, "NumberValidator::implValidateNormalized: invalid transition table (row not found)!" );
if ( m_aTransitions.end() != aRow )
{ // look up the current character in this row
StateTransitions::const_iterator aTransition = aRow->second.find( *pCheckPos ); if ( aRow->second.end() != aTransition )
{ // there is a valid transition for this character
eCurrentState = aTransition->second;
++pCheckPos; continue;
}
}
// if we're here, there is no valid transition break;
}
DBG_ASSERT( ( END != eCurrentState ) || ( 0 == *pCheckPos ), "NumberValidator::implValidateNormalized: inconsistency!" ); // if we're at END, then the string should be done, too - the string should be normalized, means ending // a "_" and not containing any other "_" (except at the start), and "_" is the only possibility // to reach the END state
// the string is valid if and only if we reached the final state return ( END == eCurrentState );
}
bool NumberValidator::isValidNumericFragment( std::u16string_view _rText )
{ if ( _rText.empty() ) // empty strings are always allowed returntrue;
void Formatter::SetTextFormatted(const OUString& rStr)
{
SAL_INFO_IF(GetOrCreateFormatter().IsTextFormat(m_nFormatKey), "svtools", "FormattedField::SetTextFormatted : valid only with text formats !");
// calculate the new selection
Selection aSel(GetEntrySelection());
Selection aNewSel(aSel);
aNewSel.Normalize();
sal_Int32 nNewLen = sFormatted.getLength();
sal_Int32 nCurrentLen = GetEntryText().getLength(); if ((nNewLen > nCurrentLen) && (aNewSel.Max() == nCurrentLen))
{ // the new text is longer and the cursor was behind the last char (of the old text) if (aNewSel.Min() == 0)
{ // the whole text was selected -> select the new text on the whole, too
aNewSel.Max() = nNewLen; if (!nCurrentLen)
{ // there wasn't really a previous selection (as there was no previous text), we're setting a new one -> check the selection options
SelectionOptions nSelOptions = GetEntrySelectionOptions(); if (nSelOptions & SelectionOptions::ShowFirst)
{ // selection should be from right to left -> swap min and max
aNewSel.Min() = aNewSel.Max();
aNewSel.Max() = 0;
}
}
} elseif (aNewSel.Max() == aNewSel.Min())
{ // there was no selection -> set the cursor behind the new last char
aNewSel.Max() = nNewLen;
aNewSel.Min() = nNewLen;
}
} elseif (aNewSel.Max() > nNewLen)
aNewSel.Max() = nNewLen; else
aNewSel = aSel; // don't use the justified version
SetEntryText(sFormatted, aNewSel);
m_ValueState = valueString;
}
void Formatter::SetAutoColor(bool _bAutomatic)
{ if (_bAutomatic == m_bAutoColor) return;
m_bAutoColor = _bAutomatic; if (m_bAutoColor)
{ // if auto color is switched on, adjust the current text color, too
SetEntryTextColor(m_pLastOutputColor);
}
}
if ((nNewLen > nCurrentLen) && (aSel.Max() == nCurrentLen))
{ // new text is longer and the cursor is behind the last char if (aSel.Min() == 0)
{ if (!nCurrentLen)
{ // there wasn't really a previous selection (as there was no previous text)
aSel.Max() = 0;
} else
{ // the whole text was selected -> select the new text on the whole, too
aSel.Max() = nNewLen;
}
} elseif (aSel.Max() == aSel.Min())
{ // there was no selection -> set the cursor behind the new last char
aSel.Max() = nNewLen;
aSel.Min() = nNewLen;
}
} elseif (aSel.Max() > nNewLen)
aSel.Max() = nNewLen;
SetEntryText(rNew, aSel);
}
m_ValueState = valueDirty; // not always necessary, but better re-evaluate for safety reasons
}
void Formatter::ImplSetFormatKey(sal_uLong nFormatKey)
{
m_nFormatKey = nFormatKey; bool bNeedFormatter = (m_pFormatter == nullptr) && (nFormatKey != 0); if (bNeedFormatter)
{
GetOrCreateFormatter(); // this creates a standard formatter
assert(m_pFormatter);
// It might happen that the standard formatter makes no sense here, but it takes a default // format. Thus, it is possible to set one of the other standard keys (which are spanning // across multiple formatters).
m_nFormatKey = nFormatKey; // When calling SetFormatKey without a formatter, the key must be one of the standard values // that is available for all formatters (and, thus, also in this new one).
DBG_ASSERT(m_pFormatter->GetEntry(nFormatKey) != nullptr, "FormattedField::ImplSetFormatKey : invalid format key !");
}
}
// calc the default format key from the Office's UI locale if ( m_pFormatter )
{ // get the Office's locale and translate
LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false); // get the standard numeric format for this language
m_nFormatKey = m_pFormatter->GetStandardFormat( SvNumFormatType::NUMBER, eSysLanguage );
} else
m_nFormatKey = 0;
} else
{
LanguageType aOldLang;
OUString sOldFormat = GetFormat(aOldLang);
sal_uInt32 nDestKey = pFormatter->TestNewString(sOldFormat); if (nDestKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
{ // language of the new formatter const SvNumberformat* pDefaultEntry = pFormatter->GetEntry(0);
LanguageType aNewLang = pDefaultEntry ? pDefaultEntry->GetLanguage() : LANGUAGE_DONTKNOW;
// convert the old format string into the new language
sal_Int32 nCheckPos;
SvNumFormatType nType;
pFormatter->PutandConvertEntry(sOldFormat, nCheckPos, nType, nDestKey, aOldLang, aNewLang, true);
m_nFormatKey = nDestKey;
}
m_pFormatter = pFormatter;
}
FormatChanged(FORMAT_CHANGE_TYPE::FORMATTER);
}
OUString Formatter::GetFormat(LanguageType& eLang) const
{ const SvNumberformat* pFormatEntry = GetOrCreateFormatter().GetEntry(m_nFormatKey);
DBG_ASSERT(pFormatEntry != nullptr, "FormattedField::GetFormat: no number format for the given format key.");
OUString sFormatString = pFormatEntry ? pFormatEntry->GetFormatstring() : OUString();
eLang = pFormatEntry ? pFormatEntry->GetLanguage() : LANGUAGE_DONTKNOW;
if (nNewKey != m_nFormatKey)
SetFormatKey(nNewKey); returntrue;
}
OUString Formatter::FormatValue(double fValue)
{ if (m_aFormatValueHdl.IsSet())
{
std::optional<OUString> aText = m_aFormatValueHdl.Call(fValue); if (aText.has_value()) return aText.value();
}
OUString sNewText; if (GetOrCreateFormatter().IsTextFormat(m_nFormatKey))
{ // first convert the number as string in standard format
OUString sTemp;
GetOrCreateFormatter().GetOutputString(fValue, 0, sTemp, &m_pLastOutputColor); // then encode the string in the corresponding text format
GetOrCreateFormatter().GetOutputString(sTemp, m_nFormatKey, sNewText, &m_pLastOutputColor);
} else
{ if( IsUsingInputStringForFormatting())
{
sNewText = GetOrCreateFormatter().GetInputLineString(fValue, m_nFormatKey);
} else
{
GetOrCreateFormatter().GetOutputString(fValue, m_nFormatKey, sNewText, &m_pLastOutputColor);
}
}
return sNewText;
}
bool Formatter::GetThousandsSep() const
{
DBG_ASSERT(!GetOrCreateFormatter().IsTextFormat(m_nFormatKey), "FormattedField::GetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
void Formatter::SetThousandsSep(bool _bUseSeparator)
{
DBG_ASSERT(!GetOrCreateFormatter().IsTextFormat(m_nFormatKey), "FormattedField::SetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
// get the current settings bool bThousand, IsRed;
sal_uInt16 nPrecision, nLeadingCnt;
GetOrCreateFormatter().GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt); if (bThousand == _bUseSeparator) return;
// we need the language for the following
LanguageType eLang;
GetFormat(eLang);
// generate a new format ...
OUString sFmtDescription = GetOrCreateFormatter().GenerateFormat(m_nFormatKey, eLang, _bUseSeparator, IsRed, nPrecision, nLeadingCnt); // ... and introduce it to the formatter
sal_Int32 nCheckPos = 0;
sal_uInt32 nNewKey;
SvNumFormatType nType;
GetOrCreateFormatter().PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
// set the new key
ImplSetFormatKey(nNewKey);
FormatChanged(FORMAT_CHANGE_TYPE::THOUSANDSSEP);
}
sal_uInt16 Formatter::GetDecimalDigits() const
{
DBG_ASSERT(!GetOrCreateFormatter().IsTextFormat(m_nFormatKey), "FormattedField::GetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
void Formatter::SetDecimalDigits(sal_uInt16 _nPrecision)
{
DBG_ASSERT(!GetOrCreateFormatter().IsTextFormat(m_nFormatKey), "FormattedField::SetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
// get the current settings bool bThousand, IsRed;
sal_uInt16 nPrecision, nLeadingCnt;
GetOrCreateFormatter().GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt); if (nPrecision == _nPrecision) return;
// we need the language for the following
LanguageType eLang;
GetFormat(eLang);
// generate a new format ...
OUString sFmtDescription = GetOrCreateFormatter().GenerateFormat(m_nFormatKey, eLang, bThousand, IsRed, _nPrecision, nLeadingCnt); // ... and introduce it to the formatter
sal_Int32 nCheckPos = 0;
sal_uInt32 nNewKey;
SvNumFormatType nType;
GetOrCreateFormatter().PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
// set the new key
ImplSetFormatKey(nNewKey);
FormatChanged(FORMAT_CHANGE_TYPE::PRECISION);
}
void Formatter::EntryLostFocus()
{ // special treatment for empty texts if (GetEntryText().isEmpty())
{ if (!IsEmptyFieldEnabled())
{ if (TreatingAsNumber())
{
ImplSetValue(m_dCurrentValue, true);
Modify();
m_ValueState = valueDouble;
} else
{
OUString sNew = GetTextValue(); if (!sNew.isEmpty())
SetTextFormatted(sNew); else
SetTextFormatted(m_sDefaultText);
m_ValueState = valueString;
}
}
} else
{
Commit();
}
}
void Formatter::Commit()
{ // remember the old text
OUString sOld(GetEntryText());
// do the reformat
ReFormat();
// did the text change? if (GetEntryText() != sOld)
{ // consider the field as modified, // but we already have the most recent value; // don't reparse it from the text // (can lead to data loss when the format is lossy, // as is e.g. our default date format: 2-digit year!)
Modify(false);
}
}
if (!bUseExternalFormatterValue)
{
sal_uInt32 nFormatKey = m_nFormatKey; // IsNumberFormat changes the FormatKey!
if (GetOrCreateFormatter().IsTextFormat(nFormatKey) && m_bTreatAsNumber) // for detection of values like "1,1" in fields that are formatted as text
nFormatKey = 0;
// special treatment for percentage formatting
OUString sText = rText; if (GetOrCreateFormatter().GetType(m_nFormatKey) == SvNumFormatType::PERCENT)
{ // the language of our format
LanguageType eLanguage = m_pFormatter->GetEntry(m_nFormatKey)->GetLanguage(); // the default number format for this language
sal_uLong nStandardNumericFormat = m_pFormatter->GetStandardFormat(SvNumFormatType::NUMBER, eLanguage);
sal_uInt32 nTempFormat = nStandardNumericFormat; double dTemp; if (m_pFormatter->IsNumberFormat(sText, nTempFormat, dTemp) &&
SvNumFormatType::NUMBER == m_pFormatter->GetType(nTempFormat)) // the string is equivalent to a number formatted one (has no % sign) -> append it
sText += "%"; // (with this, an input of '3' becomes '3%', which then by the formatter is translated // into 0.03. Without this, the formatter would give us the double 3 for an input '3', // which equals 300 percent.
} if (!GetOrCreateFormatter().IsNumberFormat(sText, nFormatKey, fValue)) return std::optional<double>();
}
// tdf#155241 default to m_dDefaultValue only if explicitly set // otherwise default to m_dCurrentValue if (m_bDefaultValueSet)
dNewVal = m_dDefaultValue;
OUString sText(GetEntryText()); if (sText.isEmpty()) returntrue;
std::optional<double> aValue = ParseText(sText); if (!aValue.has_value()) returnfalse;
class DoubleNumericFormatter : public FieldFormatter
{ private:
DoubleNumericField& m_rNumericSpinButton; public:
DoubleNumericFormatter(DoubleNumericField& rNumericSpinButton)
: FieldFormatter(rNumericSpinButton)
, m_rNumericSpinButton(rNumericSpinButton)
{
}
virtualbool CheckText(const OUString& sText) const override
{ // We'd like to implement this using the NumberFormatter::IsNumberFormat, but unfortunately, this doesn't // recognize fragments of numbers (like, for instance "1e", which happens during entering e.g. "1e10") // Thus, the roundabout way via a regular expression return m_rNumericSpinButton.GetNumberValidator().isValidNumericFragment(sText);
}
switch (nWhat)
{ case FORMAT_CHANGE_TYPE::FORMATTER: case FORMAT_CHANGE_TYPE::PRECISION: case FORMAT_CHANGE_TYPE::THOUSANDSSEP: // the aspects which changed don't take our currency settings into account (in fact, they most probably // destroyed them)
m_rCurrencySpinButton.UpdateCurrencyFormat(); break; case FORMAT_CHANGE_TYPE::KEYONLY:
OSL_FAIL("DoubleCurrencyField::FormatChanged : somebody modified my key !"); // We always build our own format from the settings we get via special methods (setCurrencySymbol etc.). // Nobody but ourself should modify the format key directly! break; default: break;
}
FieldFormatter::FormatChanged(nWhat);
}
void GuardSetFormat(const OUString& rString, LanguageType eLanguage)
{ // set this new basic format
m_bChangingFormat = true;
SetFormat(rString, eLanguage);
m_bChangingFormat = false;
}
void DoubleNumericField::ResetConformanceTester()
{ // the thousands and the decimal separator are language dependent
Formatter& rFormatter = GetFormatter(); const SvNumberformat* pFormatEntry = rFormatter.GetOrCreateFormatter().GetEntry(rFormatter.GetFormatKey());
void DoubleCurrencyField::UpdateCurrencyFormat()
{ // the old settings
LanguageType eLanguage;
m_pFormatter->GetFormat(eLanguage); bool bThSep = m_pFormatter->GetThousandsSep();
sal_uInt16 nDigits = m_pFormatter->GetDecimalDigits();
// build a new format string with the base class' and my own settings
/* Strangely with gcc 4.6.3 this needs a temporary LanguageTag, otherwise * there's * error: request for member 'getNumThousandSep' in 'aLocaleInfo', which is
* of non-class type 'LocaleDataWrapper(LanguageTag)' */
LocaleDataWrapper aLocaleInfo(( LanguageTag(eLanguage) ));
OUString sTemp = "[$" + sSymbol + "] "
+ sNewFormat // for negative values : $ -0.00, not -$ 0.00... // (the real solution would be a possibility to choose a "positive currency format" and a "negative currency format"... // But not now... (and hey, you could take a formatted field for this...)) // FS - 31.03.00 74642
+ ";[$"
+ sSymbol
+ "] -"
+ sNewFormat;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.