sdext: adapt xpdfwrapper to poppler 24.12
[LibreOffice.git] / vcl / source / control / fmtfield.cxx
blobec8f5569ecab208c1a7e4f59317dc8a4f4730a30
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/field.hxx>
31 #include <vcl/toolkit/fmtfield.hxx>
32 #include <vcl/uitest/uiobject.hxx>
33 #include <vcl/uitest/formattedfielduiobject.hxx>
34 #include <vcl/weld.hxx>
35 #include <vcl/weldutils.hxx>
36 #include <i18nlangtag/languagetag.hxx>
37 #include <unotools/syslocale.hxx>
38 #include <limits>
39 #include <map>
40 #include <rtl/math.hxx>
41 #include <rtl/ustrbuf.hxx>
42 #include <sal/log.hxx>
43 #include <svl/numformat.hxx>
44 #include <osl/diagnose.h>
45 #include <tools/json_writer.hxx>
47 using namespace ::com::sun::star::lang;
49 // hmm. No support for regular expression. Well, I always (not really :) wanted to write a finite automat
50 // so here comes a finite automat ...
52 namespace validation
54 static void lcl_insertStopTransition( StateTransitions& _rRow )
56 _rRow.insert( Transition( '_', END ) );
59 static void lcl_insertStartExponentTransition( StateTransitions& _rRow )
61 _rRow.insert( Transition( 'e', EXPONENT_START ) );
64 static void lcl_insertSignTransitions( StateTransitions& _rRow, const State eNextState )
66 _rRow.insert( Transition( '-', eNextState ) );
67 _rRow.insert( Transition( '+', eNextState ) );
70 static void lcl_insertDigitTransitions( StateTransitions& _rRow, const State eNextState )
72 for ( sal_Unicode aChar = '0'; aChar <= '9'; ++aChar )
73 _rRow.insert( Transition( aChar, eNextState ) );
76 static void lcl_insertCommonPreCommaTransitions( StateTransitions& _rRow, const sal_Unicode _cThSep, const sal_Unicode _cDecSep )
78 // digits are allowed
79 lcl_insertDigitTransitions( _rRow, DIGIT_PRE_COMMA );
81 // the thousand separator is allowed
82 _rRow.insert( Transition( _cThSep, DIGIT_PRE_COMMA ) );
84 // a comma is allowed
85 _rRow.insert( Transition( _cDecSep, DIGIT_POST_COMMA ) );
88 NumberValidator::NumberValidator( const sal_Unicode _cThSep, const sal_Unicode _cDecSep )
90 // build up our transition table
92 // how to proceed from START
94 StateTransitions& rRow = m_aTransitions[ START ];
95 rRow.insert( Transition( '_', NUM_START ) );
96 // if we encounter the normalizing character, we want to proceed with the number
99 // how to proceed from NUM_START
101 StateTransitions& rRow = m_aTransitions[ NUM_START ];
103 // a sign is allowed
104 lcl_insertSignTransitions( rRow, DIGIT_PRE_COMMA );
106 // common transitions for the two pre-comma states
107 lcl_insertCommonPreCommaTransitions( rRow, _cThSep, _cDecSep );
109 // the exponent may start here
110 // (this would mean string like "_+e10_", but this is a valid fragment, though no valid number)
111 lcl_insertStartExponentTransition( rRow );
114 // how to proceed from DIGIT_PRE_COMMA
116 StateTransitions& rRow = m_aTransitions[ DIGIT_PRE_COMMA ];
118 // common transitions for the two pre-comma states
119 lcl_insertCommonPreCommaTransitions( rRow, _cThSep, _cDecSep );
121 // the exponent may start here
122 lcl_insertStartExponentTransition( rRow );
124 // the final transition indicating the end of the string
125 // (if there is no comma and no post-comma, then the string may end here)
126 lcl_insertStopTransition( rRow );
129 // how to proceed from DIGIT_POST_COMMA
131 StateTransitions& rRow = m_aTransitions[ DIGIT_POST_COMMA ];
133 // there might be digits, which would keep the state at DIGIT_POST_COMMA
134 lcl_insertDigitTransitions( rRow, DIGIT_POST_COMMA );
136 // the exponent may start here
137 lcl_insertStartExponentTransition( rRow );
139 // the string may end here
140 lcl_insertStopTransition( rRow );
143 // how to proceed from EXPONENT_START
145 StateTransitions& rRow = m_aTransitions[ EXPONENT_START ];
147 // there may be a sign
148 lcl_insertSignTransitions( rRow, EXPONENT_DIGIT );
150 // there may be digits
151 lcl_insertDigitTransitions( rRow, EXPONENT_DIGIT );
153 // the string may end here
154 lcl_insertStopTransition( rRow );
157 // how to proceed from EXPONENT_DIGIT
159 StateTransitions& rRow = m_aTransitions[ EXPONENT_DIGIT ];
161 // there may be digits
162 lcl_insertDigitTransitions( rRow, EXPONENT_DIGIT );
164 // the string may end here
165 lcl_insertStopTransition( rRow );
168 // how to proceed from END
170 /*StateTransitions& rRow =*/ m_aTransitions[ EXPONENT_DIGIT ];
171 // no valid transition to leave this state
172 // (note that we, for consistency, nevertheless want to have a row in the table)
176 bool NumberValidator::implValidateNormalized( const OUString& _rText )
178 const sal_Unicode* pCheckPos = _rText.getStr();
179 State eCurrentState = START;
181 while ( END != eCurrentState )
183 // look up the transition row for the current state
184 TransitionTable::const_iterator aRow = m_aTransitions.find( eCurrentState );
185 DBG_ASSERT( m_aTransitions.end() != aRow,
186 "NumberValidator::implValidateNormalized: invalid transition table (row not found)!" );
188 if ( m_aTransitions.end() != aRow )
190 // look up the current character in this row
191 StateTransitions::const_iterator aTransition = aRow->second.find( *pCheckPos );
192 if ( aRow->second.end() != aTransition )
194 // there is a valid transition for this character
195 eCurrentState = aTransition->second;
196 ++pCheckPos;
197 continue;
201 // if we're here, there is no valid transition
202 break;
205 DBG_ASSERT( ( END != eCurrentState ) || ( 0 == *pCheckPos ),
206 "NumberValidator::implValidateNormalized: inconsistency!" );
207 // if we're at END, then the string should be done, too - the string should be normalized, means ending
208 // a "_" and not containing any other "_" (except at the start), and "_" is the only possibility
209 // to reach the END state
211 // the string is valid if and only if we reached the final state
212 return ( END == eCurrentState );
215 bool NumberValidator::isValidNumericFragment( std::u16string_view _rText )
217 if ( _rText.empty() )
218 // empty strings are always allowed
219 return true;
221 // normalize the string
222 OUString sNormalized = OUString::Concat("_") + _rText + "_";
224 return implValidateNormalized( sNormalized );
228 SvNumberFormatter* Formatter::StaticFormatter::s_cFormatter = nullptr;
229 sal_uLong Formatter::StaticFormatter::s_nReferences = 0;
231 SvNumberFormatter* Formatter::StaticFormatter::GetFormatter()
233 if (!s_cFormatter)
235 // get the Office's locale and translate
236 LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
237 s_cFormatter = new SvNumberFormatter(
238 ::comphelper::getProcessComponentContext(),
239 eSysLanguage);
241 return s_cFormatter;
244 Formatter::StaticFormatter::StaticFormatter()
246 ++s_nReferences;
249 Formatter::StaticFormatter::~StaticFormatter()
251 if (--s_nReferences == 0)
253 delete s_cFormatter;
254 s_cFormatter = nullptr;
258 Formatter::Formatter()
259 :m_aLastSelection(0,0)
260 ,m_dMinValue(0)
261 ,m_dMaxValue(0)
262 ,m_bHasMin(false)
263 ,m_bHasMax(false)
264 ,m_bWrapOnLimits(false)
265 ,m_bStrictFormat(true)
266 ,m_bEnableEmptyField(true)
267 ,m_bAutoColor(false)
268 ,m_bEnableNaN(false)
269 ,m_bDisableRemainderFactor(false)
270 ,m_bDefaultValueSet(false)
271 ,m_ValueState(valueDirty)
272 ,m_dCurrentValue(0)
273 ,m_dDefaultValue(0)
274 ,m_nFormatKey(0)
275 ,m_pFormatter(nullptr)
276 ,m_dSpinSize(1)
277 ,m_dSpinFirst(-1000000)
278 ,m_dSpinLast(1000000)
279 ,m_bTreatAsNumber(true)
280 ,m_pLastOutputColor(nullptr)
281 ,m_bUseInputStringForFormatting(false)
285 Formatter::~Formatter()
289 void Formatter::SetFieldText(const OUString& rStr, const Selection& rNewSelection)
291 SetEntryText(rStr, rNewSelection);
292 m_ValueState = valueDirty;
295 void Formatter::SetTextFormatted(const OUString& rStr)
297 SAL_INFO_IF(GetOrCreateFormatter()->IsTextFormat(m_nFormatKey), "svtools",
298 "FormattedField::SetTextFormatted : valid only with text formats !");
300 m_sCurrentTextValue = rStr;
302 OUString sFormatted;
303 double dNumber = 0.0;
304 // IsNumberFormat changes the format key parameter
305 sal_uInt32 nTempFormatKey = static_cast< sal_uInt32 >( m_nFormatKey );
306 if( IsUsingInputStringForFormatting() &&
307 GetOrCreateFormatter()->IsNumberFormat(m_sCurrentTextValue, nTempFormatKey, dNumber) )
309 sFormatted = GetOrCreateFormatter()->GetInputLineString(dNumber, m_nFormatKey);
311 else
313 GetOrCreateFormatter()->GetOutputString(m_sCurrentTextValue,
314 m_nFormatKey,
315 sFormatted,
316 &m_pLastOutputColor);
319 // calculate the new selection
320 Selection aSel(GetEntrySelection());
321 Selection aNewSel(aSel);
322 aNewSel.Normalize();
323 sal_Int32 nNewLen = sFormatted.getLength();
324 sal_Int32 nCurrentLen = GetEntryText().getLength();
325 if ((nNewLen > nCurrentLen) && (aNewSel.Max() == nCurrentLen))
326 { // the new text is longer and the cursor was behind the last char (of the old text)
327 if (aNewSel.Min() == 0)
328 { // the whole text was selected -> select the new text on the whole, too
329 aNewSel.Max() = nNewLen;
330 if (!nCurrentLen)
331 { // there wasn't really a previous selection (as there was no previous text), we're setting a new one -> check the selection options
332 SelectionOptions nSelOptions = GetEntrySelectionOptions();
333 if (nSelOptions & SelectionOptions::ShowFirst)
334 { // selection should be from right to left -> swap min and max
335 aNewSel.Min() = aNewSel.Max();
336 aNewSel.Max() = 0;
340 else if (aNewSel.Max() == aNewSel.Min())
341 { // there was no selection -> set the cursor behind the new last char
342 aNewSel.Max() = nNewLen;
343 aNewSel.Min() = nNewLen;
346 else if (aNewSel.Max() > nNewLen)
347 aNewSel.Max() = nNewLen;
348 else
349 aNewSel = aSel; // don't use the justified version
350 SetEntryText(sFormatted, aNewSel);
351 m_ValueState = valueString;
354 OUString const & Formatter::GetTextValue() const
356 if (m_ValueState != valueString )
358 const_cast<Formatter*>(this)->m_sCurrentTextValue = GetEntryText();
359 const_cast<Formatter*>(this)->m_ValueState = valueString;
361 return m_sCurrentTextValue;
364 void Formatter::EnableNotANumber(bool _bEnable)
366 if ( m_bEnableNaN == _bEnable )
367 return;
369 m_bEnableNaN = _bEnable;
372 void Formatter::SetAutoColor(bool _bAutomatic)
374 if (_bAutomatic == m_bAutoColor)
375 return;
377 m_bAutoColor = _bAutomatic;
378 if (m_bAutoColor)
380 // if auto color is switched on, adjust the current text color, too
381 SetEntryTextColor(m_pLastOutputColor);
385 void Formatter::Modify(bool makeValueDirty)
387 if (!IsStrictFormat())
389 if(makeValueDirty)
390 m_ValueState = valueDirty;
391 FieldModified();
392 return;
395 OUString sCheck = GetEntryText();
396 if (CheckText(sCheck))
398 m_sLastValidText = sCheck;
399 m_aLastSelection = GetEntrySelection();
400 if(makeValueDirty)
401 m_ValueState = valueDirty;
403 else
405 ImplSetTextImpl(m_sLastValidText, &m_aLastSelection);
408 FieldModified();
411 void Formatter::ImplSetTextImpl(const OUString& rNew, Selection const * pNewSel)
413 if (m_bAutoColor)
414 SetEntryTextColor(m_pLastOutputColor);
416 if (pNewSel)
417 SetEntryText(rNew, *pNewSel);
418 else
420 Selection aSel(GetEntrySelection());
421 aSel.Normalize();
423 sal_Int32 nNewLen = rNew.getLength();
424 sal_Int32 nCurrentLen = GetEntryText().getLength();
426 if ((nNewLen > nCurrentLen) && (aSel.Max() == nCurrentLen))
427 { // new text is longer and the cursor is behind the last char
428 if (aSel.Min() == 0)
430 if (!nCurrentLen)
431 { // there wasn't really a previous selection (as there was no previous text)
432 aSel.Max() = 0;
434 else
435 { // the whole text was selected -> select the new text on the whole, too
436 aSel.Max() = nNewLen;
439 else if (aSel.Max() == aSel.Min())
440 { // there was no selection -> set the cursor behind the new last char
441 aSel.Max() = nNewLen;
442 aSel.Min() = nNewLen;
445 else if (aSel.Max() > nNewLen)
446 aSel.Max() = nNewLen;
447 SetEntryText(rNew, aSel);
450 m_ValueState = valueDirty; // not always necessary, but better re-evaluate for safety reasons
453 void Formatter::ImplSetFormatKey(sal_uLong nFormatKey)
455 m_nFormatKey = nFormatKey;
456 bool bNeedFormatter = (m_pFormatter == nullptr) && (nFormatKey != 0);
457 if (bNeedFormatter)
459 GetOrCreateFormatter(); // this creates a standard formatter
460 assert(m_pFormatter);
462 // It might happen that the standard formatter makes no sense here, but it takes a default
463 // format. Thus, it is possible to set one of the other standard keys (which are spanning
464 // across multiple formatters).
465 m_nFormatKey = nFormatKey;
466 // When calling SetFormatKey without a formatter, the key must be one of the standard values
467 // that is available for all formatters (and, thus, also in this new one).
468 DBG_ASSERT(m_pFormatter->GetEntry(nFormatKey) != nullptr, "FormattedField::ImplSetFormatKey : invalid format key !");
472 void Formatter::SetFormatKey(sal_uLong nFormatKey)
474 bool bNoFormatter = (m_pFormatter == nullptr);
475 ImplSetFormatKey(nFormatKey);
476 FormatChanged((bNoFormatter && (m_pFormatter != nullptr)) ? FORMAT_CHANGE_TYPE::FORMATTER : FORMAT_CHANGE_TYPE::KEYONLY);
479 void Formatter::SetFormatter(SvNumberFormatter* pFormatter, bool bResetFormat)
482 if (bResetFormat)
484 m_pFormatter = pFormatter;
486 // calc the default format key from the Office's UI locale
487 if ( m_pFormatter )
489 // get the Office's locale and translate
490 LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
491 // get the standard numeric format for this language
492 m_nFormatKey = m_pFormatter->GetStandardFormat( SvNumFormatType::NUMBER, eSysLanguage );
494 else
495 m_nFormatKey = 0;
497 else
499 LanguageType aOldLang;
500 OUString sOldFormat = GetFormat(aOldLang);
502 sal_uInt32 nDestKey = pFormatter->TestNewString(sOldFormat);
503 if (nDestKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
505 // language of the new formatter
506 const SvNumberformat* pDefaultEntry = pFormatter->GetEntry(0);
507 LanguageType aNewLang = pDefaultEntry ? pDefaultEntry->GetLanguage() : LANGUAGE_DONTKNOW;
509 // convert the old format string into the new language
510 sal_Int32 nCheckPos;
511 SvNumFormatType nType;
512 pFormatter->PutandConvertEntry(sOldFormat, nCheckPos, nType, nDestKey, aOldLang, aNewLang, true);
513 m_nFormatKey = nDestKey;
515 m_pFormatter = pFormatter;
518 FormatChanged(FORMAT_CHANGE_TYPE::FORMATTER);
521 OUString Formatter::GetFormat(LanguageType& eLang) const
523 const SvNumberformat* pFormatEntry = GetOrCreateFormatter()->GetEntry(m_nFormatKey);
524 DBG_ASSERT(pFormatEntry != nullptr, "FormattedField::GetFormat: no number format for the given format key.");
525 OUString sFormatString = pFormatEntry ? pFormatEntry->GetFormatstring() : OUString();
526 eLang = pFormatEntry ? pFormatEntry->GetLanguage() : LANGUAGE_DONTKNOW;
528 return sFormatString;
531 bool Formatter::SetFormat(const OUString& rFormatString, LanguageType eLang)
533 sal_uInt32 nNewKey = GetOrCreateFormatter()->TestNewString(rFormatString, eLang);
534 if (nNewKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
536 sal_Int32 nCheckPos;
537 SvNumFormatType nType;
538 OUString rFormat(rFormatString);
539 if (!GetOrCreateFormatter()->PutEntry(rFormat, nCheckPos, nType, nNewKey, eLang))
540 return false;
541 DBG_ASSERT(nNewKey != NUMBERFORMAT_ENTRY_NOT_FOUND, "FormattedField::SetFormatString : PutEntry returned an invalid key !");
544 if (nNewKey != m_nFormatKey)
545 SetFormatKey(nNewKey);
546 return true;
549 bool Formatter::GetThousandsSep() const
551 DBG_ASSERT(!GetOrCreateFormatter()->IsTextFormat(m_nFormatKey),
552 "FormattedField::GetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
554 bool bThousand, IsRed;
555 sal_uInt16 nPrecision, nLeadingCnt;
556 GetOrCreateFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
558 return bThousand;
561 void Formatter::SetThousandsSep(bool _bUseSeparator)
563 DBG_ASSERT(!GetOrCreateFormatter()->IsTextFormat(m_nFormatKey),
564 "FormattedField::SetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
566 // get the current settings
567 bool bThousand, IsRed;
568 sal_uInt16 nPrecision, nLeadingCnt;
569 GetOrCreateFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
570 if (bThousand == _bUseSeparator)
571 return;
573 // we need the language for the following
574 LanguageType eLang;
575 GetFormat(eLang);
577 // generate a new format ...
578 OUString sFmtDescription = GetOrCreateFormatter()->GenerateFormat(m_nFormatKey, eLang, _bUseSeparator, IsRed, nPrecision, nLeadingCnt);
579 // ... and introduce it to the formatter
580 sal_Int32 nCheckPos = 0;
581 sal_uInt32 nNewKey;
582 SvNumFormatType nType;
583 GetOrCreateFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
585 // set the new key
586 ImplSetFormatKey(nNewKey);
587 FormatChanged(FORMAT_CHANGE_TYPE::THOUSANDSSEP);
590 sal_uInt16 Formatter::GetDecimalDigits() const
592 DBG_ASSERT(!GetOrCreateFormatter()->IsTextFormat(m_nFormatKey),
593 "FormattedField::GetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
595 bool bThousand, IsRed;
596 sal_uInt16 nPrecision, nLeadingCnt;
597 GetOrCreateFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
599 return nPrecision;
602 void Formatter::SetDecimalDigits(sal_uInt16 _nPrecision)
604 DBG_ASSERT(!GetOrCreateFormatter()->IsTextFormat(m_nFormatKey),
605 "FormattedField::SetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
607 // get the current settings
608 bool bThousand, IsRed;
609 sal_uInt16 nPrecision, nLeadingCnt;
610 GetOrCreateFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
611 if (nPrecision == _nPrecision)
612 return;
614 // we need the language for the following
615 LanguageType eLang;
616 GetFormat(eLang);
618 // generate a new format ...
619 OUString sFmtDescription = GetOrCreateFormatter()->GenerateFormat(m_nFormatKey, eLang, bThousand, IsRed, _nPrecision, nLeadingCnt);
620 // ... and introduce it to the formatter
621 sal_Int32 nCheckPos = 0;
622 sal_uInt32 nNewKey;
623 SvNumFormatType nType;
624 GetOrCreateFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
626 // set the new key
627 ImplSetFormatKey(nNewKey);
628 FormatChanged(FORMAT_CHANGE_TYPE::PRECISION);
631 void Formatter::FormatChanged(FORMAT_CHANGE_TYPE _nWhat)
633 m_pLastOutputColor = nullptr;
635 if ( (_nWhat == FORMAT_CHANGE_TYPE::FORMATTER) && m_pFormatter )
636 m_pFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_FORMAT_INTL );
638 ReFormat();
641 void Formatter::EntryLostFocus()
643 // special treatment for empty texts
644 if (GetEntryText().isEmpty())
646 if (!IsEmptyFieldEnabled())
648 if (TreatingAsNumber())
650 ImplSetValue(m_dCurrentValue, true);
651 Modify();
652 m_ValueState = valueDouble;
654 else
656 OUString sNew = GetTextValue();
657 if (!sNew.isEmpty())
658 SetTextFormatted(sNew);
659 else
660 SetTextFormatted(m_sDefaultText);
661 m_ValueState = valueString;
665 else
667 Commit();
671 void Formatter::Commit()
673 // remember the old text
674 OUString sOld(GetEntryText());
676 // do the reformat
677 ReFormat();
679 // did the text change?
680 if (GetEntryText() != sOld)
681 { // consider the field as modified,
682 // but we already have the most recent value;
683 // don't reparse it from the text
684 // (can lead to data loss when the format is lossy,
685 // as is e.g. our default date format: 2-digit year!)
686 Modify(false);
690 void Formatter::ReFormat()
692 if (!IsEmptyFieldEnabled() || !GetEntryText().isEmpty())
694 if (TreatingAsNumber())
696 double dValue = GetValue();
697 if ( m_bEnableNaN && std::isnan( dValue ) )
698 return;
699 ImplSetValue( dValue, true );
701 else
702 SetTextFormatted(GetTextValue());
706 void Formatter::SetMinValue(double dMin)
708 DBG_ASSERT(m_bTreatAsNumber, "FormattedField::SetMinValue : only to be used in numeric mode !");
710 m_dMinValue = dMin;
711 m_bHasMin = true;
712 // for checking the current value at the new border -> ImplSetValue
713 ReFormat();
716 void Formatter::SetMaxValue(double dMax)
718 DBG_ASSERT(m_bTreatAsNumber, "FormattedField::SetMaxValue : only to be used in numeric mode !");
720 m_dMaxValue = dMax;
721 m_bHasMax = true;
722 // for checking the current value at the new border -> ImplSetValue
723 ReFormat();
726 void Formatter::SetTextValue(const OUString& rText)
728 SetFieldText(rText, Selection(0, 0));
729 ReFormat();
732 void Formatter::EnableEmptyField(bool bEnable)
734 if (bEnable == m_bEnableEmptyField)
735 return;
737 m_bEnableEmptyField = bEnable;
738 if (!m_bEnableEmptyField && GetEntryText().isEmpty())
739 ImplSetValue(m_dCurrentValue, true);
742 void Formatter::ImplSetValue(double dVal, bool bForce)
744 if (m_bHasMin && (dVal<m_dMinValue))
746 dVal = m_bWrapOnLimits ? fmod(dVal + m_dMaxValue + 1 - m_dMinValue, m_dMaxValue + 1) + m_dMinValue
747 : m_dMinValue;
749 if (m_bHasMax && (dVal>m_dMaxValue))
751 dVal = m_bWrapOnLimits ? fmod(dVal - m_dMinValue, m_dMaxValue + 1) + m_dMinValue
752 : m_dMaxValue;
754 if (!bForce && (dVal == GetValue()))
755 return;
757 DBG_ASSERT(GetOrCreateFormatter() != nullptr, "FormattedField::ImplSetValue : can't set a value without a formatter !");
759 m_ValueState = valueDouble;
760 UpdateCurrentValue(dVal);
762 if (!m_aOutputHdl.IsSet() || !m_aOutputHdl.Call(nullptr))
764 OUString sNewText;
765 if (GetOrCreateFormatter()->IsTextFormat(m_nFormatKey))
767 // first convert the number as string in standard format
768 OUString sTemp;
769 GetOrCreateFormatter()->GetOutputString(dVal, 0, sTemp, &m_pLastOutputColor);
770 // then encode the string in the corresponding text format
771 GetOrCreateFormatter()->GetOutputString(sTemp, m_nFormatKey, sNewText, &m_pLastOutputColor);
773 else
775 if( IsUsingInputStringForFormatting())
777 sNewText = GetOrCreateFormatter()->GetInputLineString(dVal, m_nFormatKey);
779 else
781 GetOrCreateFormatter()->GetOutputString(dVal, m_nFormatKey, sNewText, &m_pLastOutputColor);
784 ImplSetTextImpl(sNewText, nullptr);
785 DBG_ASSERT(CheckText(sNewText), "FormattedField::ImplSetValue : formatted string doesn't match the criteria !");
788 m_ValueState = valueDouble;
791 bool Formatter::ImplGetValue(double& dNewVal)
793 dNewVal = m_dCurrentValue;
794 if (m_ValueState == valueDouble)
795 return true;
797 // tdf#155241 default to m_dDefaultValue only if explicitly set
798 // otherwise default to m_dCurrentValue
799 if (m_bDefaultValueSet)
800 dNewVal = m_dDefaultValue;
802 OUString sText(GetEntryText());
803 if (sText.isEmpty())
804 return true;
806 bool bUseExternalFormatterValue = false;
807 if (m_aInputHdl.IsSet())
809 sal_Int64 nResult;
810 auto eState = m_aInputHdl.Call(&nResult);
811 bUseExternalFormatterValue = eState != TRISTATE_INDET;
812 if (bUseExternalFormatterValue)
814 if (eState == TRISTATE_TRUE)
816 dNewVal = nResult;
817 dNewVal /= weld::SpinButton::Power10(GetDecimalDigits());
819 else
820 dNewVal = m_dCurrentValue;
824 if (!bUseExternalFormatterValue)
826 DBG_ASSERT(GetOrCreateFormatter() != nullptr, "FormattedField::ImplGetValue : can't give you a current value without a formatter !");
828 sal_uInt32 nFormatKey = m_nFormatKey; // IsNumberFormat changes the FormatKey!
830 if (GetOrCreateFormatter()->IsTextFormat(nFormatKey) && m_bTreatAsNumber)
831 // for detection of values like "1,1" in fields that are formatted as text
832 nFormatKey = 0;
834 // special treatment for percentage formatting
835 if (GetOrCreateFormatter()->GetType(m_nFormatKey) == SvNumFormatType::PERCENT)
837 // the language of our format
838 LanguageType eLanguage = m_pFormatter->GetEntry(m_nFormatKey)->GetLanguage();
839 // the default number format for this language
840 sal_uLong nStandardNumericFormat = m_pFormatter->GetStandardFormat(SvNumFormatType::NUMBER, eLanguage);
842 sal_uInt32 nTempFormat = nStandardNumericFormat;
843 double dTemp;
844 if (m_pFormatter->IsNumberFormat(sText, nTempFormat, dTemp) &&
845 SvNumFormatType::NUMBER == m_pFormatter->GetType(nTempFormat))
846 // the string is equivalent to a number formatted one (has no % sign) -> append it
847 sText += "%";
848 // (with this, an input of '3' becomes '3%', which then by the formatter is translated
849 // into 0.03. Without this, the formatter would give us the double 3 for an input '3',
850 // which equals 300 percent.
852 if (!GetOrCreateFormatter()->IsNumberFormat(sText, nFormatKey, dNewVal))
853 return false;
856 if (m_bHasMin && (dNewVal<m_dMinValue))
857 dNewVal = m_dMinValue;
858 if (m_bHasMax && (dNewVal>m_dMaxValue))
859 dNewVal = m_dMaxValue;
860 return true;
863 void Formatter::SetValue(double dVal)
865 ImplSetValue(dVal, m_ValueState != valueDouble);
868 double Formatter::GetValue()
870 if ( !ImplGetValue( m_dCurrentValue ) )
871 UpdateCurrentValue(m_bEnableNaN ? std::numeric_limits<double>::quiet_NaN() : m_dDefaultValue);
873 m_ValueState = valueDouble;
874 return m_dCurrentValue;
877 void Formatter::DisableRemainderFactor()
879 m_bDisableRemainderFactor = true;
882 void Formatter::UseInputStringForFormatting()
884 m_bUseInputStringForFormatting = true;
887 namespace
889 class FieldFormatter : public Formatter
891 private:
892 FormattedField& m_rSpinButton;
893 public:
894 FieldFormatter(FormattedField& rSpinButton)
895 : m_rSpinButton(rSpinButton)
899 // Formatter overrides
900 virtual Selection GetEntrySelection() const override
902 return m_rSpinButton.GetSelection();
905 virtual OUString GetEntryText() const override
907 return m_rSpinButton.GetText();
910 void SetEntryText(const OUString& rText, const Selection& rSel) override
912 m_rSpinButton.SpinField::SetText(rText, rSel);
915 virtual void SetEntryTextColor(const ::Color* pColor) override
917 if (pColor)
918 m_rSpinButton.SetControlForeground(*pColor);
919 else
920 m_rSpinButton.SetControlForeground();
923 virtual SelectionOptions GetEntrySelectionOptions() const override
925 return m_rSpinButton.GetSettings().GetStyleSettings().GetSelectionOptions();
928 virtual void FieldModified() override
930 m_rSpinButton.SpinField::Modify();
933 virtual void UpdateCurrentValue(double dCurrentValue) override
935 Formatter::UpdateCurrentValue(dCurrentValue);
936 m_rSpinButton.SetUpperEnabled(!m_bHasMax || dCurrentValue < m_dMaxValue);
937 m_rSpinButton.SetLowerEnabled(!m_bHasMin || dCurrentValue > m_dMinValue);
941 class DoubleNumericFormatter : public FieldFormatter
943 private:
944 DoubleNumericField& m_rNumericSpinButton;
945 public:
946 DoubleNumericFormatter(DoubleNumericField& rNumericSpinButton)
947 : FieldFormatter(rNumericSpinButton)
948 , m_rNumericSpinButton(rNumericSpinButton)
952 virtual bool CheckText(const OUString& sText) const override
954 // We'd like to implement this using the NumberFormatter::IsNumberFormat, but unfortunately, this doesn't
955 // recognize fragments of numbers (like, for instance "1e", which happens during entering e.g. "1e10")
956 // Thus, the roundabout way via a regular expression
957 return m_rNumericSpinButton.GetNumberValidator().isValidNumericFragment(sText);
960 virtual void FormatChanged(FORMAT_CHANGE_TYPE nWhat) override
962 m_rNumericSpinButton.ResetConformanceTester();
963 FieldFormatter::FormatChanged(nWhat);
967 class DoubleCurrencyFormatter : public FieldFormatter
969 private:
970 DoubleCurrencyField& m_rCurrencySpinButton;
971 bool m_bChangingFormat;
972 public:
973 DoubleCurrencyFormatter(DoubleCurrencyField& rNumericSpinButton)
974 : FieldFormatter(rNumericSpinButton)
975 , m_rCurrencySpinButton(rNumericSpinButton)
976 , m_bChangingFormat(false)
980 virtual void FormatChanged(FORMAT_CHANGE_TYPE nWhat) override
982 if (m_bChangingFormat)
984 FieldFormatter::FormatChanged(nWhat);
985 return;
988 switch (nWhat)
990 case FORMAT_CHANGE_TYPE::FORMATTER:
991 case FORMAT_CHANGE_TYPE::PRECISION:
992 case FORMAT_CHANGE_TYPE::THOUSANDSSEP:
993 // the aspects which changed don't take our currency settings into account (in fact, they most probably
994 // destroyed them)
995 m_rCurrencySpinButton.UpdateCurrencyFormat();
996 break;
997 case FORMAT_CHANGE_TYPE::KEYONLY:
998 OSL_FAIL("DoubleCurrencyField::FormatChanged : somebody modified my key !");
999 // We always build our own format from the settings we get via special methods (setCurrencySymbol etc.).
1000 // Nobody but ourself should modify the format key directly!
1001 break;
1002 default: break;
1005 FieldFormatter::FormatChanged(nWhat);
1008 void GuardSetFormat(const OUString& rString, LanguageType eLanguage)
1010 // set this new basic format
1011 m_bChangingFormat = true;
1012 SetFormat(rString, eLanguage);
1013 m_bChangingFormat = false;
1019 DoubleNumericField::DoubleNumericField(vcl::Window* pParent, WinBits nStyle)
1020 : FormattedField(pParent, nStyle)
1022 m_xOwnFormatter.reset(new DoubleNumericFormatter(*this));
1023 m_pFormatter = m_xOwnFormatter.get();
1024 ResetConformanceTester();
1027 DoubleNumericField::~DoubleNumericField() = default;
1029 void DoubleNumericField::ResetConformanceTester()
1031 // the thousands and the decimal separator are language dependent
1032 Formatter& rFormatter = GetFormatter();
1033 const SvNumberformat* pFormatEntry = rFormatter.GetOrCreateFormatter()->GetEntry(rFormatter.GetFormatKey());
1035 sal_Unicode cSeparatorThousand = ',';
1036 sal_Unicode cSeparatorDecimal = '.';
1037 if (pFormatEntry)
1039 LocaleDataWrapper aLocaleInfo( LanguageTag( pFormatEntry->GetLanguage()) );
1041 OUString sSeparator = aLocaleInfo.getNumThousandSep();
1042 if (!sSeparator.isEmpty())
1043 cSeparatorThousand = sSeparator[0];
1045 sSeparator = aLocaleInfo.getNumDecimalSep();
1046 if (!sSeparator.isEmpty())
1047 cSeparatorDecimal = sSeparator[0];
1050 m_pNumberValidator.reset(new validation::NumberValidator( cSeparatorThousand, cSeparatorDecimal ));
1054 DoubleCurrencyField::DoubleCurrencyField(vcl::Window* pParent, WinBits nStyle)
1055 :FormattedField(pParent, nStyle)
1057 m_xOwnFormatter.reset(new DoubleCurrencyFormatter(*this));
1058 m_pFormatter = m_xOwnFormatter.get();
1060 m_bPrependCurrSym = false;
1062 // initialize with a system currency format
1063 m_sCurrencySymbol = SvtSysLocale().GetLocaleData().getCurrSymbol();
1064 UpdateCurrencyFormat();
1067 void DoubleCurrencyField::setCurrencySymbol(const OUString& rSymbol)
1069 if (m_sCurrencySymbol == rSymbol)
1070 return;
1072 m_sCurrencySymbol = rSymbol;
1073 UpdateCurrencyFormat();
1074 m_pFormatter->FormatChanged(FORMAT_CHANGE_TYPE::CURRENCY_SYMBOL);
1077 void DoubleCurrencyField::setPrependCurrSym(bool _bPrepend)
1079 if (m_bPrependCurrSym == _bPrepend)
1080 return;
1082 m_bPrependCurrSym = _bPrepend;
1083 UpdateCurrencyFormat();
1084 m_pFormatter->FormatChanged(FORMAT_CHANGE_TYPE::CURRSYM_POSITION);
1087 void DoubleCurrencyField::UpdateCurrencyFormat()
1089 // the old settings
1090 LanguageType eLanguage;
1091 m_pFormatter->GetFormat(eLanguage);
1092 bool bThSep = m_pFormatter->GetThousandsSep();
1093 sal_uInt16 nDigits = m_pFormatter->GetDecimalDigits();
1095 // build a new format string with the base class' and my own settings
1097 /* Strangely with gcc 4.6.3 this needs a temporary LanguageTag, otherwise
1098 * there's
1099 * error: request for member 'getNumThousandSep' in 'aLocaleInfo', which is
1100 * of non-class type 'LocaleDataWrapper(LanguageTag)' */
1101 LocaleDataWrapper aLocaleInfo(( LanguageTag(eLanguage) ));
1103 OUStringBuffer sNewFormat;
1104 if (bThSep)
1106 sNewFormat.append("#" + aLocaleInfo.getNumThousandSep() + "##0");
1108 else
1109 sNewFormat.append('0');
1111 if (nDigits)
1113 sNewFormat.append(aLocaleInfo.getNumDecimalSep());
1114 comphelper::string::padToLength(sNewFormat, sNewFormat.getLength() + nDigits, '0');
1117 if (getPrependCurrSym())
1119 OUString sSymbol = getCurrencySymbol();
1120 sSymbol = comphelper::string::strip(sSymbol, ' ');
1122 OUString sTemp =
1123 "[$" + sSymbol + "] "
1124 + sNewFormat
1125 // for negative values : $ -0.00, not -$ 0.00...
1126 // (the real solution would be a possibility to choose a "positive currency format" and a "negative currency format"...
1127 // But not now... (and hey, you could take a formatted field for this...))
1128 // FS - 31.03.00 74642
1129 + ";[$"
1130 + sSymbol
1131 + "] -"
1132 + sNewFormat;
1134 sNewFormat = sTemp;
1136 else
1138 OUString sTemp = getCurrencySymbol();
1139 sTemp = comphelper::string::strip(sTemp, ' ');
1141 sNewFormat.append(" [$" + sTemp + "]");
1144 // set this new basic format
1145 static_cast<DoubleCurrencyFormatter*>(m_pFormatter)->GuardSetFormat(sNewFormat.makeStringAndClear(), eLanguage);
1148 FormattedField::FormattedField(vcl::Window* pParent, WinBits nStyle)
1149 : SpinField(pParent, nStyle, WindowType::FORMATTEDFIELD)
1150 , m_pFormatter(nullptr)
1154 void FormattedField::dispose()
1156 m_pFormatter = nullptr;
1157 m_xOwnFormatter.reset();
1158 SpinField::dispose();
1161 void FormattedField::SetText(const OUString& rStr)
1163 GetFormatter().SetFieldText(rStr, Selection(0, 0));
1166 void FormattedField::SetText(const OUString& rStr, const Selection& rNewSelection)
1168 GetFormatter().SetFieldText(rStr, rNewSelection);
1169 SetSelection(rNewSelection);
1172 bool FormattedField::set_property(const OUString &rKey, const OUString &rValue)
1174 if (rKey == "digits")
1175 GetFormatter().SetDecimalDigits(rValue.toInt32());
1176 else if (rKey == "wrap")
1177 GetFormatter().SetWrapOnLimits(toBool(rValue));
1178 else
1179 return SpinField::set_property(rKey, rValue);
1180 return true;
1183 void FormattedField::Up()
1185 Formatter& rFormatter = GetFormatter();
1186 auto nScale = weld::SpinButton::Power10(rFormatter.GetDecimalDigits());
1188 sal_Int64 nValue = std::round(rFormatter.GetValue() * nScale);
1189 sal_Int64 nSpinSize = std::round(rFormatter.GetSpinSize() * nScale);
1190 assert(nSpinSize != 0);
1191 sal_Int64 nRemainder = rFormatter.GetDisableRemainderFactor() || nSpinSize == 0 ? 0 : nValue % nSpinSize;
1192 if (nValue >= 0)
1193 nValue = (nRemainder == 0) ? nValue + nSpinSize : nValue + nSpinSize - nRemainder;
1194 else
1195 nValue = (nRemainder == 0) ? nValue + nSpinSize : nValue - nRemainder;
1197 // setValue handles under- and overflows (min/max) automatically
1198 rFormatter.SetValue(static_cast<double>(nValue) / nScale);
1199 SetModifyFlag();
1200 Modify();
1202 SpinField::Up();
1205 void FormattedField::Down()
1207 Formatter& rFormatter = GetFormatter();
1208 auto nScale = weld::SpinButton::Power10(rFormatter.GetDecimalDigits());
1210 sal_Int64 nValue = std::round(rFormatter.GetValue() * nScale);
1211 sal_Int64 nSpinSize = std::round(rFormatter.GetSpinSize() * nScale);
1212 assert(nSpinSize != 0);
1213 sal_Int64 nRemainder = rFormatter.GetDisableRemainderFactor() || nSpinSize == 0 ? 0 : nValue % nSpinSize;
1214 if (nValue >= 0)
1215 nValue = (nRemainder == 0) ? nValue - nSpinSize : nValue - nRemainder;
1216 else
1217 nValue = (nRemainder == 0) ? nValue - nSpinSize : nValue - nSpinSize - nRemainder;
1219 // setValue handles under- and overflows (min/max) automatically
1220 rFormatter.SetValue(static_cast<double>(nValue) / nScale);
1221 SetModifyFlag();
1222 Modify();
1224 SpinField::Down();
1227 void FormattedField::First()
1229 Formatter& rFormatter = GetFormatter();
1230 if (rFormatter.HasMinValue())
1232 rFormatter.SetValue(rFormatter.GetMinValue());
1233 SetModifyFlag();
1234 Modify();
1237 SpinField::First();
1240 void FormattedField::Last()
1242 Formatter& rFormatter = GetFormatter();
1243 if (rFormatter.HasMaxValue())
1245 rFormatter.SetValue(rFormatter.GetMaxValue());
1246 SetModifyFlag();
1247 Modify();
1250 SpinField::Last();
1253 void FormattedField::Modify()
1255 GetFormatter().Modify();
1258 bool FormattedField::PreNotify(NotifyEvent& rNEvt)
1260 if (rNEvt.GetType() == NotifyEventType::KEYINPUT)
1261 GetFormatter().SetLastSelection(GetSelection());
1262 return SpinField::PreNotify(rNEvt);
1265 bool FormattedField::EventNotify(NotifyEvent& rNEvt)
1267 if ((rNEvt.GetType() == NotifyEventType::KEYINPUT) && !IsReadOnly())
1269 const KeyEvent& rKEvt = *rNEvt.GetKeyEvent();
1270 sal_uInt16 nMod = rKEvt.GetKeyCode().GetModifier();
1271 switch ( rKEvt.GetKeyCode().GetCode() )
1273 case KEY_UP:
1274 case KEY_DOWN:
1275 case KEY_PAGEUP:
1276 case KEY_PAGEDOWN:
1278 Formatter& rFormatter = GetFormatter();
1279 if (!nMod && rFormatter.GetOrCreateFormatter()->IsTextFormat(rFormatter.GetFormatKey()))
1281 // the base class would translate this into calls to Up/Down/First/Last,
1282 // but we don't want this if we are text-formatted
1283 return true;
1289 if ((rNEvt.GetType() == NotifyEventType::COMMAND) && !IsReadOnly())
1291 const CommandEvent* pCommand = rNEvt.GetCommandEvent();
1292 if (pCommand->GetCommand() == CommandEventId::Wheel)
1294 const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData();
1295 Formatter& rFormatter = GetFormatter();
1296 if ((pData->GetMode() == CommandWheelMode::SCROLL) &&
1297 rFormatter.GetOrCreateFormatter()->IsTextFormat(rFormatter.GetFormatKey()))
1299 // same as above : prevent the base class from doing Up/Down-calls
1300 // (normally I should put this test into the Up/Down methods itself, shouldn't I ?)
1301 // FS - 71553 - 19.01.00
1302 return true;
1307 if (rNEvt.GetType() == NotifyEventType::LOSEFOCUS && m_pFormatter)
1308 m_pFormatter->EntryLostFocus();
1310 return SpinField::EventNotify( rNEvt );
1313 Formatter& FormattedField::GetFormatter()
1315 if (!m_pFormatter)
1317 m_xOwnFormatter.reset(new FieldFormatter(*this));
1318 m_pFormatter = m_xOwnFormatter.get();
1320 return *m_pFormatter;
1323 void FormattedField::SetFormatter(Formatter* pFormatter)
1325 m_xOwnFormatter.reset();
1326 m_pFormatter = pFormatter;
1329 // currently used by online
1330 void FormattedField::SetValueFromString(const OUString& rStr)
1332 sal_Int32 nEnd;
1333 rtl_math_ConversionStatus eStatus;
1334 Formatter& rFormatter = GetFormatter();
1335 double fValue = ::rtl::math::stringToDouble(rStr, '.', rFormatter.GetDecimalDigits(), &eStatus, &nEnd );
1337 if (eStatus == rtl_math_ConversionStatus_Ok &&
1338 nEnd == rStr.getLength())
1340 rFormatter.SetValue(fValue);
1341 SetModifyFlag();
1342 Modify();
1344 // Notify the value has changed
1345 SpinField::Up();
1347 else
1349 SAL_WARN("vcl", "fail to convert the value: " << rStr);
1353 void FormattedField::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
1355 SpinField::DumpAsPropertyTree(rJsonWriter);
1356 Formatter& rFormatter = GetFormatter();
1358 if (dynamic_cast<weld::TimeFormatter*>(&rFormatter))
1360 // weld::TimeFormatter uses h24 format
1361 rJsonWriter.put("type", "time");
1363 rJsonWriter.put("min", rFormatter.GetMinValue());
1364 rJsonWriter.put("max", rFormatter.GetMaxValue());
1365 rJsonWriter.put("value", rFormatter.GetValue());
1366 rJsonWriter.put("step", rFormatter.GetSpinSize());
1369 FactoryFunction FormattedField::GetUITestFactory() const
1371 return FormattedFieldUIObject::create;
1374 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */