bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / source / control / fmtfield.cxx
blobfc7bdfee6f58540864eb96f5182a830c6768fcb8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <tools/debug.hxx>
21 #include <boost/property_tree/json_parser.hpp>
22 #include <comphelper/processfactory.hxx>
23 #include <comphelper/string.hxx>
24 #include <unotools/localedatawrapper.hxx>
25 #include <vcl/builder.hxx>
26 #include <vcl/event.hxx>
27 #include <vcl/settings.hxx>
28 #include <vcl/commandevent.hxx>
29 #include <svl/zformat.hxx>
30 #include <vcl/toolkit/fmtfield.hxx>
31 #include <vcl/uitest/uiobject.hxx>
32 #include <vcl/uitest/formattedfielduiobject.hxx>
33 #include <vcl/weld.hxx>
34 #include <i18nlangtag/languagetag.hxx>
35 #include <unotools/syslocale.hxx>
36 #include <limits>
37 #include <map>
38 #include <rtl/math.hxx>
39 #include <rtl/ustrbuf.hxx>
40 #include <sal/log.hxx>
41 #include <svl/numformat.hxx>
42 #include <osl/diagnose.h>
43 #include <tools/json_writer.hxx>
45 using namespace ::com::sun::star::lang;
46 using namespace ::com::sun::star::util;
48 // hmm. No support for regular expression. Well, I always (not really :) wanted to write a finite automat
49 // so here comes a finite automat ...
51 namespace validation
53 static void lcl_insertStopTransition( StateTransitions& _rRow )
55 _rRow.insert( Transition( '_', END ) );
58 static void lcl_insertStartExponentTransition( StateTransitions& _rRow )
60 _rRow.insert( Transition( 'e', EXPONENT_START ) );
63 static void lcl_insertSignTransitions( StateTransitions& _rRow, const State eNextState )
65 _rRow.insert( Transition( '-', eNextState ) );
66 _rRow.insert( Transition( '+', eNextState ) );
69 static void lcl_insertDigitTransitions( StateTransitions& _rRow, const State eNextState )
71 for ( sal_Unicode aChar = '0'; aChar <= '9'; ++aChar )
72 _rRow.insert( Transition( aChar, eNextState ) );
75 static void lcl_insertCommonPreCommaTransitions( StateTransitions& _rRow, const sal_Unicode _cThSep, const sal_Unicode _cDecSep )
77 // digits are allowed
78 lcl_insertDigitTransitions( _rRow, DIGIT_PRE_COMMA );
80 // the thousand separator is allowed
81 _rRow.insert( Transition( _cThSep, DIGIT_PRE_COMMA ) );
83 // a comma is allowed
84 _rRow.insert( Transition( _cDecSep, DIGIT_POST_COMMA ) );
87 NumberValidator::NumberValidator( const sal_Unicode _cThSep, const sal_Unicode _cDecSep )
89 // build up our transition table
91 // how to proceed from START
93 StateTransitions& rRow = m_aTransitions[ START ];
94 rRow.insert( Transition( '_', NUM_START ) );
95 // if we encounter the normalizing character, we want to proceed with the number
98 // how to proceed from NUM_START
100 StateTransitions& rRow = m_aTransitions[ NUM_START ];
102 // a sign is allowed
103 lcl_insertSignTransitions( rRow, DIGIT_PRE_COMMA );
105 // common transitions for the two pre-comma states
106 lcl_insertCommonPreCommaTransitions( rRow, _cThSep, _cDecSep );
108 // the exponent may start here
109 // (this would mean string like "_+e10_", but this is a valid fragment, though no valid number)
110 lcl_insertStartExponentTransition( rRow );
113 // how to proceed from DIGIT_PRE_COMMA
115 StateTransitions& rRow = m_aTransitions[ DIGIT_PRE_COMMA ];
117 // common transitions for the two pre-comma states
118 lcl_insertCommonPreCommaTransitions( rRow, _cThSep, _cDecSep );
120 // the exponent may start here
121 lcl_insertStartExponentTransition( rRow );
123 // the final transition indicating the end of the string
124 // (if there is no comma and no post-comma, then the string may end here)
125 lcl_insertStopTransition( rRow );
128 // how to proceed from DIGIT_POST_COMMA
130 StateTransitions& rRow = m_aTransitions[ DIGIT_POST_COMMA ];
132 // there might be digits, which would keep the state at DIGIT_POST_COMMA
133 lcl_insertDigitTransitions( rRow, DIGIT_POST_COMMA );
135 // the exponent may start here
136 lcl_insertStartExponentTransition( rRow );
138 // the string may end here
139 lcl_insertStopTransition( rRow );
142 // how to proceed from EXPONENT_START
144 StateTransitions& rRow = m_aTransitions[ EXPONENT_START ];
146 // there may be a sign
147 lcl_insertSignTransitions( rRow, EXPONENT_DIGIT );
149 // there may be digits
150 lcl_insertDigitTransitions( rRow, EXPONENT_DIGIT );
152 // the string may end here
153 lcl_insertStopTransition( rRow );
156 // how to proceed from EXPONENT_DIGIT
158 StateTransitions& rRow = m_aTransitions[ EXPONENT_DIGIT ];
160 // there may be digits
161 lcl_insertDigitTransitions( rRow, EXPONENT_DIGIT );
163 // the string may end here
164 lcl_insertStopTransition( rRow );
167 // how to proceed from END
169 /*StateTransitions& rRow =*/ m_aTransitions[ EXPONENT_DIGIT ];
170 // no valid transition to leave this state
171 // (note that we, for consistency, nevertheless want to have a row in the table)
175 bool NumberValidator::implValidateNormalized( const OUString& _rText )
177 const sal_Unicode* pCheckPos = _rText.getStr();
178 State eCurrentState = START;
180 while ( END != eCurrentState )
182 // look up the transition row for the current state
183 TransitionTable::const_iterator aRow = m_aTransitions.find( eCurrentState );
184 DBG_ASSERT( m_aTransitions.end() != aRow,
185 "NumberValidator::implValidateNormalized: invalid transition table (row not found)!" );
187 if ( m_aTransitions.end() != aRow )
189 // look up the current character in this row
190 StateTransitions::const_iterator aTransition = aRow->second.find( *pCheckPos );
191 if ( aRow->second.end() != aTransition )
193 // there is a valid transition for this character
194 eCurrentState = aTransition->second;
195 ++pCheckPos;
196 continue;
200 // if we're here, there is no valid transition
201 break;
204 DBG_ASSERT( ( END != eCurrentState ) || ( 0 == *pCheckPos ),
205 "NumberValidator::implValidateNormalized: inconsistency!" );
206 // if we're at END, then the string should be done, too - the string should be normalized, means ending
207 // a "_" and not containing any other "_" (except at the start), and "_" is the only possibility
208 // to reach the END state
210 // the string is valid if and only if we reached the final state
211 return ( END == eCurrentState );
214 bool NumberValidator::isValidNumericFragment( std::u16string_view _rText )
216 if ( _rText.empty() )
217 // empty strings are always allowed
218 return true;
220 // normalize the string
221 OUString sNormalized = OUString::Concat("_") + _rText + "_";
223 return implValidateNormalized( sNormalized );
227 SvNumberFormatter* Formatter::StaticFormatter::s_cFormatter = nullptr;
228 sal_uLong Formatter::StaticFormatter::s_nReferences = 0;
230 SvNumberFormatter* Formatter::StaticFormatter::GetFormatter()
232 if (!s_cFormatter)
234 // get the Office's locale and translate
235 LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
236 s_cFormatter = new SvNumberFormatter(
237 ::comphelper::getProcessComponentContext(),
238 eSysLanguage);
240 return s_cFormatter;
243 Formatter::StaticFormatter::StaticFormatter()
245 ++s_nReferences;
248 Formatter::StaticFormatter::~StaticFormatter()
250 if (--s_nReferences == 0)
252 delete s_cFormatter;
253 s_cFormatter = nullptr;
257 Formatter::Formatter()
258 :m_aLastSelection(0,0)
259 ,m_dMinValue(0)
260 ,m_dMaxValue(0)
261 ,m_bHasMin(false)
262 ,m_bHasMax(false)
263 ,m_bWrapOnLimits(false)
264 ,m_bStrictFormat(true)
265 ,m_bEnableEmptyField(true)
266 ,m_bAutoColor(false)
267 ,m_bEnableNaN(false)
268 ,m_bDisableRemainderFactor(false)
269 ,m_bDefaultValueSet(false)
270 ,m_ValueState(valueDirty)
271 ,m_dCurrentValue(0)
272 ,m_dDefaultValue(0)
273 ,m_nFormatKey(0)
274 ,m_pFormatter(nullptr)
275 ,m_dSpinSize(1)
276 ,m_dSpinFirst(-1000000)
277 ,m_dSpinLast(1000000)
278 ,m_bTreatAsNumber(true)
279 ,m_pLastOutputColor(nullptr)
280 ,m_bUseInputStringForFormatting(false)
284 Formatter::~Formatter()
288 void Formatter::SetFieldText(const OUString& rStr, const Selection& rNewSelection)
290 SetEntryText(rStr, rNewSelection);
291 m_ValueState = valueDirty;
294 void Formatter::SetTextFormatted(const OUString& rStr)
296 SAL_INFO_IF(GetOrCreateFormatter()->IsTextFormat(m_nFormatKey), "svtools",
297 "FormattedField::SetTextFormatted : valid only with text formats !");
299 m_sCurrentTextValue = rStr;
301 OUString sFormatted;
302 double dNumber = 0.0;
303 // IsNumberFormat changes the format key parameter
304 sal_uInt32 nTempFormatKey = static_cast< sal_uInt32 >( m_nFormatKey );
305 if( IsUsingInputStringForFormatting() &&
306 GetOrCreateFormatter()->IsNumberFormat(m_sCurrentTextValue, nTempFormatKey, dNumber) )
308 GetOrCreateFormatter()->GetInputLineString(dNumber, m_nFormatKey, sFormatted);
310 else
312 GetOrCreateFormatter()->GetOutputString(m_sCurrentTextValue,
313 m_nFormatKey,
314 sFormatted,
315 &m_pLastOutputColor);
318 // calculate the new selection
319 Selection aSel(GetEntrySelection());
320 Selection aNewSel(aSel);
321 aNewSel.Normalize();
322 sal_Int32 nNewLen = sFormatted.getLength();
323 sal_Int32 nCurrentLen = GetEntryText().getLength();
324 if ((nNewLen > nCurrentLen) && (aNewSel.Max() == nCurrentLen))
325 { // the new text is longer and the cursor was behind the last char (of the old text)
326 if (aNewSel.Min() == 0)
327 { // the whole text was selected -> select the new text on the whole, too
328 aNewSel.Max() = nNewLen;
329 if (!nCurrentLen)
330 { // there wasn't really a previous selection (as there was no previous text), we're setting a new one -> check the selection options
331 SelectionOptions nSelOptions = GetEntrySelectionOptions();
332 if (nSelOptions & SelectionOptions::ShowFirst)
333 { // selection should be from right to left -> swap min and max
334 aNewSel.Min() = aNewSel.Max();
335 aNewSel.Max() = 0;
339 else if (aNewSel.Max() == aNewSel.Min())
340 { // there was no selection -> set the cursor behind the new last char
341 aNewSel.Max() = nNewLen;
342 aNewSel.Min() = nNewLen;
345 else if (aNewSel.Max() > nNewLen)
346 aNewSel.Max() = nNewLen;
347 else
348 aNewSel = aSel; // don't use the justified version
349 SetEntryText(sFormatted, aNewSel);
350 m_ValueState = valueString;
353 OUString const & Formatter::GetTextValue() const
355 if (m_ValueState != valueString )
357 const_cast<Formatter*>(this)->m_sCurrentTextValue = GetEntryText();
358 const_cast<Formatter*>(this)->m_ValueState = valueString;
360 return m_sCurrentTextValue;
363 void Formatter::EnableNotANumber(bool _bEnable)
365 if ( m_bEnableNaN == _bEnable )
366 return;
368 m_bEnableNaN = _bEnable;
371 void Formatter::SetAutoColor(bool _bAutomatic)
373 if (_bAutomatic == m_bAutoColor)
374 return;
376 m_bAutoColor = _bAutomatic;
377 if (m_bAutoColor)
379 // if auto color is switched on, adjust the current text color, too
380 SetEntryTextColor(m_pLastOutputColor);
384 void Formatter::Modify(bool makeValueDirty)
386 if (!IsStrictFormat())
388 if(makeValueDirty)
389 m_ValueState = valueDirty;
390 FieldModified();
391 return;
394 OUString sCheck = GetEntryText();
395 if (CheckText(sCheck))
397 m_sLastValidText = sCheck;
398 m_aLastSelection = GetEntrySelection();
399 if(makeValueDirty)
400 m_ValueState = valueDirty;
402 else
404 ImplSetTextImpl(m_sLastValidText, &m_aLastSelection);
407 FieldModified();
410 void Formatter::ImplSetTextImpl(const OUString& rNew, Selection const * pNewSel)
412 if (m_bAutoColor)
413 SetEntryTextColor(m_pLastOutputColor);
415 if (pNewSel)
416 SetEntryText(rNew, *pNewSel);
417 else
419 Selection aSel(GetEntrySelection());
420 aSel.Normalize();
422 sal_Int32 nNewLen = rNew.getLength();
423 sal_Int32 nCurrentLen = GetEntryText().getLength();
425 if ((nNewLen > nCurrentLen) && (aSel.Max() == nCurrentLen))
426 { // new text is longer and the cursor is behind the last char
427 if (aSel.Min() == 0)
429 if (!nCurrentLen)
430 { // there wasn't really a previous selection (as there was no previous text)
431 aSel.Max() = 0;
433 else
434 { // the whole text was selected -> select the new text on the whole, too
435 aSel.Max() = nNewLen;
438 else if (aSel.Max() == aSel.Min())
439 { // there was no selection -> set the cursor behind the new last char
440 aSel.Max() = nNewLen;
441 aSel.Min() = nNewLen;
444 else if (aSel.Max() > nNewLen)
445 aSel.Max() = nNewLen;
446 SetEntryText(rNew, aSel);
449 m_ValueState = valueDirty; // not always necessary, but better re-evaluate for safety reasons
452 void Formatter::ImplSetFormatKey(sal_uLong nFormatKey)
454 m_nFormatKey = nFormatKey;
455 bool bNeedFormatter = (m_pFormatter == nullptr) && (nFormatKey != 0);
456 if (bNeedFormatter)
458 GetOrCreateFormatter(); // this creates a standard formatter
460 // It might happen that the standard formatter makes no sense here, but it takes a default
461 // format. Thus, it is possible to set one of the other standard keys (which are spanning
462 // across multiple formatters).
463 m_nFormatKey = nFormatKey;
464 // When calling SetFormatKey without a formatter, the key must be one of the standard values
465 // that is available for all formatters (and, thus, also in this new one).
466 DBG_ASSERT(m_pFormatter->GetEntry(nFormatKey) != nullptr, "FormattedField::ImplSetFormatKey : invalid format key !");
470 void Formatter::SetFormatKey(sal_uLong nFormatKey)
472 bool bNoFormatter = (m_pFormatter == nullptr);
473 ImplSetFormatKey(nFormatKey);
474 FormatChanged((bNoFormatter && (m_pFormatter != nullptr)) ? FORMAT_CHANGE_TYPE::FORMATTER : FORMAT_CHANGE_TYPE::KEYONLY);
477 void Formatter::SetFormatter(SvNumberFormatter* pFormatter, bool bResetFormat)
480 if (bResetFormat)
482 m_pFormatter = pFormatter;
484 // calc the default format key from the Office's UI locale
485 if ( m_pFormatter )
487 // get the Office's locale and translate
488 LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
489 // get the standard numeric format for this language
490 m_nFormatKey = m_pFormatter->GetStandardFormat( SvNumFormatType::NUMBER, eSysLanguage );
492 else
493 m_nFormatKey = 0;
495 else
497 LanguageType aOldLang;
498 OUString sOldFormat = GetFormat(aOldLang);
500 sal_uInt32 nDestKey = pFormatter->TestNewString(sOldFormat);
501 if (nDestKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
503 // language of the new formatter
504 const SvNumberformat* pDefaultEntry = pFormatter->GetEntry(0);
505 LanguageType aNewLang = pDefaultEntry ? pDefaultEntry->GetLanguage() : LANGUAGE_DONTKNOW;
507 // convert the old format string into the new language
508 sal_Int32 nCheckPos;
509 SvNumFormatType nType;
510 pFormatter->PutandConvertEntry(sOldFormat, nCheckPos, nType, nDestKey, aOldLang, aNewLang, true);
511 m_nFormatKey = nDestKey;
513 m_pFormatter = pFormatter;
516 FormatChanged(FORMAT_CHANGE_TYPE::FORMATTER);
519 OUString Formatter::GetFormat(LanguageType& eLang) const
521 const SvNumberformat* pFormatEntry = GetOrCreateFormatter()->GetEntry(m_nFormatKey);
522 DBG_ASSERT(pFormatEntry != nullptr, "FormattedField::GetFormat: no number format for the given format key.");
523 OUString sFormatString = pFormatEntry ? pFormatEntry->GetFormatstring() : OUString();
524 eLang = pFormatEntry ? pFormatEntry->GetLanguage() : LANGUAGE_DONTKNOW;
526 return sFormatString;
529 bool Formatter::SetFormat(const OUString& rFormatString, LanguageType eLang)
531 sal_uInt32 nNewKey = GetOrCreateFormatter()->TestNewString(rFormatString, eLang);
532 if (nNewKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
534 sal_Int32 nCheckPos;
535 SvNumFormatType nType;
536 OUString rFormat(rFormatString);
537 if (!GetOrCreateFormatter()->PutEntry(rFormat, nCheckPos, nType, nNewKey, eLang))
538 return false;
539 DBG_ASSERT(nNewKey != NUMBERFORMAT_ENTRY_NOT_FOUND, "FormattedField::SetFormatString : PutEntry returned an invalid key !");
542 if (nNewKey != m_nFormatKey)
543 SetFormatKey(nNewKey);
544 return true;
547 bool Formatter::GetThousandsSep() const
549 DBG_ASSERT(!GetOrCreateFormatter()->IsTextFormat(m_nFormatKey),
550 "FormattedField::GetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
552 bool bThousand, IsRed;
553 sal_uInt16 nPrecision, nLeadingCnt;
554 GetOrCreateFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
556 return bThousand;
559 void Formatter::SetThousandsSep(bool _bUseSeparator)
561 DBG_ASSERT(!GetOrCreateFormatter()->IsTextFormat(m_nFormatKey),
562 "FormattedField::SetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
564 // get the current settings
565 bool bThousand, IsRed;
566 sal_uInt16 nPrecision, nLeadingCnt;
567 GetOrCreateFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
568 if (bThousand == _bUseSeparator)
569 return;
571 // we need the language for the following
572 LanguageType eLang;
573 GetFormat(eLang);
575 // generate a new format ...
576 OUString sFmtDescription = GetOrCreateFormatter()->GenerateFormat(m_nFormatKey, eLang, _bUseSeparator, IsRed, nPrecision, nLeadingCnt);
577 // ... and introduce it to the formatter
578 sal_Int32 nCheckPos = 0;
579 sal_uInt32 nNewKey;
580 SvNumFormatType nType;
581 GetOrCreateFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
583 // set the new key
584 ImplSetFormatKey(nNewKey);
585 FormatChanged(FORMAT_CHANGE_TYPE::THOUSANDSSEP);
588 sal_uInt16 Formatter::GetDecimalDigits() const
590 DBG_ASSERT(!GetOrCreateFormatter()->IsTextFormat(m_nFormatKey),
591 "FormattedField::GetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
593 bool bThousand, IsRed;
594 sal_uInt16 nPrecision, nLeadingCnt;
595 GetOrCreateFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
597 return nPrecision;
600 void Formatter::SetDecimalDigits(sal_uInt16 _nPrecision)
602 DBG_ASSERT(!GetOrCreateFormatter()->IsTextFormat(m_nFormatKey),
603 "FormattedField::SetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
605 // get the current settings
606 bool bThousand, IsRed;
607 sal_uInt16 nPrecision, nLeadingCnt;
608 GetOrCreateFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
609 if (nPrecision == _nPrecision)
610 return;
612 // we need the language for the following
613 LanguageType eLang;
614 GetFormat(eLang);
616 // generate a new format ...
617 OUString sFmtDescription = GetOrCreateFormatter()->GenerateFormat(m_nFormatKey, eLang, bThousand, IsRed, _nPrecision, nLeadingCnt);
618 // ... and introduce it to the formatter
619 sal_Int32 nCheckPos = 0;
620 sal_uInt32 nNewKey;
621 SvNumFormatType nType;
622 GetOrCreateFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
624 // set the new key
625 ImplSetFormatKey(nNewKey);
626 FormatChanged(FORMAT_CHANGE_TYPE::PRECISION);
629 void Formatter::FormatChanged(FORMAT_CHANGE_TYPE _nWhat)
631 m_pLastOutputColor = nullptr;
633 if ( (_nWhat == FORMAT_CHANGE_TYPE::FORMATTER) && m_pFormatter )
634 m_pFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_FORMAT_INTL );
636 ReFormat();
639 void Formatter::EntryLostFocus()
641 // special treatment for empty texts
642 if (GetEntryText().isEmpty())
644 if (!IsEmptyFieldEnabled())
646 if (TreatingAsNumber())
648 ImplSetValue(m_dCurrentValue, true);
649 Modify();
650 m_ValueState = valueDouble;
652 else
654 OUString sNew = GetTextValue();
655 if (!sNew.isEmpty())
656 SetTextFormatted(sNew);
657 else
658 SetTextFormatted(m_sDefaultText);
659 m_ValueState = valueString;
663 else
665 Commit();
669 void Formatter::Commit()
671 // remember the old text
672 OUString sOld(GetEntryText());
674 // do the reformat
675 ReFormat();
677 // did the text change?
678 if (GetEntryText() != sOld)
679 { // consider the field as modified,
680 // but we already have the most recent value;
681 // don't reparse it from the text
682 // (can lead to data loss when the format is lossy,
683 // as is e.g. our default date format: 2-digit year!)
684 Modify(false);
688 void Formatter::ReFormat()
690 if (!IsEmptyFieldEnabled() || !GetEntryText().isEmpty())
692 if (TreatingAsNumber())
694 double dValue = GetValue();
695 if ( m_bEnableNaN && std::isnan( dValue ) )
696 return;
697 ImplSetValue( dValue, true );
699 else
700 SetTextFormatted(GetTextValue());
704 void Formatter::SetMinValue(double dMin)
706 DBG_ASSERT(m_bTreatAsNumber, "FormattedField::SetMinValue : only to be used in numeric mode !");
708 m_dMinValue = dMin;
709 m_bHasMin = true;
710 // for checking the current value at the new border -> ImplSetValue
711 ReFormat();
714 void Formatter::SetMaxValue(double dMax)
716 DBG_ASSERT(m_bTreatAsNumber, "FormattedField::SetMaxValue : only to be used in numeric mode !");
718 m_dMaxValue = dMax;
719 m_bHasMax = true;
720 // for checking the current value at the new border -> ImplSetValue
721 ReFormat();
724 void Formatter::SetTextValue(const OUString& rText)
726 SetFieldText(rText, Selection(0, 0));
727 ReFormat();
730 void Formatter::EnableEmptyField(bool bEnable)
732 if (bEnable == m_bEnableEmptyField)
733 return;
735 m_bEnableEmptyField = bEnable;
736 if (!m_bEnableEmptyField && GetEntryText().isEmpty())
737 ImplSetValue(m_dCurrentValue, true);
740 void Formatter::ImplSetValue(double dVal, bool bForce)
742 if (m_bHasMin && (dVal<m_dMinValue))
744 dVal = m_bWrapOnLimits ? fmod(dVal + m_dMaxValue + 1 - m_dMinValue, m_dMaxValue + 1) + m_dMinValue
745 : m_dMinValue;
747 if (m_bHasMax && (dVal>m_dMaxValue))
749 dVal = m_bWrapOnLimits ? fmod(dVal - m_dMinValue, m_dMaxValue + 1) + m_dMinValue
750 : m_dMaxValue;
752 if (!bForce && (dVal == GetValue()))
753 return;
755 DBG_ASSERT(GetOrCreateFormatter() != nullptr, "FormattedField::ImplSetValue : can't set a value without a formatter !");
757 m_ValueState = valueDouble;
758 UpdateCurrentValue(dVal);
760 if (!m_aOutputHdl.IsSet() || !m_aOutputHdl.Call(nullptr))
762 OUString sNewText;
763 if (GetOrCreateFormatter()->IsTextFormat(m_nFormatKey))
765 // first convert the number as string in standard format
766 OUString sTemp;
767 GetOrCreateFormatter()->GetOutputString(dVal, 0, sTemp, &m_pLastOutputColor);
768 // then encode the string in the corresponding text format
769 GetOrCreateFormatter()->GetOutputString(sTemp, m_nFormatKey, sNewText, &m_pLastOutputColor);
771 else
773 if( IsUsingInputStringForFormatting())
775 GetOrCreateFormatter()->GetInputLineString(dVal, m_nFormatKey, sNewText);
777 else
779 GetOrCreateFormatter()->GetOutputString(dVal, m_nFormatKey, sNewText, &m_pLastOutputColor);
782 ImplSetTextImpl(sNewText, nullptr);
783 DBG_ASSERT(CheckText(sNewText), "FormattedField::ImplSetValue : formatted string doesn't match the criteria !");
786 m_ValueState = valueDouble;
789 bool Formatter::ImplGetValue(double& dNewVal)
791 dNewVal = m_dCurrentValue;
792 if (m_ValueState == valueDouble)
793 return true;
795 // tdf#155241 default to m_dDefaultValue only if explicitly set
796 // otherwise default to m_dCurrentValue
797 if (m_bDefaultValueSet)
798 dNewVal = m_dDefaultValue;
800 OUString sText(GetEntryText());
801 if (sText.isEmpty())
802 return true;
804 bool bUseExternalFormatterValue = false;
805 if (m_aInputHdl.IsSet())
807 sal_Int64 nResult;
808 auto eState = m_aInputHdl.Call(&nResult);
809 bUseExternalFormatterValue = eState != TRISTATE_INDET;
810 if (bUseExternalFormatterValue)
812 if (eState == TRISTATE_TRUE)
814 dNewVal = nResult;
815 dNewVal /= weld::SpinButton::Power10(GetDecimalDigits());
817 else
818 dNewVal = m_dCurrentValue;
822 if (!bUseExternalFormatterValue)
824 DBG_ASSERT(GetOrCreateFormatter() != nullptr, "FormattedField::ImplGetValue : can't give you a current value without a formatter !");
826 sal_uInt32 nFormatKey = m_nFormatKey; // IsNumberFormat changes the FormatKey!
828 if (GetOrCreateFormatter()->IsTextFormat(nFormatKey) && m_bTreatAsNumber)
829 // for detection of values like "1,1" in fields that are formatted as text
830 nFormatKey = 0;
832 // special treatment for percentage formatting
833 if (GetOrCreateFormatter()->GetType(m_nFormatKey) == SvNumFormatType::PERCENT)
835 // the language of our format
836 LanguageType eLanguage = m_pFormatter->GetEntry(m_nFormatKey)->GetLanguage();
837 // the default number format for this language
838 sal_uLong nStandardNumericFormat = m_pFormatter->GetStandardFormat(SvNumFormatType::NUMBER, eLanguage);
840 sal_uInt32 nTempFormat = nStandardNumericFormat;
841 double dTemp;
842 if (m_pFormatter->IsNumberFormat(sText, nTempFormat, dTemp) &&
843 SvNumFormatType::NUMBER == m_pFormatter->GetType(nTempFormat))
844 // the string is equivalent to a number formatted one (has no % sign) -> append it
845 sText += "%";
846 // (with this, an input of '3' becomes '3%', which then by the formatter is translated
847 // into 0.03. Without this, the formatter would give us the double 3 for an input '3',
848 // which equals 300 percent.
850 if (!GetOrCreateFormatter()->IsNumberFormat(sText, nFormatKey, dNewVal))
851 return false;
854 if (m_bHasMin && (dNewVal<m_dMinValue))
855 dNewVal = m_dMinValue;
856 if (m_bHasMax && (dNewVal>m_dMaxValue))
857 dNewVal = m_dMaxValue;
858 return true;
861 void Formatter::SetValue(double dVal)
863 ImplSetValue(dVal, m_ValueState != valueDouble);
866 double Formatter::GetValue()
868 if ( !ImplGetValue( m_dCurrentValue ) )
869 UpdateCurrentValue(m_bEnableNaN ? std::numeric_limits<double>::quiet_NaN() : m_dDefaultValue);
871 m_ValueState = valueDouble;
872 return m_dCurrentValue;
875 void Formatter::DisableRemainderFactor()
877 m_bDisableRemainderFactor = true;
880 void Formatter::UseInputStringForFormatting()
882 m_bUseInputStringForFormatting = true;
885 namespace
887 class FieldFormatter : public Formatter
889 private:
890 FormattedField& m_rSpinButton;
891 public:
892 FieldFormatter(FormattedField& rSpinButton)
893 : m_rSpinButton(rSpinButton)
897 // Formatter overrides
898 virtual Selection GetEntrySelection() const override
900 return m_rSpinButton.GetSelection();
903 virtual OUString GetEntryText() const override
905 return m_rSpinButton.GetText();
908 void SetEntryText(const OUString& rText, const Selection& rSel) override
910 m_rSpinButton.SpinField::SetText(rText, rSel);
913 virtual void SetEntryTextColor(const ::Color* pColor) override
915 if (pColor)
916 m_rSpinButton.SetControlForeground(*pColor);
917 else
918 m_rSpinButton.SetControlForeground();
921 virtual SelectionOptions GetEntrySelectionOptions() const override
923 return m_rSpinButton.GetSettings().GetStyleSettings().GetSelectionOptions();
926 virtual void FieldModified() override
928 m_rSpinButton.SpinField::Modify();
931 virtual void UpdateCurrentValue(double dCurrentValue) override
933 Formatter::UpdateCurrentValue(dCurrentValue);
934 m_rSpinButton.SetUpperEnabled(!m_bHasMax || dCurrentValue < m_dMaxValue);
935 m_rSpinButton.SetLowerEnabled(!m_bHasMin || dCurrentValue > m_dMinValue);
939 class DoubleNumericFormatter : public FieldFormatter
941 private:
942 DoubleNumericField& m_rNumericSpinButton;
943 public:
944 DoubleNumericFormatter(DoubleNumericField& rNumericSpinButton)
945 : FieldFormatter(rNumericSpinButton)
946 , m_rNumericSpinButton(rNumericSpinButton)
950 virtual bool CheckText(const OUString& sText) const override
952 // We'd like to implement this using the NumberFormatter::IsNumberFormat, but unfortunately, this doesn't
953 // recognize fragments of numbers (like, for instance "1e", which happens during entering e.g. "1e10")
954 // Thus, the roundabout way via a regular expression
955 return m_rNumericSpinButton.GetNumberValidator().isValidNumericFragment(sText);
958 virtual void FormatChanged(FORMAT_CHANGE_TYPE nWhat) override
960 m_rNumericSpinButton.ResetConformanceTester();
961 FieldFormatter::FormatChanged(nWhat);
965 class DoubleCurrencyFormatter : public FieldFormatter
967 private:
968 DoubleCurrencyField& m_rCurrencySpinButton;
969 bool m_bChangingFormat;
970 public:
971 DoubleCurrencyFormatter(DoubleCurrencyField& rNumericSpinButton)
972 : FieldFormatter(rNumericSpinButton)
973 , m_rCurrencySpinButton(rNumericSpinButton)
974 , m_bChangingFormat(false)
978 virtual void FormatChanged(FORMAT_CHANGE_TYPE nWhat) override
980 if (m_bChangingFormat)
982 FieldFormatter::FormatChanged(nWhat);
983 return;
986 switch (nWhat)
988 case FORMAT_CHANGE_TYPE::FORMATTER:
989 case FORMAT_CHANGE_TYPE::PRECISION:
990 case FORMAT_CHANGE_TYPE::THOUSANDSSEP:
991 // the aspects which changed don't take our currency settings into account (in fact, they most probably
992 // destroyed them)
993 m_rCurrencySpinButton.UpdateCurrencyFormat();
994 break;
995 case FORMAT_CHANGE_TYPE::KEYONLY:
996 OSL_FAIL("DoubleCurrencyField::FormatChanged : somebody modified my key !");
997 // We always build our own format from the settings we get via special methods (setCurrencySymbol etc.).
998 // Nobody but ourself should modify the format key directly!
999 break;
1000 default: break;
1003 FieldFormatter::FormatChanged(nWhat);
1006 void GuardSetFormat(const OUString& rString, LanguageType eLanguage)
1008 // set this new basic format
1009 m_bChangingFormat = true;
1010 SetFormat(rString, eLanguage);
1011 m_bChangingFormat = false;
1017 DoubleNumericField::DoubleNumericField(vcl::Window* pParent, WinBits nStyle)
1018 : FormattedField(pParent, nStyle)
1020 m_xOwnFormatter.reset(new DoubleNumericFormatter(*this));
1021 m_pFormatter = m_xOwnFormatter.get();
1022 ResetConformanceTester();
1025 DoubleNumericField::~DoubleNumericField() = default;
1027 void DoubleNumericField::ResetConformanceTester()
1029 // the thousands and the decimal separator are language dependent
1030 Formatter& rFormatter = GetFormatter();
1031 const SvNumberformat* pFormatEntry = rFormatter.GetOrCreateFormatter()->GetEntry(rFormatter.GetFormatKey());
1033 sal_Unicode cSeparatorThousand = ',';
1034 sal_Unicode cSeparatorDecimal = '.';
1035 if (pFormatEntry)
1037 LocaleDataWrapper aLocaleInfo( LanguageTag( pFormatEntry->GetLanguage()) );
1039 OUString sSeparator = aLocaleInfo.getNumThousandSep();
1040 if (!sSeparator.isEmpty())
1041 cSeparatorThousand = sSeparator[0];
1043 sSeparator = aLocaleInfo.getNumDecimalSep();
1044 if (!sSeparator.isEmpty())
1045 cSeparatorDecimal = sSeparator[0];
1048 m_pNumberValidator.reset(new validation::NumberValidator( cSeparatorThousand, cSeparatorDecimal ));
1052 DoubleCurrencyField::DoubleCurrencyField(vcl::Window* pParent, WinBits nStyle)
1053 :FormattedField(pParent, nStyle)
1055 m_xOwnFormatter.reset(new DoubleCurrencyFormatter(*this));
1056 m_pFormatter = m_xOwnFormatter.get();
1058 m_bPrependCurrSym = false;
1060 // initialize with a system currency format
1061 m_sCurrencySymbol = SvtSysLocale().GetLocaleData().getCurrSymbol();
1062 UpdateCurrencyFormat();
1065 void DoubleCurrencyField::setCurrencySymbol(const OUString& rSymbol)
1067 if (m_sCurrencySymbol == rSymbol)
1068 return;
1070 m_sCurrencySymbol = rSymbol;
1071 UpdateCurrencyFormat();
1072 m_pFormatter->FormatChanged(FORMAT_CHANGE_TYPE::CURRENCY_SYMBOL);
1075 void DoubleCurrencyField::setPrependCurrSym(bool _bPrepend)
1077 if (m_bPrependCurrSym == _bPrepend)
1078 return;
1080 m_bPrependCurrSym = _bPrepend;
1081 UpdateCurrencyFormat();
1082 m_pFormatter->FormatChanged(FORMAT_CHANGE_TYPE::CURRSYM_POSITION);
1085 void DoubleCurrencyField::UpdateCurrencyFormat()
1087 // the old settings
1088 LanguageType eLanguage;
1089 m_pFormatter->GetFormat(eLanguage);
1090 bool bThSep = m_pFormatter->GetThousandsSep();
1091 sal_uInt16 nDigits = m_pFormatter->GetDecimalDigits();
1093 // build a new format string with the base class' and my own settings
1095 /* Strangely with gcc 4.6.3 this needs a temporary LanguageTag, otherwise
1096 * there's
1097 * error: request for member 'getNumThousandSep' in 'aLocaleInfo', which is
1098 * of non-class type 'LocaleDataWrapper(LanguageTag)' */
1099 LocaleDataWrapper aLocaleInfo(( LanguageTag(eLanguage) ));
1101 OUStringBuffer sNewFormat;
1102 if (bThSep)
1104 sNewFormat.append("#" + aLocaleInfo.getNumThousandSep() + "##0");
1106 else
1107 sNewFormat.append('0');
1109 if (nDigits)
1111 sNewFormat.append(aLocaleInfo.getNumDecimalSep());
1112 comphelper::string::padToLength(sNewFormat, sNewFormat.getLength() + nDigits, '0');
1115 if (getPrependCurrSym())
1117 OUString sSymbol = getCurrencySymbol();
1118 sSymbol = comphelper::string::strip(sSymbol, ' ');
1120 OUString sTemp =
1121 "[$" + sSymbol + "] "
1122 + sNewFormat
1123 // for negative values : $ -0.00, not -$ 0.00...
1124 // (the real solution would be a possibility to choose a "positive currency format" and a "negative currency format"...
1125 // But not now... (and hey, you could take a formatted field for this...))
1126 // FS - 31.03.00 74642
1127 + ";[$"
1128 + sSymbol
1129 + "] -"
1130 + sNewFormat;
1132 sNewFormat = sTemp;
1134 else
1136 OUString sTemp = getCurrencySymbol();
1137 sTemp = comphelper::string::strip(sTemp, ' ');
1139 sNewFormat.append(" [$" + sTemp + "]");
1142 // set this new basic format
1143 static_cast<DoubleCurrencyFormatter*>(m_pFormatter)->GuardSetFormat(sNewFormat.makeStringAndClear(), eLanguage);
1146 FormattedField::FormattedField(vcl::Window* pParent, WinBits nStyle)
1147 : SpinField(pParent, nStyle, WindowType::FORMATTEDFIELD)
1148 , m_pFormatter(nullptr)
1152 void FormattedField::dispose()
1154 m_pFormatter = nullptr;
1155 m_xOwnFormatter.reset();
1156 SpinField::dispose();
1159 void FormattedField::SetText(const OUString& rStr)
1161 GetFormatter().SetFieldText(rStr, Selection(0, 0));
1164 void FormattedField::SetText(const OUString& rStr, const Selection& rNewSelection)
1166 GetFormatter().SetFieldText(rStr, rNewSelection);
1167 SetSelection(rNewSelection);
1170 bool FormattedField::set_property(const OUString &rKey, const OUString &rValue)
1172 if (rKey == "digits")
1173 GetFormatter().SetDecimalDigits(rValue.toInt32());
1174 else if (rKey == "wrap")
1175 GetFormatter().SetWrapOnLimits(toBool(rValue));
1176 else
1177 return SpinField::set_property(rKey, rValue);
1178 return true;
1181 void FormattedField::Up()
1183 Formatter& rFormatter = GetFormatter();
1184 auto nScale = weld::SpinButton::Power10(rFormatter.GetDecimalDigits());
1186 sal_Int64 nValue = std::round(rFormatter.GetValue() * nScale);
1187 sal_Int64 nSpinSize = std::round(rFormatter.GetSpinSize() * nScale);
1188 assert(nSpinSize != 0);
1189 sal_Int64 nRemainder = rFormatter.GetDisableRemainderFactor() || nSpinSize == 0 ? 0 : nValue % nSpinSize;
1190 if (nValue >= 0)
1191 nValue = (nRemainder == 0) ? nValue + nSpinSize : nValue + nSpinSize - nRemainder;
1192 else
1193 nValue = (nRemainder == 0) ? nValue + nSpinSize : nValue - nRemainder;
1195 // setValue handles under- and overflows (min/max) automatically
1196 rFormatter.SetValue(static_cast<double>(nValue) / nScale);
1197 SetModifyFlag();
1198 Modify();
1200 SpinField::Up();
1203 void FormattedField::Down()
1205 Formatter& rFormatter = GetFormatter();
1206 auto nScale = weld::SpinButton::Power10(rFormatter.GetDecimalDigits());
1208 sal_Int64 nValue = std::round(rFormatter.GetValue() * nScale);
1209 sal_Int64 nSpinSize = std::round(rFormatter.GetSpinSize() * nScale);
1210 assert(nSpinSize != 0);
1211 sal_Int64 nRemainder = rFormatter.GetDisableRemainderFactor() || nSpinSize == 0 ? 0 : nValue % nSpinSize;
1212 if (nValue >= 0)
1213 nValue = (nRemainder == 0) ? nValue - nSpinSize : nValue - nRemainder;
1214 else
1215 nValue = (nRemainder == 0) ? nValue - nSpinSize : nValue - nSpinSize - nRemainder;
1217 // setValue handles under- and overflows (min/max) automatically
1218 rFormatter.SetValue(static_cast<double>(nValue) / nScale);
1219 SetModifyFlag();
1220 Modify();
1222 SpinField::Down();
1225 void FormattedField::First()
1227 Formatter& rFormatter = GetFormatter();
1228 if (rFormatter.HasMinValue())
1230 rFormatter.SetValue(rFormatter.GetMinValue());
1231 SetModifyFlag();
1232 Modify();
1235 SpinField::First();
1238 void FormattedField::Last()
1240 Formatter& rFormatter = GetFormatter();
1241 if (rFormatter.HasMaxValue())
1243 rFormatter.SetValue(rFormatter.GetMaxValue());
1244 SetModifyFlag();
1245 Modify();
1248 SpinField::Last();
1251 void FormattedField::Modify()
1253 GetFormatter().Modify();
1256 bool FormattedField::PreNotify(NotifyEvent& rNEvt)
1258 if (rNEvt.GetType() == NotifyEventType::KEYINPUT)
1259 GetFormatter().SetLastSelection(GetSelection());
1260 return SpinField::PreNotify(rNEvt);
1263 bool FormattedField::EventNotify(NotifyEvent& rNEvt)
1265 if ((rNEvt.GetType() == NotifyEventType::KEYINPUT) && !IsReadOnly())
1267 const KeyEvent& rKEvt = *rNEvt.GetKeyEvent();
1268 sal_uInt16 nMod = rKEvt.GetKeyCode().GetModifier();
1269 switch ( rKEvt.GetKeyCode().GetCode() )
1271 case KEY_UP:
1272 case KEY_DOWN:
1273 case KEY_PAGEUP:
1274 case KEY_PAGEDOWN:
1276 Formatter& rFormatter = GetFormatter();
1277 if (!nMod && rFormatter.GetOrCreateFormatter()->IsTextFormat(rFormatter.GetFormatKey()))
1279 // the base class would translate this into calls to Up/Down/First/Last,
1280 // but we don't want this if we are text-formatted
1281 return true;
1287 if ((rNEvt.GetType() == NotifyEventType::COMMAND) && !IsReadOnly())
1289 const CommandEvent* pCommand = rNEvt.GetCommandEvent();
1290 if (pCommand->GetCommand() == CommandEventId::Wheel)
1292 const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData();
1293 Formatter& rFormatter = GetFormatter();
1294 if ((pData->GetMode() == CommandWheelMode::SCROLL) &&
1295 rFormatter.GetOrCreateFormatter()->IsTextFormat(rFormatter.GetFormatKey()))
1297 // same as above : prevent the base class from doing Up/Down-calls
1298 // (normally I should put this test into the Up/Down methods itself, shouldn't I ?)
1299 // FS - 71553 - 19.01.00
1300 return true;
1305 if (rNEvt.GetType() == NotifyEventType::LOSEFOCUS && m_pFormatter)
1306 m_pFormatter->EntryLostFocus();
1308 return SpinField::EventNotify( rNEvt );
1311 Formatter& FormattedField::GetFormatter()
1313 if (!m_pFormatter)
1315 m_xOwnFormatter.reset(new FieldFormatter(*this));
1316 m_pFormatter = m_xOwnFormatter.get();
1318 return *m_pFormatter;
1321 void FormattedField::SetFormatter(Formatter* pFormatter)
1323 m_xOwnFormatter.reset();
1324 m_pFormatter = pFormatter;
1327 // currently used by online
1328 void FormattedField::SetValueFromString(const OUString& rStr)
1330 sal_Int32 nEnd;
1331 rtl_math_ConversionStatus eStatus;
1332 Formatter& rFormatter = GetFormatter();
1333 double fValue = ::rtl::math::stringToDouble(rStr, '.', rFormatter.GetDecimalDigits(), &eStatus, &nEnd );
1335 if (eStatus == rtl_math_ConversionStatus_Ok &&
1336 nEnd == rStr.getLength())
1338 rFormatter.SetValue(fValue);
1339 SetModifyFlag();
1340 Modify();
1342 // Notify the value has changed
1343 SpinField::Up();
1345 else
1347 SAL_WARN("vcl", "fail to convert the value: " << rStr);
1351 void FormattedField::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
1353 SpinField::DumpAsPropertyTree(rJsonWriter);
1354 Formatter& rFormatter = GetFormatter();
1355 rJsonWriter.put("min", rFormatter.GetMinValue());
1356 rJsonWriter.put("max", rFormatter.GetMaxValue());
1357 rJsonWriter.put("value", rFormatter.GetValue());
1358 rJsonWriter.put("step", rFormatter.GetSpinSize());
1361 FactoryFunction FormattedField::GetUITestFactory() const
1363 return FormattedFieldUIObject::create;
1366 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */