/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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 .
*/
#include <sal/config.h>
#include <cmath>
#include <string_view>
#include <sal/log.hxx>
#include <o3tl/string_view.hxx>
#include <osl/diagnose.h>
#include <comphelper/string.hxx>
#include <tools/UnitConversion.hxx>
#include <vcl/builder.hxx>
#include <vcl/fieldvalues.hxx>
#include <vcl/toolkit/field.hxx>
#include <vcl/event.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <vcl/uitest/uiobject.hxx>
#include <vcl/uitest/metricfielduiobject.hxx>
#include <svdata.hxx>
#include <i18nutil/unicode.hxx>
#include <rtl/math.hxx>
#include <unotools/localedatawrapper.hxx>
#include <boost/property_tree/ptree.hpp>
#include <tools/json_writer.hxx>
using namespace ::com::sun::star;
using namespace ::comphelper;
namespace
{
std::string FieldUnitToString(FieldUnit unit)
{
switch(unit)
{
case FieldUnit::NONE:
return "";
case FieldUnit::MM:
return "mm";
case FieldUnit::CM:
return "cm";
case FieldUnit::M:
return "m";
case FieldUnit::KM:
return "km";
case FieldUnit::TWIP:
return "twip";
case FieldUnit::POINT:
return "point";
case FieldUnit::PICA:
return "pica";
case FieldUnit::INCH:
return "inch";
case FieldUnit::FOOT:
return "foot";
case FieldUnit::MILE:
return "mile";
case FieldUnit::CHAR:
return "char";
case FieldUnit::LINE:
return "line";
case FieldUnit::CUSTOM:
return "custom";
case FieldUnit::PERCENT:
return "percent";
case FieldUnit::MM_100TH:
return "mm100th";
case FieldUnit::PIXEL:
return "pixel";
case FieldUnit::DEGREE:
return "degree";
case FieldUnit::SECOND:
return "second";
case FieldUnit::MILLISECOND:
return "millisecond";
}
return "";
}
sal_Int64 ImplPower10( sal_uInt16 n )
{
sal_uInt16 i;
sal_Int64 nValue = 1;
for ( i=0; i < n; i++ )
nValue *= 10;
return nValue;
}
bool ImplNumericProcessKeyInput( const KeyEvent& rKEvt,
bool bStrictFormat, bool bThousandSep,
const LocaleDataWrapper& rLocaleDataWrapper )
{
if ( !bStrictFormat )
return false;
else
{
sal_Unicode cChar = rKEvt.GetCharCode();
sal_uInt16 nGroup = rKEvt.GetKeyCode().GetGroup();
return !((nGroup == KEYGROUP_FKEYS) ||
(nGroup == KEYGROUP_CURSOR) ||
(nGroup == KEYGROUP_MISC) ||
((cChar >= '0') && (cChar <= '9')) ||
rLocaleDataWrapper.getNumDecimalSep() == OUStringChar(cChar) ||
(bThousandSep && rLocaleDataWrapper.getNumThousandSep() == OUStringChar(cChar)) ||
rLocaleDataWrapper.getNumDecimalSepAlt() == OUStringChar(cChar) ||
(cChar == '-'));
}
}
bool ImplNumericGetValue( const OUString& rStr, sal_Int64& rValue,
sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrapper,
bool bCurrency = false )
{
OUString aStr = rStr;
OUStringBuffer aStr1, aStr2, aStrNum, aStrDenom;
bool bNegative = false;
bool bFrac = false;
sal_Int32 nDecPos, nFracDivPos;
sal_Int64 nValue;
// react on empty string
if ( rStr.isEmpty() )
return false;
// remove leading and trailing spaces
aStr = aStr.trim();
// find position of decimal point
nDecPos = aStr.indexOf( rLocaleDataWrapper.getNumDecimalSep() );
if (nDecPos < 0 && !rLocaleDataWrapper.getNumDecimalSepAlt().isEmpty())
nDecPos = aStr.indexOf( rLocaleDataWrapper.getNumDecimalSepAlt() );
// find position of fraction
nFracDivPos = aStr.indexOf( '/' );
// parse fractional strings
if (nFracDivPos > 0)
{
bFrac = true;
sal_Int32 nFracNumPos = aStr.lastIndexOf(' ', nFracDivPos);
// If in "a b/c" format.
if(nFracNumPos != -1 )
{
aStr1.append(aStr.subView(0, nFracNumPos));
aStrNum.append(aStr.subView(nFracNumPos+1, nFracDivPos-nFracNumPos-1));
aStrDenom.append(aStr.subView(nFracDivPos+1));
}
// "a/b" format, or not a fraction at all
else
{
aStrNum.append(aStr.subView(0, nFracDivPos));
aStrDenom.append(aStr.subView(nFracDivPos+1));
}
}
// parse decimal strings
else if ( nDecPos >= 0)
{
aStr1.append(aStr.subView(0, nDecPos));
aStr2.append(aStr.subView(nDecPos+1));
}
else
aStr1 = aStr;
// negative?
if ( bCurrency )
{
if ( aStr.startsWith("(") && aStr.endsWith(")") )
bNegative = true;
if ( !bNegative )
{
for (sal_Int32 i=0; i < aStr.getLength(); i++ )
{
if ( (aStr[i] >= '0') && (aStr[i] <= '9') )
break;
else if ( aStr[i] == '-' )
{
bNegative = true;
break;
}
}
}
if (!bNegative && !aStr.isEmpty())
{
sal_uInt16 nFormat = rLocaleDataWrapper.getCurrNegativeFormat();
if ( (nFormat == 3) || (nFormat == 6) || // $1- || 1-$
(nFormat == 7) || (nFormat == 10) ) // 1$- || 1 $-
{
for (sal_Int32 i = aStr.getLength()-1; i > 0; --i )
{
if ( (aStr[i] >= '0') && (aStr[i] <= '9') )
break;
else if ( aStr[i] == '-' )
{
bNegative = true;
break;
}
}
}
}
}
else
{
if ( !aStr1.isEmpty() && aStr1[0] == '-')
bNegative = true;
if ( !aStrNum.isEmpty() && aStrNum[0] == '-') // For non-mixed fractions
bNegative = true;
}
// remove all unwanted characters
// For whole number
for (sal_Int32 i=0; i < aStr1.getLength(); )
{
if ( (aStr1[i] >= '0') && (aStr1[i] <= '9') )
i++;
else
aStr1.remove( i, 1 );
}
// For decimal
if (!bFrac) {
for (sal_Int32 i=0; i < aStr2.getLength(); )
{
if ((aStr2[i] >= '0') && (aStr2[i] <= '9'))
++i;
else
aStr2.remove(i, 1);
}
}
else {
// for numerator
for (sal_Int32 i=0; i < aStrNum.getLength(); )
{
if ((aStrNum[i] >= '0') && (aStrNum[i] <= '9'))
++i;
else
aStrNum.remove(i, 1);
}
// for denominator
for (sal_Int32 i=0; i < aStrDenom.getLength(); )
{
if ((aStrDenom[i] >= '0') && (aStrDenom[i] <= '9'))
++i;
else
aStrDenom.remove(i, 1);
}
}
if ( !bFrac && aStr1.isEmpty() && aStr2.isEmpty() )
return false;
else if ( bFrac && aStr1.isEmpty() && (aStrNum.isEmpty() || aStrDenom.isEmpty()) )
return false;
if ( aStr1.isEmpty() )
aStr1 = "0";
if ( bNegative )
aStr1.insert(0, "-");
// Convert fractional strings
if (bFrac) {
// Convert to fraction
sal_Int64 nWholeNum = o3tl::toInt64(aStr1);
aStr1.setLength(0);
sal_Int64 nNum = o3tl::toInt64(aStrNum);
sal_Int64 nDenom = o3tl::toInt64(aStrDenom);
if (nDenom == 0) return false; // Division by zero
double nFrac2Dec = nWholeNum + static_cast<double>(nNum)/nDenom; // Convert to double for floating point precision
OUStringBuffer aStrFrac(OUString::number(nFrac2Dec));
// Reconvert division result to string and parse
nDecPos = aStrFrac.indexOf('.');
if ( nDecPos >= 0)
{
aStr1.append(aStrFrac.getStr(), nDecPos);
aStr2.append(aStrFrac.getStr()+nDecPos+1);
}
else
aStr1 = std::move(aStrFrac);
}
// prune and round fraction
bool bRound = false;
if (aStr2.getLength() > nDecDigits)
{
if (aStr2[nDecDigits] >= '5')
bRound = true;
string::truncateToLength(aStr2, nDecDigits);
}
if (aStr2.getLength() < nDecDigits)
string::padToLength(aStr2, nDecDigits, '0');
aStr = aStr1 + aStr2;
// check range
nValue = aStr.toInt64();
if( nValue == 0 )
{
// check if string is equivalent to zero
sal_Int32 nIndex = bNegative ? 1 : 0;
while (nIndex < aStr.getLength() && aStr[nIndex] == '0')
++nIndex;
if( nIndex < aStr.getLength() )
{
rValue = bNegative ? SAL_MIN_INT64 : SAL_MAX_INT64;
return true;
}
}
if (bRound)
{
if ( !bNegative )
nValue++;
else
nValue--;
}
rValue = nValue;
return true;
}
void ImplUpdateSeparatorString( OUString& io_rText,
std::u16string_view rOldDecSep, std::u16string_view rNewDecSep,
std::u16string_view rOldThSep, std::u16string_view rNewThSep )
{
OUStringBuffer aBuf( io_rText.getLength() );
sal_Int32 nIndexDec = 0, nIndexTh = 0, nIndex = 0;
const sal_Unicode* pBuffer = io_rText.getStr();
while( nIndex != -1 )
{
nIndexDec = io_rText.indexOf( rOldDecSep, nIndex );
nIndexTh = io_rText.indexOf( rOldThSep, nIndex );
if( (nIndexTh != -1 && nIndexDec != -1 && nIndexTh < nIndexDec )
|| (nIndexTh != -1 && nIndexDec == -1)
)
{
aBuf.append( OUString::Concat(std::u16string_view(pBuffer + nIndex, nIndexTh - nIndex )) + rNewThSep );
nIndex = nIndexTh + rOldThSep.size();
}
else if( nIndexDec != -1 )
{
aBuf.append( OUString::Concat(std::u16string_view(pBuffer + nIndex, nIndexDec - nIndex )) + rNewDecSep );
nIndex = nIndexDec + rOldDecSep.size();
}
else
{
aBuf.append( pBuffer + nIndex );
nIndex = -1;
}
}
io_rText = aBuf.makeStringAndClear();
}
void ImplUpdateSeparators( std::u16string_view rOldDecSep, std::u16string_view rNewDecSep,
std::u16string_view rOldThSep, std::u16string_view rNewThSep,
Edit* pEdit )
{
bool bChangeDec = (rOldDecSep != rNewDecSep);
bool bChangeTh = (rOldThSep != rNewThSep );
if( !(bChangeDec || bChangeTh) )
return;
bool bUpdateMode = pEdit->IsUpdateMode();
pEdit->SetUpdateMode( false );
OUString aText = pEdit->GetText();
ImplUpdateSeparatorString( aText, rOldDecSep, rNewDecSep, rOldThSep, rNewThSep );
pEdit->SetText( aText );
ComboBox* pCombo = dynamic_cast<ComboBox*>(pEdit);
if( pCombo )
{
// update box entries
sal_Int32 nEntryCount = pCombo->GetEntryCount();
for ( sal_Int32 i=0; i < nEntryCount; i++ )
{
aText = pCombo->GetEntry( i );
void* pEntryData = pCombo->GetEntryData( i );
ImplUpdateSeparatorString( aText, rOldDecSep, rNewDecSep, rOldThSep, rNewThSep );
pCombo->RemoveEntryAt(i);
pCombo->InsertEntry( aText, i );
pCombo->SetEntryData( i, pEntryData );
}
}
if( bUpdateMode )
pEdit->SetUpdateMode( bUpdateMode );
}
} // namespace
FormatterBase::FormatterBase(Edit* pField)
{
mpField = pField;
mpLocaleDataWrapper = nullptr;
mbReformat = false;
mbStrictFormat = false;
mbEmptyFieldValue = false;
mbEmptyFieldValueEnabled = false;
}
FormatterBase::~FormatterBase()
{
}
LocaleDataWrapper& FormatterBase::ImplGetLocaleDataWrapper() const
{
if ( !mpLocaleDataWrapper )
{
mpLocaleDataWrapper.reset( new LocaleDataWrapper( GetLanguageTag() ) );
}
return *mpLocaleDataWrapper;
}
/** reset the LocaleDataWrapper when the language tag changes */
void FormatterBase::ImplResetLocaleDataWrapper() const
{
// just get rid of, the next time it is requested, it will get loaded with the right
// language tag
mpLocaleDataWrapper.reset();
}
const LocaleDataWrapper& FormatterBase::GetLocaleDataWrapper() const
{
return ImplGetLocaleDataWrapper();
}
void FormatterBase::Reformat()
{
}
void FormatterBase::ReformatAll()
{
Reformat();
};
void FormatterBase::SetStrictFormat( bool bStrict )
{
if ( bStrict != mbStrictFormat )
{
mbStrictFormat = bStrict;
if ( mbStrictFormat )
ReformatAll();
}
}
const lang::Locale& FormatterBase::GetLocale() const
{
if ( mpField )
return mpField->GetSettings().GetLanguageTag().getLocale();
else
return Application::GetSettings().GetLanguageTag().getLocale();
}
const LanguageTag& FormatterBase::GetLanguageTag() const
{
if ( mpField )
return mpField->GetSettings().GetLanguageTag();
else
return Application::GetSettings().GetLanguageTag();
}
void FormatterBase::ImplSetText( const OUString& rText, Selection const * pNewSelection )
{
if ( mpField )
{
if (pNewSelection)
mpField->SetText(rText, *pNewSelection);
else
{
Selection aSel = mpField->GetSelection();
aSel.Min() = aSel.Max();
mpField->SetText(rText, aSel);
}
MarkToBeReformatted( false );
}
}
void FormatterBase::SetEmptyFieldValue()
{
if ( mpField )
mpField->SetText( OUString() );
mbEmptyFieldValue = true;
}
bool FormatterBase::IsEmptyFieldValue() const
{
return (!mpField || mpField->GetText().isEmpty());
}
void NumericFormatter::FormatValue(Selection const * pNewSelection)
{
mbFormatting = true;
ImplSetText(CreateFieldText(mnLastValue), pNewSelection);
mbFormatting = false;
}
void NumericFormatter::ImplNumericReformat()
{
mnLastValue = GetValue();
FormatValue();
}
NumericFormatter::NumericFormatter(Edit* pEdit)
: FormatterBase(pEdit)
, mnLastValue(0)
, mnMin(0)
// a "large" value substantially smaller than SAL_MAX_INT64, to avoid
// overflow in computations using this "dummy" value
, mnMax(SAL_MAX_INT32)
, mbFormatting(false)
, mnSpinSize(1)
// for fields
, mnFirst(mnMin)
, mnLast(mnMax)
, mnDecimalDigits(0)
, mbThousandSep(true)
{
ReformatAll();
}
NumericFormatter::~NumericFormatter()
{
}
void NumericFormatter::SetMin( sal_Int64 nNewMin )
{
mnMin = nNewMin;
if ( !IsEmptyFieldValue() )
ReformatAll();
}
void NumericFormatter::SetMax( sal_Int64 nNewMax )
{
mnMax = nNewMax;
if ( !IsEmptyFieldValue() )
ReformatAll();
}
void NumericFormatter::SetUseThousandSep( bool bValue )
{
mbThousandSep = bValue;
ReformatAll();
}
void NumericFormatter::SetDecimalDigits( sal_uInt16 nDigits )
{
mnDecimalDigits = nDigits;
ReformatAll();
}
void NumericFormatter::SetValue( sal_Int64 nNewValue )
{
SetUserValue( nNewValue );
SetEmptyFieldValueData( false );
}
OUString NumericFormatter::CreateFieldText( sal_Int64 nValue ) const
{
return ImplGetLocaleDataWrapper().getNum( nValue, GetDecimalDigits(), IsUseThousandSep(), /*ShowTrailingZeros*/true );
}
void NumericFormatter::ImplSetUserValue( sal_Int64 nNewValue, Selection const * pNewSelection )
{
nNewValue = ClipAgainstMinMax(nNewValue);
mnLastValue = nNewValue;
if ( GetField() )
FormatValue(pNewSelection);
}
void NumericFormatter::SetUserValue( sal_Int64 nNewValue )
{
ImplSetUserValue( nNewValue );
}
sal_Int64 NumericFormatter::GetValueFromString(const OUString& rStr) const
{
sal_Int64 nTempValue;
if (ImplNumericGetValue(rStr, nTempValue,
GetDecimalDigits(), ImplGetLocaleDataWrapper()))
{
return ClipAgainstMinMax(nTempValue);
}
else
return mnLastValue;
}
OUString NumericFormatter::GetValueString() const
{
return Application::GetSettings().GetNeutralLocaleDataWrapper().
getNum(GetValue(), GetDecimalDigits(), false, false);
}
// currently used by online
void NumericFormatter::SetValueFromString(const OUString& rStr)
{
sal_Int64 nValue;
if (ImplNumericGetValue(rStr, nValue, GetDecimalDigits(),
Application::GetSettings().GetNeutralLocaleDataWrapper()))
{
ImplNewFieldValue(nValue);
}
else
{
SAL_WARN("vcl", "fail to convert the value: " << rStr );
}
}
sal_Int64 NumericFormatter::GetValue() const
{
if (mbFormatting) //don't parse the entry if we're currently formatting what to put in it
return mnLastValue;
return GetField() ? GetValueFromString(GetField()->GetText()) : 0;
}
sal_Int64 NumericFormatter::Normalize( sal_Int64 nValue ) const
{
return (nValue * ImplPower10( GetDecimalDigits() ) );
}
sal_Int64 NumericFormatter::Denormalize( sal_Int64 nValue ) const
{
sal_Int64 nFactor = ImplPower10( GetDecimalDigits() );
if ((nValue < ( SAL_MIN_INT64 + nFactor )) ||
(nValue > ( SAL_MAX_INT64 - nFactor )))
{
return ( nValue / nFactor );
}
if( nValue < 0 )
{
sal_Int64 nHalf = nFactor / 2;
return ((nValue - nHalf) / nFactor );
}
else
{
sal_Int64 nHalf = nFactor / 2;
return ((nValue + nHalf) / nFactor );
}
}
void NumericFormatter::Reformat()
{
if ( !GetField() )
return;
if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
return;
ImplNumericReformat();
}
void NumericFormatter::FieldUp()
{
sal_Int64 nValue = GetValue();
sal_Int64 nRemainder = nValue % mnSpinSize;
if (nValue >= 0)
nValue = (nRemainder == 0) ? nValue + mnSpinSize : nValue + mnSpinSize - nRemainder;
else
nValue = (nRemainder == 0) ? nValue + mnSpinSize : nValue - nRemainder;
nValue = ClipAgainstMinMax(nValue);
ImplNewFieldValue( nValue );
}
void NumericFormatter::FieldDown()
{
sal_Int64 nValue = GetValue();
sal_Int64 nRemainder = nValue % mnSpinSize;
if (nValue >= 0)
nValue = (nRemainder == 0) ? nValue - mnSpinSize : nValue - nRemainder;
else
nValue = (nRemainder == 0) ? nValue - mnSpinSize : nValue - mnSpinSize - nRemainder;
nValue = ClipAgainstMinMax(nValue);
ImplNewFieldValue( nValue );
}
void NumericFormatter::FieldFirst()
{
ImplNewFieldValue( mnFirst );
}
void NumericFormatter::FieldLast()
{
ImplNewFieldValue( mnLast );
}
void NumericFormatter::ImplNewFieldValue( sal_Int64 nNewValue )
{
if ( !GetField() )
return;
// !!! We should check why we do not validate in ImplSetUserValue() if the value was
// changed. This should be done there as well since otherwise the call to Modify would not
// be allowed. Anyway, the paths from ImplNewFieldValue, ImplSetUserValue, and ImplSetText
// should be checked and clearly traced (with comment) in order to find out what happens.
Selection aSelection = GetField()->GetSelection();
aSelection.Normalize();
OUString aText = GetField()->GetText();
// leave it as is if selected until end
if ( static_cast<sal_Int32>(aSelection.Max()) == aText.getLength() )
{
if ( !aSelection.Len() )
aSelection.Min() = SELECTION_MAX;
aSelection.Max() = SELECTION_MAX;
}
sal_Int64 nOldLastValue = mnLastValue;
ImplSetUserValue( nNewValue, &aSelection );
mnLastValue = nOldLastValue;
// Modify during Edit is only set during KeyInput
if ( GetField()->GetText() != aText )
{
GetField()->SetModifyFlag();
GetField()->Modify();
}
}
sal_Int64 NumericFormatter::ClipAgainstMinMax(sal_Int64 nValue) const
{
if (nValue > mnMax)
nValue = mnMax;
else if (nValue < mnMin)
nValue = mnMin;
return nValue;
}
namespace
{
Size calcMinimumSize(const Edit &rSpinField, const NumericFormatter &rFormatter)
{
OUStringBuffer aBuf;
sal_Int32 nTextLen;
nTextLen = std::u16string_view(OUString::number(rFormatter.GetMin())).size();
string::padToLength(aBuf, nTextLen, '9');
Size aMinTextSize = rSpinField.CalcMinimumSizeForText(
rFormatter.CreateFieldText(OUString::unacquired(aBuf).toInt64()));
aBuf.setLength(0);
nTextLen = std::u16string_view(OUString::number(rFormatter.GetMax())).size();
string::padToLength(aBuf, nTextLen, '9');
Size aMaxTextSize = rSpinField.CalcMinimumSizeForText(
rFormatter.CreateFieldText(OUString::unacquired(aBuf).toInt64()));
aBuf.setLength(0);
Size aRet(std::max(aMinTextSize.Width(), aMaxTextSize.Width()),
std::max(aMinTextSize.Height(), aMaxTextSize.Height()));
OUStringBuffer sBuf("999999999");
sal_uInt16 nDigits = rFormatter.GetDecimalDigits();
if (nDigits)
{
sBuf.append('.');
string::padToLength(aBuf, aBuf.getLength() + nDigits, '9');
}
aMaxTextSize = rSpinField.CalcMinimumSizeForText(sBuf.makeStringAndClear());
aRet.setWidth( std::min(aRet.Width(), aMaxTextSize.Width()) );
return aRet;
}
}
NumericBox::NumericBox(vcl::Window* pParent, WinBits nWinStyle)
: ComboBox(pParent, nWinStyle)
, NumericFormatter(this)
{
Reformat();
if ( !(nWinStyle & WB_HIDE ) )
Show();
}
void NumericBox::dispose()
{
ClearField();
ComboBox::dispose();
}
Size NumericBox::CalcMinimumSize() const
{
Size aRet(calcMinimumSize(*this, *this));
if (IsDropDownBox())
{
Size aComboSugg(ComboBox::CalcMinimumSize());
aRet.setWidth( std::max(aRet.Width(), aComboSugg.Width()) );
aRet.setHeight( std::max(aRet.Height(), aComboSugg.Height()) );
}
return aRet;
}
bool NumericBox::PreNotify( NotifyEvent& rNEvt )
{
if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
{
if ( ImplNumericProcessKeyInput( *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
return true;
}
return ComboBox::PreNotify( rNEvt );
}
bool NumericBox::EventNotify( NotifyEvent& rNEvt )
{
if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
MarkToBeReformatted( false );
else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
{
if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
Reformat();
}
return ComboBox::EventNotify( rNEvt );
}
void NumericBox::DataChanged( const DataChangedEvent& rDCEvt )
{
ComboBox::DataChanged( rDCEvt );
if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
{
OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
ImplResetLocaleDataWrapper();
OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
ReformatAll();
}
}
void NumericBox::Modify()
{
MarkToBeReformatted( true );
ComboBox::Modify();
}
void NumericBox::ImplNumericReformat( const OUString& rStr, sal_Int64& rValue,
OUString& rOutStr )
{
if (ImplNumericGetValue(rStr, rValue, GetDecimalDigits(), ImplGetLocaleDataWrapper()))
{
sal_Int64 nTempVal = ClipAgainstMinMax(rValue);
rOutStr = CreateFieldText( nTempVal );
}
}
void NumericBox::ReformatAll()
{
sal_Int64 nValue;
OUString aStr;
SetUpdateMode( false );
sal_Int32 nEntryCount = GetEntryCount();
for ( sal_Int32 i=0; i < nEntryCount; i++ )
{
ImplNumericReformat( GetEntry( i ), nValue, aStr );
RemoveEntryAt(i);
InsertEntry( aStr, i );
}
NumericFormatter::Reformat();
SetUpdateMode( true );
}
static bool ImplMetricProcessKeyInput( const KeyEvent& rKEvt,
bool bUseThousandSep, const LocaleDataWrapper& rWrapper )
{
// no meaningful strict format; therefore allow all characters
return ImplNumericProcessKeyInput( rKEvt, false, bUseThousandSep, rWrapper );
}
static OUString ImplMetricGetUnitText(std::u16string_view rStr)
{
// fetch unit text
OUStringBuffer aStr;
for (sal_Int32 i = static_cast<sal_Int32>(rStr.size())-1; i >= 0; --i)
{
sal_Unicode c = rStr[i];
if ( (c == '\'') || (c == '\"') || (c == '%') || (c == 0x2032) || (c == 0x2033) || unicode::isAlpha(c) || unicode::isControl(c) )
aStr.insert(0, c);
else
{
if (!aStr.isEmpty())
break;
}
}
return aStr.makeStringAndClear();
}
// #104355# support localized measurements
static OUString ImplMetricToString( FieldUnit rUnit )
{
// return unit's default string (ie, the first one )
for (auto const& elem : ImplGetFieldUnits())
{
if (elem.second == rUnit)
return elem.first;
}
return OUString();
}
namespace
{
FieldUnit StringToMetric(const OUString &rMetricString)
{
// return FieldUnit
OUString aStr = rMetricString.toAsciiLowerCase().replaceAll(" ", "");
for (auto const& elem : ImplGetCleanedFieldUnits())
{
if ( elem.first == aStr )
return elem.second;
}
return FieldUnit::NONE;
}
}
static FieldUnit ImplMetricGetUnit(std::u16string_view rStr)
{
OUString aStr = ImplMetricGetUnitText(rStr);
return StringToMetric(aStr);
}
static FieldUnit ImplMap2FieldUnit( MapUnit meUnit, tools::Long& nDecDigits )
{
switch( meUnit )
{
case MapUnit::Map100thMM :
nDecDigits -= 2;
return FieldUnit::MM;
case MapUnit::Map10thMM :
nDecDigits -= 1;
return FieldUnit::MM;
case MapUnit::MapMM :
return FieldUnit::MM;
case MapUnit::MapCM :
return FieldUnit::CM;
case MapUnit::Map1000thInch :
nDecDigits -= 3;
return FieldUnit::INCH;
case MapUnit::Map100thInch :
nDecDigits -= 2;
return FieldUnit::INCH;
case MapUnit::Map10thInch :
nDecDigits -= 1;
return FieldUnit::INCH;
case MapUnit::MapInch :
return FieldUnit::INCH;
case MapUnit::MapPoint :
return FieldUnit::POINT;
case MapUnit::MapTwip :
return FieldUnit::TWIP;
default:
OSL_FAIL( "default eInUnit" );
break;
}
return FieldUnit::NONE;
}
static double nonValueDoubleToValueDouble( double nValue )
{
return std::isfinite( nValue ) ? nValue : 0.0;
}
namespace vcl
{
sal_Int64 ConvertValue(sal_Int64 nValue, sal_Int64 mnBaseValue, sal_uInt16 nDecDigits,
FieldUnit eInUnit, FieldUnit eOutUnit)
{
double nDouble = nonValueDoubleToValueDouble(vcl::ConvertDoubleValue(
static_cast<double>(nValue), mnBaseValue, nDecDigits, eInUnit, eOutUnit));
sal_Int64 nLong ;
// caution: precision loss in double cast
if ( nDouble <= double(SAL_MIN_INT64) )
nLong = SAL_MIN_INT64;
else if ( nDouble >= double(SAL_MAX_INT64) )
nLong = SAL_MAX_INT64;
else
nLong = static_cast<sal_Int64>( std::round(nDouble) );
return nLong;
}
}
namespace {
bool checkConversionUnits(MapUnit eInUnit, FieldUnit eOutUnit)
{
return eOutUnit != FieldUnit::PERCENT
&& eOutUnit != FieldUnit::CUSTOM
&& eOutUnit != FieldUnit::NONE
&& eInUnit != MapUnit::MapPixel
&& eInUnit != MapUnit::MapSysFont
&& eInUnit != MapUnit::MapAppFont
&& eInUnit != MapUnit::MapRelative;
}
double convertValue( double nValue, tools::Long nDigits, FieldUnit eInUnit, FieldUnit eOutUnit )
{
if ( nDigits < 0 )
{
while ( nDigits )
{
nValue += 5;
nValue /= 10;
nDigits++;
}
}
else
{
nValue *= ImplPower10(nDigits);
}
if ( eInUnit != eOutUnit )
{
const o3tl::Length eFrom = FieldToO3tlLength(eInUnit), eTo = FieldToO3tlLength(eOutUnit);
if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid)
nValue = o3tl::convert(nValue, eFrom, eTo);
}
return nValue;
}
}
namespace vcl
{
sal_Int64 ConvertValue( sal_Int64 nValue, sal_uInt16 nDigits,
MapUnit eInUnit, FieldUnit eOutUnit )
{
if ( !checkConversionUnits(eInUnit, eOutUnit) )
{
OSL_FAIL( "invalid parameters" );
return nValue;
}
tools::Long nDecDigits = nDigits;
FieldUnit eFieldUnit = ImplMap2FieldUnit( eInUnit, nDecDigits );
// Avoid sal_Int64 <-> double conversion issues if possible:
if (eFieldUnit == eOutUnit && nDigits == 0)
{
return nValue;
}
return static_cast<sal_Int64>(
nonValueDoubleToValueDouble(
convertValue( nValue, nDecDigits, eFieldUnit, eOutUnit ) ) );
}
double ConvertDoubleValue(double nValue, sal_Int64 mnBaseValue, sal_uInt16 nDecDigits,
FieldUnit eInUnit, FieldUnit eOutUnit)
{
if ( eInUnit != eOutUnit )
{
if (eInUnit == FieldUnit::PERCENT && mnBaseValue > 0 && nValue > 0)
{
sal_Int64 nDiv = 100 * ImplPower10(nDecDigits);
if (mnBaseValue != 1)
nValue *= mnBaseValue;
nValue += nDiv / 2;
nValue /= nDiv;
}
else
{
const o3tl::Length eFrom = FieldToO3tlLength(eInUnit, o3tl::Length::invalid);
const o3tl::Length eTo = FieldToO3tlLength(eOutUnit, o3tl::Length::invalid);
if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid)
nValue = o3tl::convert(nValue, eFrom, eTo);
}
}
return nValue;
}
double ConvertDoubleValue(double nValue, sal_uInt16 nDigits,
MapUnit eInUnit, FieldUnit eOutUnit)
{
if ( !checkConversionUnits(eInUnit, eOutUnit) )
{
OSL_FAIL( "invalid parameters" );
return nValue;
}
tools::Long nDecDigits = nDigits;
FieldUnit eFieldUnit = ImplMap2FieldUnit( eInUnit, nDecDigits );
return convertValue(nValue, nDecDigits, eFieldUnit, eOutUnit);
}
double ConvertDoubleValue(double nValue, sal_uInt16 nDigits,
FieldUnit eInUnit, MapUnit eOutUnit)
{
if ( eInUnit == FieldUnit::PERCENT ||
eInUnit == FieldUnit::CUSTOM ||
eInUnit == FieldUnit::NONE ||
eInUnit == FieldUnit::DEGREE ||
eInUnit == FieldUnit::SECOND ||
eInUnit == FieldUnit::MILLISECOND ||
eInUnit == FieldUnit::PIXEL ||
eOutUnit == MapUnit::MapPixel ||
eOutUnit == MapUnit::MapSysFont ||
eOutUnit == MapUnit::MapAppFont ||
eOutUnit == MapUnit::MapRelative )
{
OSL_FAIL( "invalid parameters" );
return nValue;
}
tools::Long nDecDigits = nDigits;
FieldUnit eFieldUnit = ImplMap2FieldUnit( eOutUnit, nDecDigits );
if ( nDecDigits < 0 )
{
nValue *= ImplPower10(-nDecDigits);
}
else
{
nValue /= ImplPower10(nDecDigits);
}
if ( eFieldUnit != eInUnit )
{
const o3tl::Length eFrom = FieldToO3tlLength(eInUnit, o3tl::Length::invalid);
const o3tl::Length eTo = FieldToO3tlLength(eFieldUnit, o3tl::Length::invalid);
if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid)
nValue = o3tl::convert(nValue, eFrom, eTo);
}
return nValue;
}
}
namespace vcl
{
bool TextToValue(const OUString& rStr, double& rValue, sal_Int64 nBaseValue,
sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrapper, FieldUnit eUnit)
{
// Get value
sal_Int64 nValue;
if ( !ImplNumericGetValue( rStr, nValue, nDecDigits, rLocaleDataWrapper ) )
return false;
// Determine unit
FieldUnit eEntryUnit = ImplMetricGetUnit( rStr );
// Recalculate unit
// caution: conversion to double loses precision
rValue = vcl::ConvertDoubleValue(static_cast<double>(nValue), nBaseValue, nDecDigits, eEntryUnit, eUnit);
return true;
}
}
void MetricFormatter::ImplMetricReformat( const OUString& rStr, double& rValue, OUString& rOutStr )
{
if (!vcl::TextToValue(rStr, rValue, 0, GetDecimalDigits(), ImplGetLocaleDataWrapper(), meUnit))
return;
double nTempVal = rValue;
// caution: precision loss in double cast
if ( nTempVal > GetMax() )
nTempVal = static_cast<double>(GetMax());
else if ( nTempVal < GetMin())
nTempVal = static_cast<double>(GetMin());
rOutStr = CreateFieldText( static_cast<sal_Int64>(nTempVal) );
}
MetricFormatter::MetricFormatter(Edit* pEdit)
: NumericFormatter(pEdit)
, meUnit(FieldUnit::NONE)
{
}
MetricFormatter::~MetricFormatter()
{
}
void MetricFormatter::SetUnit( FieldUnit eNewUnit )
{
if (eNewUnit == FieldUnit::MM_100TH)
{
SetDecimalDigits( GetDecimalDigits() + 2 );
meUnit = FieldUnit::MM;
}
else
meUnit = eNewUnit;
ReformatAll();
}
void MetricFormatter::SetCustomUnitText( const OUString& rStr )
{
maCustomUnitText = rStr;
ReformatAll();
}
void MetricFormatter::SetValue( sal_Int64 nNewValue, FieldUnit eInUnit )
{
SetUserValue( nNewValue, eInUnit );
}
OUString MetricFormatter::CreateFieldText( sal_Int64 nValue ) const
{
//whether percent is separated from its number is locale
//specific, pawn it off to icu to decide
if (meUnit == FieldUnit::PERCENT)
{
double dValue = nValue;
dValue /= ImplPower10(GetDecimalDigits());
return unicode::formatPercent(dValue, GetLanguageTag());
}
OUString aStr = NumericFormatter::CreateFieldText( nValue );
if( meUnit == FieldUnit::CUSTOM )
aStr += maCustomUnitText;
else
{
OUString aSuffix = ImplMetricToString( meUnit );
if (meUnit != FieldUnit::NONE && meUnit != FieldUnit::DEGREE && meUnit != FieldUnit::INCH && meUnit != FieldUnit::FOOT)
aStr += " ";
if (meUnit == FieldUnit::INCH)
{
OUString sDoublePrime = u"\u2033"_ustr;
if (aSuffix != "\"" && aSuffix != sDoublePrime)
aStr += " ";
else
aSuffix = sDoublePrime;
}
else if (meUnit == FieldUnit::FOOT)
{
OUString sPrime = u"\u2032"_ustr;
if (aSuffix != "'" && aSuffix != sPrime)
aStr += " ";
else
aSuffix = sPrime;
}
assert(meUnit != FieldUnit::PERCENT);
aStr += aSuffix;
}
return aStr;
}
void MetricFormatter::SetUserValue( sal_Int64 nNewValue, FieldUnit eInUnit )
{
// convert to previously configured units
nNewValue = vcl::ConvertValue( nNewValue, 0, GetDecimalDigits(), eInUnit, meUnit );
NumericFormatter::SetUserValue( nNewValue );
}
sal_Int64 MetricFormatter::GetValueFromStringUnit(const OUString& rStr, FieldUnit eOutUnit) const
{
double nTempValue;
// caution: precision loss in double cast
if (!vcl::TextToValue(rStr, nTempValue, 0, GetDecimalDigits(), ImplGetLocaleDataWrapper(), meUnit))
nTempValue = static_cast<double>(mnLastValue);
// caution: precision loss in double cast
if (nTempValue > mnMax)
nTempValue = static_cast<double>(mnMax);
else if (nTempValue < mnMin)
nTempValue = static_cast<double>(mnMin);
// convert to requested units
return vcl::ConvertValue(static_cast<sal_Int64>(nTempValue), 0, GetDecimalDigits(), meUnit, eOutUnit);
}
sal_Int64 MetricFormatter::GetValueFromString(const OUString& rStr) const
{
return GetValueFromStringUnit(rStr, FieldUnit::NONE);
}
sal_Int64 MetricFormatter::GetValue( FieldUnit eOutUnit ) const
{
return GetField() ? GetValueFromStringUnit(GetField()->GetText(), eOutUnit) : 0;
}
void MetricFormatter::SetValue( sal_Int64 nValue )
{
// Implementation not inline, because it is a virtual Function
SetValue( nValue, FieldUnit::NONE );
}
void MetricFormatter::SetMin( sal_Int64 nNewMin, FieldUnit eInUnit )
{
// convert to requested units
NumericFormatter::SetMin(vcl::ConvertValue(nNewMin, 0, GetDecimalDigits(), eInUnit, meUnit));
}
sal_Int64 MetricFormatter::GetMin( FieldUnit eOutUnit ) const
{
// convert to requested units
return vcl::ConvertValue(NumericFormatter::GetMin(), 0, GetDecimalDigits(), meUnit, eOutUnit);
}
void MetricFormatter::SetMax( sal_Int64 nNewMax, FieldUnit eInUnit )
{
// convert to requested units
NumericFormatter::SetMax(vcl::ConvertValue(nNewMax, 0, GetDecimalDigits(), eInUnit, meUnit));
}
sal_Int64 MetricFormatter::GetMax( FieldUnit eOutUnit ) const
{
// convert to requested units
return vcl::ConvertValue(NumericFormatter::GetMax(), 0, GetDecimalDigits(), meUnit, eOutUnit);
}
void MetricFormatter::Reformat()
{
if ( !GetField() )
return;
OUString aText = GetField()->GetText();
OUString aStr;
// caution: precision loss in double cast
double nTemp = static_cast<double>(mnLastValue);
ImplMetricReformat( aText, nTemp, aStr );
mnLastValue = static_cast<sal_Int64>(nTemp);
if ( !aStr.isEmpty() )
{
ImplSetText( aStr );
}
else
SetValue( mnLastValue );
}
sal_Int64 MetricFormatter::GetCorrectedValue( FieldUnit eOutUnit ) const
{
// convert to requested units
return vcl::ConvertValue(0/*nCorrectedValue*/, 0, GetDecimalDigits(),
meUnit, eOutUnit);
}
MetricField::MetricField(vcl::Window* pParent, WinBits nWinStyle)
: SpinField(pParent, nWinStyle, WindowType::METRICFIELD)
, MetricFormatter(this)
{
Reformat();
}
void MetricField::dispose()
{
ClearField();
SpinField::dispose();
}
Size MetricField::CalcMinimumSize() const
{
return calcMinimumSize(*this, *this);
}
bool MetricField::set_property(const OUString &rKey, const OUString &rValue)
{
if (rKey == "digits")
SetDecimalDigits(rValue.toInt32());
else if (rKey == "spin-size")
SetSpinSize(rValue.toInt32());
else
return SpinField::set_property(rKey, rValue);
return true;
}
void MetricField::SetUnit( FieldUnit nNewUnit )
{
sal_Int64 nRawMax = GetMax( nNewUnit );
sal_Int64 nMax = Denormalize( nRawMax );
sal_Int64 nMin = Denormalize( GetMin( nNewUnit ) );
sal_Int64 nFirst = Denormalize( GetFirst( nNewUnit ) );
sal_Int64 nLast = Denormalize( GetLast( nNewUnit ) );
MetricFormatter::SetUnit( nNewUnit );
SetMax( Normalize( nMax ), nNewUnit );
SetMin( Normalize( nMin ), nNewUnit );
SetFirst( Normalize( nFirst ), nNewUnit );
SetLast( Normalize( nLast ), nNewUnit );
}
void MetricField::SetFirst( sal_Int64 nNewFirst, FieldUnit eInUnit )
{
// convert
nNewFirst = vcl::ConvertValue(nNewFirst, 0, GetDecimalDigits(), eInUnit, meUnit);
mnFirst = nNewFirst;
}
sal_Int64 MetricField::GetFirst( FieldUnit eOutUnit ) const
{
// convert
return vcl::ConvertValue(mnFirst, 0, GetDecimalDigits(), meUnit, eOutUnit);
}
void MetricField::SetLast( sal_Int64 nNewLast, FieldUnit eInUnit )
{
// convert
nNewLast = vcl::ConvertValue(nNewLast, 0, GetDecimalDigits(), eInUnit, meUnit);
mnLast = nNewLast;
}
sal_Int64 MetricField::GetLast( FieldUnit eOutUnit ) const
{
// convert
return vcl::ConvertValue(mnLast, 0, GetDecimalDigits(), meUnit, eOutUnit);
}
bool MetricField::PreNotify( NotifyEvent& rNEvt )
{
if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
{
if ( ImplMetricProcessKeyInput( *rNEvt.GetKeyEvent(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
return true;
}
return SpinField::PreNotify( rNEvt );
}
bool MetricField::EventNotify( NotifyEvent& rNEvt )
{
if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
MarkToBeReformatted( false );
else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
{
if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
Reformat();
}
return SpinField::EventNotify( rNEvt );
}
void MetricField::DataChanged( const DataChangedEvent& rDCEvt )
{
SpinField::DataChanged( rDCEvt );
if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
{
OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
ImplResetLocaleDataWrapper();
OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
ReformatAll();
}
}
void MetricField::Modify()
{
MarkToBeReformatted( true );
SpinField::Modify();
}
void MetricField::Up()
{
FieldUp();
SpinField::Up();
}
void MetricField::Down()
{
FieldDown();
SpinField::Down();
}
void MetricField::First()
{
FieldFirst();
SpinField::First();
}
void MetricField::Last()
{
FieldLast();
SpinField::Last();
}
void MetricField::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
{
SpinField::DumpAsPropertyTree(rJsonWriter);
rJsonWriter.put("min", GetMin());
rJsonWriter.put("max", GetMax());
rJsonWriter.put("unit", FieldUnitToString(GetUnit()));
OUString sValue = Application::GetSettings().GetNeutralLocaleDataWrapper().
getNum(GetValue(), GetDecimalDigits(), false, false);
rJsonWriter.put("value", sValue);
}
FactoryFunction MetricField::GetUITestFactory() const
{
return MetricFieldUIObject::create;
}
MetricBox::MetricBox(vcl::Window* pParent, WinBits nWinStyle)
: ComboBox(pParent, nWinStyle)
, MetricFormatter(this)
{
Reformat();
}
void MetricBox::dispose()
{
ClearField();
ComboBox::dispose();
}
Size MetricBox::CalcMinimumSize() const
{
Size aRet(calcMinimumSize(*this, *this));
if (IsDropDownBox())
{
Size aComboSugg(ComboBox::CalcMinimumSize());
aRet.setWidth( std::max(aRet.Width(), aComboSugg.Width()) );
aRet.setHeight( std::max(aRet.Height(), aComboSugg.Height()) );
}
return aRet;
}
bool MetricBox::PreNotify( NotifyEvent& rNEvt )
{
if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
{
if ( ImplMetricProcessKeyInput( *rNEvt.GetKeyEvent(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
return true;
}
return ComboBox::PreNotify( rNEvt );
}
bool MetricBox::EventNotify( NotifyEvent& rNEvt )
{
if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
MarkToBeReformatted( false );
else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
{
if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
Reformat();
}
return ComboBox::EventNotify( rNEvt );
}
void MetricBox::DataChanged( const DataChangedEvent& rDCEvt )
{
ComboBox::DataChanged( rDCEvt );
if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
{
OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
ImplResetLocaleDataWrapper();
OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
ReformatAll();
}
}
void MetricBox::Modify()
{
MarkToBeReformatted( true );
ComboBox::Modify();
}
void MetricBox::ReformatAll()
{
double nValue;
OUString aStr;
SetUpdateMode( false );
sal_Int32 nEntryCount = GetEntryCount();
for ( sal_Int32 i=0; i < nEntryCount; i++ )
{
ImplMetricReformat( GetEntry( i ), nValue, aStr );
RemoveEntryAt(i);
InsertEntry( aStr, i );
}
MetricFormatter::Reformat();
SetUpdateMode( true );
}
static bool ImplCurrencyProcessKeyInput( const KeyEvent& rKEvt,
bool bUseThousandSep, const LocaleDataWrapper& rWrapper )
{
// no strict format set; therefore allow all characters
return ImplNumericProcessKeyInput( rKEvt, false, bUseThousandSep, rWrapper );
}
static bool ImplCurrencyGetValue( const OUString& rStr, sal_Int64& rValue,
sal_uInt16 nDecDigits, const LocaleDataWrapper& rWrapper )
{
// fetch number
return ImplNumericGetValue( rStr, rValue, nDecDigits, rWrapper, true );
}
void CurrencyFormatter::ImplCurrencyReformat( const OUString& rStr, OUString& rOutStr )
{
sal_Int64 nValue;
if ( !ImplNumericGetValue( rStr, nValue, GetDecimalDigits(), ImplGetLocaleDataWrapper(), true ) )
return;
sal_Int64 nTempVal = nValue;
if ( nTempVal > GetMax() )
nTempVal = GetMax();
else if ( nTempVal < GetMin())
nTempVal = GetMin();
rOutStr = CreateFieldText( nTempVal );
}
CurrencyFormatter::CurrencyFormatter(Edit* pField)
: NumericFormatter(pField)
{
}
CurrencyFormatter::~CurrencyFormatter()
{
}
void CurrencyFormatter::SetValue( sal_Int64 nNewValue )
{
SetUserValue( nNewValue );
SetEmptyFieldValueData( false );
}
OUString CurrencyFormatter::CreateFieldText( sal_Int64 nValue ) const
{
return ImplGetLocaleDataWrapper().getCurr( nValue, GetDecimalDigits(),
ImplGetLocaleDataWrapper().getCurrSymbol(),
IsUseThousandSep() );
}
sal_Int64 CurrencyFormatter::GetValueFromString(const OUString& rStr) const
{
sal_Int64 nTempValue;
if ( ImplCurrencyGetValue( rStr, nTempValue, GetDecimalDigits(), ImplGetLocaleDataWrapper() ) )
{
return ClipAgainstMinMax(nTempValue);
}
else
return mnLastValue;
}
void CurrencyFormatter::Reformat()
{
if ( !GetField() )
return;
OUString aStr;
ImplCurrencyReformat( GetField()->GetText(), aStr );
if ( !aStr.isEmpty() )
{
ImplSetText( aStr );
sal_Int64 nTemp = mnLastValue;
ImplCurrencyGetValue( aStr, nTemp, GetDecimalDigits(), ImplGetLocaleDataWrapper() );
mnLastValue = nTemp;
}
else
SetValue( mnLastValue );
}
CurrencyField::CurrencyField(vcl::Window* pParent, WinBits nWinStyle)
: SpinField(pParent, nWinStyle)
, CurrencyFormatter(this)
{
Reformat();
}
void CurrencyField::dispose()
{
ClearField();
SpinField::dispose();
}
bool CurrencyField::PreNotify( NotifyEvent& rNEvt )
{
if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
{
if ( ImplCurrencyProcessKeyInput( *rNEvt.GetKeyEvent(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
return true;
}
return SpinField::PreNotify( rNEvt );
}
bool CurrencyField::EventNotify( NotifyEvent& rNEvt )
{
if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
MarkToBeReformatted( false );
else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
{
if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
Reformat();
}
return SpinField::EventNotify( rNEvt );
}
void CurrencyField::DataChanged( const DataChangedEvent& rDCEvt )
{
SpinField::DataChanged( rDCEvt );
if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
{
OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
ImplResetLocaleDataWrapper();
OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
ReformatAll();
}
}
void CurrencyField::Modify()
{
MarkToBeReformatted( true );
SpinField::Modify();
}
void CurrencyField::Up()
{
FieldUp();
SpinField::Up();
}
void CurrencyField::Down()
{
FieldDown();
SpinField::Down();
}
void CurrencyField::First()
{
FieldFirst();
SpinField::First();
}
void CurrencyField::Last()
{
FieldLast();
SpinField::Last();
}
CurrencyBox::CurrencyBox(vcl::Window* pParent, WinBits nWinStyle)
: ComboBox(pParent, nWinStyle)
, CurrencyFormatter(this)
{
Reformat();
}
void CurrencyBox::dispose()
{
ClearField();
ComboBox::dispose();
}
bool CurrencyBox::PreNotify( NotifyEvent& rNEvt )
{
if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
{
if ( ImplCurrencyProcessKeyInput( *rNEvt.GetKeyEvent(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
return true;
}
return ComboBox::PreNotify( rNEvt );
}
bool CurrencyBox::EventNotify( NotifyEvent& rNEvt )
{
if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
MarkToBeReformatted( false );
else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
{
if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
Reformat();
}
return ComboBox::EventNotify( rNEvt );
}
void CurrencyBox::DataChanged( const DataChangedEvent& rDCEvt )
{
ComboBox::DataChanged( rDCEvt );
if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
{
OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
ImplResetLocaleDataWrapper();
OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
ReformatAll();
}
}
void CurrencyBox::Modify()
{
MarkToBeReformatted( true );
ComboBox::Modify();
}
void CurrencyBox::ReformatAll()
{
OUString aStr;
SetUpdateMode( false );
sal_Int32 nEntryCount = GetEntryCount();
for ( sal_Int32 i=0; i < nEntryCount; i++ )
{
ImplCurrencyReformat( GetEntry( i ), aStr );
RemoveEntryAt(i);
InsertEntry( aStr, i );
}
CurrencyFormatter::Reformat();
SetUpdateMode( true );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'remove' is required to be utilized.
↑ V530 The return value of function 'remove' is required to be utilized.
↑ V530 The return value of function 'remove' is required to be utilized.
↑ V530 The return value of function 'remove' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'truncateToLength' is required to be utilized.
↑ V530 The return value of function 'padToLength' is required to be utilized.
↑ V530 The return value of function 'padToLength' is required to be utilized.
↑ V530 The return value of function 'padToLength' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'padToLength' is required to be utilized.
↑ V530 The return value of function 'insert' is required to be utilized.
↑ V1053 Calling the 'ReformatAll' virtual function in the constructor may lead to unexpected result at runtime.
↑ V1053 Calling the 'Reformat' virtual function in the constructor may lead to unexpected result at runtime.
↑ V636 The 'nDiv / 2' expression was implicitly cast from 'long' type to 'double' type. Consider utilizing an explicit type cast to avoid the loss of a fractional part. An example: double A = (double)(X) / Y;.