1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
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 <map>
37 #include <rtl/math.hxx>
38 #include <rtl/ustrbuf.hxx>
39 #include <sal/log.hxx>
40 #include <osl/diagnose.h>
41 #include <tools/json_writer.hxx>
42 
43 using namespace ::com::sun::star::lang;
44 using namespace ::com::sun::star::util;
45 
46 // hmm. No support for regular expression. Well, I always (not really :) wanted to write a finite automat
47 // so here comes a finite automat ...
48 
49 namespace validation
50 {
lcl_insertStopTransition(StateTransitions & _rRow)51     static void lcl_insertStopTransition( StateTransitions& _rRow )
52     {
53         _rRow.insert( Transition( '_', END ) );
54     }
55 
lcl_insertStartExponentTransition(StateTransitions & _rRow)56     static void lcl_insertStartExponentTransition( StateTransitions& _rRow )
57     {
58         _rRow.insert( Transition( 'e', EXPONENT_START ) );
59     }
60 
lcl_insertSignTransitions(StateTransitions & _rRow,const State eNextState)61     static void lcl_insertSignTransitions( StateTransitions& _rRow, const State eNextState )
62     {
63         _rRow.insert( Transition( '-', eNextState ) );
64         _rRow.insert( Transition( '+', eNextState ) );
65     }
66 
lcl_insertDigitTransitions(StateTransitions & _rRow,const State eNextState)67     static void lcl_insertDigitTransitions( StateTransitions& _rRow, const State eNextState )
68     {
69         for ( sal_Unicode aChar = '0'; aChar <= '9'; ++aChar )
70             _rRow.insert( Transition( aChar, eNextState ) );
71     }
72 
lcl_insertCommonPreCommaTransitions(StateTransitions & _rRow,const sal_Unicode _cThSep,const sal_Unicode _cDecSep)73     static void lcl_insertCommonPreCommaTransitions( StateTransitions& _rRow, const sal_Unicode _cThSep, const sal_Unicode _cDecSep )
74     {
75         // digits are allowed
76         lcl_insertDigitTransitions( _rRow, DIGIT_PRE_COMMA );
77 
78         // the thousand separator is allowed
79         _rRow.insert( Transition( _cThSep, DIGIT_PRE_COMMA ) );
80 
81         // a comma is allowed
82         _rRow.insert( Transition( _cDecSep, DIGIT_POST_COMMA ) );
83     }
84 
NumberValidator(const sal_Unicode _cThSep,const sal_Unicode _cDecSep)85     NumberValidator::NumberValidator( const sal_Unicode _cThSep, const sal_Unicode _cDecSep )
86     {
87         // build up our transition table
88 
89         // how to proceed from START
90         {
91             StateTransitions& rRow = m_aTransitions[ START ];
92             rRow.insert( Transition( '_', NUM_START ) );
93                 // if we encounter the normalizing character, we want to proceed with the number
94         }
95 
96         // how to proceed from NUM_START
97         {
98             StateTransitions& rRow = m_aTransitions[ NUM_START ];
99 
100             // a sign is allowed
101             lcl_insertSignTransitions( rRow, DIGIT_PRE_COMMA );
102 
103             // common transitions for the two pre-comma states
104             lcl_insertCommonPreCommaTransitions( rRow, _cThSep, _cDecSep );
105 
106             // the exponent may start here
107             // (this would mean string like "_+e10_", but this is a valid fragment, though no valid number)
108             lcl_insertStartExponentTransition( rRow );
109         }
110 
111         // how to proceed from DIGIT_PRE_COMMA
112         {
113             StateTransitions& rRow = m_aTransitions[ DIGIT_PRE_COMMA ];
114 
115             // common transitions for the two pre-comma states
116             lcl_insertCommonPreCommaTransitions( rRow, _cThSep, _cDecSep );
117 
118             // the exponent may start here
119             lcl_insertStartExponentTransition( rRow );
120 
121             // the final transition indicating the end of the string
122             // (if there is no comma and no post-comma, then the string may end here)
123             lcl_insertStopTransition( rRow );
124         }
125 
126         // how to proceed from DIGIT_POST_COMMA
127         {
128             StateTransitions& rRow = m_aTransitions[ DIGIT_POST_COMMA ];
129 
130             // there might be digits, which would keep the state at DIGIT_POST_COMMA
131             lcl_insertDigitTransitions( rRow, DIGIT_POST_COMMA );
132 
133             // the exponent may start here
134             lcl_insertStartExponentTransition( rRow );
135 
136             // the string may end here
137             lcl_insertStopTransition( rRow );
138         }
139 
140         // how to proceed from EXPONENT_START
141         {
142             StateTransitions& rRow = m_aTransitions[ EXPONENT_START ];
143 
144             // there may be a sign
145             lcl_insertSignTransitions( rRow, EXPONENT_DIGIT );
146 
147             // there may be digits
148             lcl_insertDigitTransitions( rRow, EXPONENT_DIGIT );
149 
150             // the string may end here
151             lcl_insertStopTransition( rRow );
152         }
153 
154         // how to proceed from EXPONENT_DIGIT
155         {
156             StateTransitions& rRow = m_aTransitions[ EXPONENT_DIGIT ];
157 
158             // there may be digits
159             lcl_insertDigitTransitions( rRow, EXPONENT_DIGIT );
160 
161             // the string may end here
162             lcl_insertStopTransition( rRow );
163         }
164 
165         // how to proceed from END
166         {
167             /*StateTransitions& rRow =*/ m_aTransitions[ EXPONENT_DIGIT ];
168             // no valid transition to leave this state
169             // (note that we, for consistency, nevertheless want to have a row in the table)
170         }
171     }
172 
implValidateNormalized(const OUString & _rText)173     bool NumberValidator::implValidateNormalized( const OUString& _rText )
174     {
175         const sal_Unicode* pCheckPos = _rText.getStr();
176         State eCurrentState = START;
177 
178         while ( END != eCurrentState )
179         {
180             // look up the transition row for the current state
181             TransitionTable::const_iterator aRow = m_aTransitions.find( eCurrentState );
182             DBG_ASSERT( m_aTransitions.end() != aRow,
183                 "NumberValidator::implValidateNormalized: invalid transition table (row not found)!" );
184 
185             if ( m_aTransitions.end() != aRow )
186             {
187                 // look up the current character in this row
188                 StateTransitions::const_iterator aTransition = aRow->second.find( *pCheckPos );
189                 if ( aRow->second.end() != aTransition )
190                 {
191                     // there is a valid transition for this character
192                     eCurrentState = aTransition->second;
193                     ++pCheckPos;
194                     continue;
195                 }
196             }
197 
198             // if we're here, there is no valid transition
199             break;
200         }
201 
202         DBG_ASSERT( ( END != eCurrentState ) || ( 0 == *pCheckPos ),
203             "NumberValidator::implValidateNormalized: inconsistency!" );
204             // if we're at END, then the string should be done, too - the string should be normalized, means ending
205             // a "_" and not containing any other "_" (except at the start), and "_" is the only possibility
206             // to reach the END state
207 
208         // the string is valid if and only if we reached the final state
209         return ( END == eCurrentState );
210     }
211 
isValidNumericFragment(std::u16string_view _rText)212     bool NumberValidator::isValidNumericFragment( std::u16string_view _rText )
213     {
214         if ( _rText.empty() )
215             // empty strings are always allowed
216             return true;
217 
218         // normalize the string
219         OUString sNormalized = OUString::Concat("_") + _rText + "_";
220 
221         return implValidateNormalized( sNormalized );
222     }
223 }
224 
225 SvNumberFormatter* Formatter::StaticFormatter::s_cFormatter = nullptr;
226 sal_uLong Formatter::StaticFormatter::s_nReferences = 0;
227 
GetFormatter()228 SvNumberFormatter* Formatter::StaticFormatter::GetFormatter()
229 {
230     if (!s_cFormatter)
231     {
232         // get the Office's locale and translate
233         LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
234         s_cFormatter = new SvNumberFormatter(
235             ::comphelper::getProcessComponentContext(),
236             eSysLanguage);
237     }
238     return s_cFormatter;
239 }
240 
StaticFormatter()241 Formatter::StaticFormatter::StaticFormatter()
242 {
243     ++s_nReferences;
244 }
245 
~StaticFormatter()246 Formatter::StaticFormatter::~StaticFormatter()
247 {
248     if (--s_nReferences == 0)
249     {
250         delete s_cFormatter;
251         s_cFormatter = nullptr;
252     }
253 }
254 
Formatter()255 Formatter::Formatter()
256     :m_aLastSelection(0,0)
257     ,m_dMinValue(0)
258     ,m_dMaxValue(0)
259     ,m_bHasMin(false)
260     ,m_bHasMax(false)
261     ,m_bWrapOnLimits(false)
262     ,m_bStrictFormat(true)
263     ,m_bEnableEmptyField(true)
264     ,m_bAutoColor(false)
265     ,m_bEnableNaN(false)
266     ,m_bDisableRemainderFactor(false)
267     ,m_ValueState(valueDirty)
268     ,m_dCurrentValue(0)
269     ,m_dDefaultValue(0)
270     ,m_nFormatKey(0)
271     ,m_pFormatter(nullptr)
272     ,m_dSpinSize(1)
273     ,m_dSpinFirst(-1000000)
274     ,m_dSpinLast(1000000)
275     ,m_bTreatAsNumber(true)
276     ,m_pLastOutputColor(nullptr)
277     ,m_bUseInputStringForFormatting(false)
278 {
279 }
280 
~Formatter()281 Formatter::~Formatter()
282 {
283 }
284 
SetFieldText(const OUString & rStr,const Selection & rNewSelection)285 void Formatter::SetFieldText(const OUString& rStr, const Selection& rNewSelection)
286 {
287     SetEntryText(rStr, rNewSelection);
288     m_ValueState = valueDirty;
289 }
290 
SetTextFormatted(const OUString & rStr)291 void Formatter::SetTextFormatted(const OUString& rStr)
292 {
293     SAL_INFO_IF(GetOrCreateFormatter()->IsTextFormat(m_nFormatKey), "svtools",
294         "FormattedField::SetTextFormatted : valid only with text formats !");
295 
296     m_sCurrentTextValue = rStr;
297 
298     OUString sFormatted;
299     double dNumber = 0.0;
300     // IsNumberFormat changes the format key parameter
301     sal_uInt32 nTempFormatKey = static_cast< sal_uInt32 >( m_nFormatKey );
302     if( IsUsingInputStringForFormatting() &&
303         GetOrCreateFormatter()->IsNumberFormat(m_sCurrentTextValue, nTempFormatKey, dNumber) )
304     {
305         GetOrCreateFormatter()->GetInputLineString(dNumber, m_nFormatKey, sFormatted);
306     }
307     else
308     {
309         GetOrCreateFormatter()->GetOutputString(m_sCurrentTextValue,
310                                             m_nFormatKey,
311                                             sFormatted,
312                                             &m_pLastOutputColor);
313     }
314 
315     // calculate the new selection
316     Selection aSel(GetEntrySelection());
317     Selection aNewSel(aSel);
318     aNewSel.Justify();
319     sal_Int32 nNewLen = sFormatted.getLength();
320     sal_Int32 nCurrentLen = GetEntryText().getLength();
321     if ((nNewLen > nCurrentLen) && (aNewSel.Max() == nCurrentLen))
322     {   // the new text is longer and the cursor was behind the last char (of the old text)
323         if (aNewSel.Min() == 0)
324         {   // the whole text was selected -> select the new text on the whole, too
325             aNewSel.Max() = nNewLen;
326             if (!nCurrentLen)
327             {   // there wasn't really a previous selection (as there was no previous text), we're setting a new one -> check the selection options
328                 SelectionOptions nSelOptions = GetEntrySelectionOptions();
329                 if (nSelOptions & SelectionOptions::ShowFirst)
330                 {   // selection should be from right to left -> swap min and max
331                     aNewSel.Min() = aNewSel.Max();
332                     aNewSel.Max() = 0;
333                 }
334             }
335         }
336         else if (aNewSel.Max() == aNewSel.Min())
337         {   // there was no selection -> set the cursor behind the new last char
338             aNewSel.Max() = nNewLen;
339             aNewSel.Min() = nNewLen;
340         }
341     }
342     else if (aNewSel.Max() > nNewLen)
343         aNewSel.Max() = nNewLen;
344     else
345         aNewSel = aSel; // don't use the justified version
346     SetEntryText(sFormatted, aNewSel);
347     m_ValueState = valueString;
348 }
349 
GetTextValue() const350 OUString const & Formatter::GetTextValue() const
351 {
352     if (m_ValueState != valueString )
353     {
354         const_cast<Formatter*>(this)->m_sCurrentTextValue = GetEntryText();
355         const_cast<Formatter*>(this)->m_ValueState = valueString;
356     }
357     return m_sCurrentTextValue;
358 }
359 
EnableNotANumber(bool _bEnable)360 void Formatter::EnableNotANumber(bool _bEnable)
361 {
362     if ( m_bEnableNaN == _bEnable )
363         return;
364 
365     m_bEnableNaN = _bEnable;
366 }
367 
SetAutoColor(bool _bAutomatic)368 void Formatter::SetAutoColor(bool _bAutomatic)
369 {
370     if (_bAutomatic == m_bAutoColor)
371         return;
372 
373     m_bAutoColor = _bAutomatic;
374     if (m_bAutoColor)
375     {
376         // if auto color is switched on, adjust the current text color, too
377         SetEntryTextColor(m_pLastOutputColor);
378     }
379 }
380 
Modify(bool makeValueDirty)381 void Formatter::Modify(bool makeValueDirty)
382 {
383     if (!IsStrictFormat())
384     {
385         if(makeValueDirty)
386             m_ValueState = valueDirty;
387         FieldModified();
388         return;
389     }
390 
391     OUString sCheck = GetEntryText();
392     if (CheckText(sCheck))
393     {
394         m_sLastValidText = sCheck;
395         m_aLastSelection = GetEntrySelection();
396         if(makeValueDirty)
397             m_ValueState = valueDirty;
398     }
399     else
400     {
401         ImplSetTextImpl(m_sLastValidText, &m_aLastSelection);
402     }
403 
404     FieldModified();
405 }
406 
ImplSetTextImpl(const OUString & rNew,Selection const * pNewSel)407 void Formatter::ImplSetTextImpl(const OUString& rNew, Selection const * pNewSel)
408 {
409     if (m_bAutoColor)
410         SetEntryTextColor(m_pLastOutputColor);
411 
412     if (pNewSel)
413         SetEntryText(rNew, *pNewSel);
414     else
415     {
416         Selection aSel(GetEntrySelection());
417         aSel.Justify();
418 
419         sal_Int32 nNewLen = rNew.getLength();
420         sal_Int32 nCurrentLen = GetEntryText().getLength();
421 
422         if ((nNewLen > nCurrentLen) && (aSel.Max() == nCurrentLen))
423         {   // new text is longer and the cursor is behind the last char
424             if (aSel.Min() == 0)
425             {
426                 if (!nCurrentLen)
427                 {   // there wasn't really a previous selection (as there was no previous text)
428                     aSel.Max() = 0;
429                 }
430                 else
431                 {   // the whole text was selected -> select the new text on the whole, too
432                     aSel.Max() = nNewLen;
433                 }
434             }
435             else if (aSel.Max() == aSel.Min())
436             {   // there was no selection -> set the cursor behind the new last char
437                 aSel.Max() = nNewLen;
438                 aSel.Min() = nNewLen;
439             }
440         }
441         else if (aSel.Max() > nNewLen)
442             aSel.Max() = nNewLen;
443         SetEntryText(rNew, aSel);
444     }
445 
446     m_ValueState = valueDirty; // not always necessary, but better re-evaluate for safety reasons
447 }
448 
ImplSetFormatKey(sal_uLong nFormatKey)449 void Formatter::ImplSetFormatKey(sal_uLong nFormatKey)
450 {
451     m_nFormatKey = nFormatKey;
452     bool bNeedFormatter = (m_pFormatter == nullptr) && (nFormatKey != 0);
453     if (bNeedFormatter)
454     {
455         GetOrCreateFormatter(); // this creates a standard formatter
456 
457         // It might happen that the standard formatter makes no sense here, but it takes a default
458         // format. Thus, it is possible to set one of the other standard keys (which are spanning
459         // across multiple formatters).
460         m_nFormatKey = nFormatKey;
461         // When calling SetFormatKey without a formatter, the key must be one of the standard values
462         // that is available for all formatters (and, thus, also in this new one).
463         DBG_ASSERT(m_pFormatter->GetEntry(nFormatKey) != nullptr, "FormattedField::ImplSetFormatKey : invalid format key !");
464     }
465 }
466 
SetFormatKey(sal_uLong nFormatKey)467 void Formatter::SetFormatKey(sal_uLong nFormatKey)
468 {
469     bool bNoFormatter = (m_pFormatter == nullptr);
470     ImplSetFormatKey(nFormatKey);
471     FormatChanged((bNoFormatter && (m_pFormatter != nullptr)) ? FORMAT_CHANGE_TYPE::FORMATTER : FORMAT_CHANGE_TYPE::KEYONLY);
472 }
473 
SetFormatter(SvNumberFormatter * pFormatter,bool bResetFormat)474 void Formatter::SetFormatter(SvNumberFormatter* pFormatter, bool bResetFormat)
475 {
476 
477     if (bResetFormat)
478     {
479         m_pFormatter = pFormatter;
480 
481         // calc the default format key from the Office's UI locale
482         if ( m_pFormatter )
483         {
484             // get the Office's locale and translate
485             LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
486             // get the standard numeric format for this language
487             m_nFormatKey = m_pFormatter->GetStandardFormat( SvNumFormatType::NUMBER, eSysLanguage );
488         }
489         else
490             m_nFormatKey = 0;
491     }
492     else
493     {
494         LanguageType aOldLang;
495         OUString sOldFormat = GetFormat(aOldLang);
496 
497         sal_uInt32 nDestKey = pFormatter->TestNewString(sOldFormat);
498         if (nDestKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
499         {
500             // language of the new formatter
501             const SvNumberformat* pDefaultEntry = pFormatter->GetEntry(0);
502             LanguageType aNewLang = pDefaultEntry ? pDefaultEntry->GetLanguage() : LANGUAGE_DONTKNOW;
503 
504             // convert the old format string into the new language
505             sal_Int32 nCheckPos;
506             SvNumFormatType nType;
507             pFormatter->PutandConvertEntry(sOldFormat, nCheckPos, nType, nDestKey, aOldLang, aNewLang, true);
508             m_nFormatKey = nDestKey;
509         }
510         m_pFormatter = pFormatter;
511     }
512 
513     FormatChanged(FORMAT_CHANGE_TYPE::FORMATTER);
514 }
515 
GetFormat(LanguageType & eLang) const516 OUString Formatter::GetFormat(LanguageType& eLang) const
517 {
518     const SvNumberformat* pFormatEntry = GetOrCreateFormatter()->GetEntry(m_nFormatKey);
519     DBG_ASSERT(pFormatEntry != nullptr, "FormattedField::GetFormat: no number format for the given format key.");
520     OUString sFormatString = pFormatEntry ? pFormatEntry->GetFormatstring() : OUString();
521     eLang = pFormatEntry ? pFormatEntry->GetLanguage() : LANGUAGE_DONTKNOW;
522 
523     return sFormatString;
524 }
525 
SetFormat(const OUString & rFormatString,LanguageType eLang)526 bool Formatter::SetFormat(const OUString& rFormatString, LanguageType eLang)
527 {
528     sal_uInt32 nNewKey = GetOrCreateFormatter()->TestNewString(rFormatString, eLang);
529     if (nNewKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
530     {
531         sal_Int32 nCheckPos;
532         SvNumFormatType nType;
533         OUString rFormat(rFormatString);
534         if (!GetOrCreateFormatter()->PutEntry(rFormat, nCheckPos, nType, nNewKey, eLang))
535             return false;
536         DBG_ASSERT(nNewKey != NUMBERFORMAT_ENTRY_NOT_FOUND, "FormattedField::SetFormatString : PutEntry returned an invalid key !");
537     }
538 
539     if (nNewKey != m_nFormatKey)
540         SetFormatKey(nNewKey);
541     return true;
542 }
543 
GetThousandsSep() const544 bool Formatter::GetThousandsSep() const
545 {
546     DBG_ASSERT(!GetOrCreateFormatter()->IsTextFormat(m_nFormatKey),
547         "FormattedField::GetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
548 
549     bool bThousand, IsRed;
550     sal_uInt16 nPrecision, nLeadingCnt;
551     GetOrCreateFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
552 
553     return bThousand;
554 }
555 
SetThousandsSep(bool _bUseSeparator)556 void Formatter::SetThousandsSep(bool _bUseSeparator)
557 {
558     DBG_ASSERT(!GetOrCreateFormatter()->IsTextFormat(m_nFormatKey),
559         "FormattedField::SetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
560 
561     // get the current settings
562     bool bThousand, IsRed;
563     sal_uInt16 nPrecision, nLeadingCnt;
564     GetOrCreateFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
565     if (bThousand == _bUseSeparator)
566         return;
567 
568     // we need the language for the following
569     LanguageType eLang;
570     GetFormat(eLang);
571 
572     // generate a new format ...
573     OUString sFmtDescription = GetOrCreateFormatter()->GenerateFormat(m_nFormatKey, eLang, _bUseSeparator, IsRed, nPrecision, nLeadingCnt);
574     // ... and introduce it to the formatter
575     sal_Int32 nCheckPos = 0;
576     sal_uInt32 nNewKey;
577     SvNumFormatType nType;
578     GetOrCreateFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
579 
580     // set the new key
581     ImplSetFormatKey(nNewKey);
582     FormatChanged(FORMAT_CHANGE_TYPE::THOUSANDSSEP);
583 }
584 
GetDecimalDigits() const585 sal_uInt16 Formatter::GetDecimalDigits() const
586 {
587     DBG_ASSERT(!GetOrCreateFormatter()->IsTextFormat(m_nFormatKey),
588         "FormattedField::GetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
589 
590     bool bThousand, IsRed;
591     sal_uInt16 nPrecision, nLeadingCnt;
592     GetOrCreateFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
593 
594     return nPrecision;
595 }
596 
SetDecimalDigits(sal_uInt16 _nPrecision)597 void Formatter::SetDecimalDigits(sal_uInt16 _nPrecision)
598 {
599     DBG_ASSERT(!GetOrCreateFormatter()->IsTextFormat(m_nFormatKey),
600         "FormattedField::SetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
601 
602     // get the current settings
603     bool bThousand, IsRed;
604     sal_uInt16 nPrecision, nLeadingCnt;
605     GetOrCreateFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
606     if (nPrecision == _nPrecision)
607         return;
608 
609     // we need the language for the following
610     LanguageType eLang;
611     GetFormat(eLang);
612 
613     // generate a new format ...
614     OUString sFmtDescription = GetOrCreateFormatter()->GenerateFormat(m_nFormatKey, eLang, bThousand, IsRed, _nPrecision, nLeadingCnt);
615     // ... and introduce it to the formatter
616     sal_Int32 nCheckPos = 0;
617     sal_uInt32 nNewKey;
618     SvNumFormatType nType;
619     GetOrCreateFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
620 
621     // set the new key
622     ImplSetFormatKey(nNewKey);
623     FormatChanged(FORMAT_CHANGE_TYPE::PRECISION);
624 }
625 
FormatChanged(FORMAT_CHANGE_TYPE _nWhat)626 void Formatter::FormatChanged(FORMAT_CHANGE_TYPE _nWhat)
627 {
628     m_pLastOutputColor = nullptr;
629 
630     if ( (_nWhat == FORMAT_CHANGE_TYPE::FORMATTER) && m_pFormatter )
631         m_pFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_FORMAT_INTL );
632 
633     ReFormat();
634 }
635 
EntryLostFocus()636 void Formatter::EntryLostFocus()
637 {
638     // special treatment for empty texts
639     if (GetEntryText().isEmpty())
640     {
641         if (!IsEmptyFieldEnabled())
642         {
643             if (TreatingAsNumber())
644             {
645                 ImplSetValue(m_dCurrentValue, true);
646                 Modify();
647                 m_ValueState = valueDouble;
648             }
649             else
650             {
651                 OUString sNew = GetTextValue();
652                 if (!sNew.isEmpty())
653                     SetTextFormatted(sNew);
654                 else
655                     SetTextFormatted(m_sDefaultText);
656                 m_ValueState = valueString;
657             }
658         }
659     }
660     else
661     {
662         Commit();
663     }
664 }
665 
Commit()666 void Formatter::Commit()
667 {
668     // remember the old text
669     OUString sOld(GetEntryText());
670 
671     // do the reformat
672     ReFormat();
673 
674     // did the text change?
675     if (GetEntryText() != sOld)
676     {   // consider the field as modified,
677         // but we already have the most recent value;
678         // don't reparse it from the text
679         // (can lead to data loss when the format is lossy,
680         //  as is e.g. our default date format: 2-digit year!)
681         Modify(false);
682     }
683 }
684 
ReFormat()685 void Formatter::ReFormat()
686 {
687     if (!IsEmptyFieldEnabled() || !GetEntryText().isEmpty())
688     {
689         if (TreatingAsNumber())
690         {
691             double dValue = GetValue();
692             if ( m_bEnableNaN && std::isnan( dValue ) )
693                 return;
694             ImplSetValue( dValue, true );
695         }
696         else
697             SetTextFormatted(GetTextValue());
698     }
699 }
700 
SetMinValue(double dMin)701 void Formatter::SetMinValue(double dMin)
702 {
703     DBG_ASSERT(m_bTreatAsNumber, "FormattedField::SetMinValue : only to be used in numeric mode !");
704 
705     m_dMinValue = dMin;
706     m_bHasMin = true;
707     // for checking the current value at the new border -> ImplSetValue
708     ReFormat();
709 }
710 
SetMaxValue(double dMax)711 void Formatter::SetMaxValue(double dMax)
712 {
713     DBG_ASSERT(m_bTreatAsNumber, "FormattedField::SetMaxValue : only to be used in numeric mode !");
714 
715     m_dMaxValue = dMax;
716     m_bHasMax = true;
717     // for checking the current value at the new border -> ImplSetValue
718     ReFormat();
719 }
720 
SetTextValue(const OUString & rText)721 void Formatter::SetTextValue(const OUString& rText)
722 {
723     SetFieldText(rText, Selection(0, 0));
724     ReFormat();
725 }
726 
EnableEmptyField(bool bEnable)727 void Formatter::EnableEmptyField(bool bEnable)
728 {
729     if (bEnable == m_bEnableEmptyField)
730         return;
731 
732     m_bEnableEmptyField = bEnable;
733     if (!m_bEnableEmptyField && GetEntryText().isEmpty())
734         ImplSetValue(m_dCurrentValue, true);
735 }
736 
ImplSetValue(double dVal,bool bForce)737 void Formatter::ImplSetValue(double dVal, bool bForce)
738 {
739     if (m_bHasMin && (dVal<m_dMinValue))
740     {
741         dVal = m_bWrapOnLimits ? fmod(dVal + m_dMaxValue + 1 - m_dMinValue, m_dMaxValue + 1) + m_dMinValue
742                                : m_dMinValue;
743     }
744     if (m_bHasMax && (dVal>m_dMaxValue))
745     {
746         dVal = m_bWrapOnLimits ? fmod(dVal - m_dMinValue, m_dMaxValue + 1) + m_dMinValue
747                                : m_dMaxValue;
748     }
749     if (!bForce && (dVal == GetValue()))
750         return;
751 
752     DBG_ASSERT(GetOrCreateFormatter() != nullptr, "FormattedField::ImplSetValue : can't set a value without a formatter !");
753 
754     m_ValueState = valueDouble;
755     UpdateCurrentValue(dVal);
756 
757     if (!m_aOutputHdl.IsSet() || !m_aOutputHdl.Call(nullptr))
758     {
759         OUString sNewText;
760         if (GetOrCreateFormatter()->IsTextFormat(m_nFormatKey))
761         {
762             // first convert the number as string in standard format
763             OUString sTemp;
764             GetOrCreateFormatter()->GetOutputString(dVal, 0, sTemp, &m_pLastOutputColor);
765             // then encode the string in the corresponding text format
766             GetOrCreateFormatter()->GetOutputString(sTemp, m_nFormatKey, sNewText, &m_pLastOutputColor);
767         }
768         else
769         {
770             if( IsUsingInputStringForFormatting())
771             {
772                 GetOrCreateFormatter()->GetInputLineString(dVal, m_nFormatKey, sNewText);
773             }
774             else
775             {
776                 GetOrCreateFormatter()->GetOutputString(dVal, m_nFormatKey, sNewText, &m_pLastOutputColor);
777             }
778         }
779         ImplSetTextImpl(sNewText, nullptr);
780         DBG_ASSERT(CheckText(sNewText), "FormattedField::ImplSetValue : formatted string doesn't match the criteria !");
781     }
782 
783     m_ValueState = valueDouble;
784 }
785 
ImplGetValue(double & dNewVal)786 bool Formatter::ImplGetValue(double& dNewVal)
787 {
788     dNewVal = m_dCurrentValue;
789     if (m_ValueState == valueDouble)
790         return true;
791 
792     dNewVal = m_dDefaultValue;
793     OUString sText(GetEntryText());
794     if (sText.isEmpty())
795         return true;
796 
797     bool bUseExternalFormatterValue = false;
798     if (m_aInputHdl.IsSet())
799     {
800         sal_Int64 nResult;
801         auto eState = m_aInputHdl.Call(&nResult);
802         bUseExternalFormatterValue = eState != TRISTATE_INDET;
803         if (bUseExternalFormatterValue)
804         {
805             if (eState == TRISTATE_TRUE)
806             {
807                 dNewVal = nResult;
808                 dNewVal /= weld::SpinButton::Power10(GetDecimalDigits());
809             }
810             else
811                 dNewVal = m_dCurrentValue;
812         }
813     }
814 
815     if (!bUseExternalFormatterValue)
816     {
817         DBG_ASSERT(GetOrCreateFormatter() != nullptr, "FormattedField::ImplGetValue : can't give you a current value without a formatter !");
818 
819         sal_uInt32 nFormatKey = m_nFormatKey; // IsNumberFormat changes the FormatKey!
820 
821         if (GetOrCreateFormatter()->IsTextFormat(nFormatKey) && m_bTreatAsNumber)
822             // for detection of values like "1,1" in fields that are formatted as text
823             nFormatKey = 0;
824 
825         // special treatment for percentage formatting
826         if (GetOrCreateFormatter()->GetType(m_nFormatKey) == SvNumFormatType::PERCENT)
827         {
828             // the language of our format
829             LanguageType eLanguage = m_pFormatter->GetEntry(m_nFormatKey)->GetLanguage();
830             // the default number format for this language
831             sal_uLong nStandardNumericFormat = m_pFormatter->GetStandardFormat(SvNumFormatType::NUMBER, eLanguage);
832 
833             sal_uInt32 nTempFormat = nStandardNumericFormat;
834             double dTemp;
835             if (m_pFormatter->IsNumberFormat(sText, nTempFormat, dTemp) &&
836                 SvNumFormatType::NUMBER == m_pFormatter->GetType(nTempFormat))
837                 // the string is equivalent to a number formatted one (has no % sign) -> append it
838                 sText += "%";
839             // (with this, an input of '3' becomes '3%', which then by the formatter is translated
840             // into 0.03. Without this, the formatter would give us the double 3 for an input '3',
841             // which equals 300 percent.
842         }
843         if (!GetOrCreateFormatter()->IsNumberFormat(sText, nFormatKey, dNewVal))
844             return false;
845     }
846 
847     if (m_bHasMin && (dNewVal<m_dMinValue))
848         dNewVal = m_dMinValue;
849     if (m_bHasMax && (dNewVal>m_dMaxValue))
850         dNewVal = m_dMaxValue;
851     return true;
852 }
853 
SetValue(double dVal)854 void Formatter::SetValue(double dVal)
855 {
856     ImplSetValue(dVal, m_ValueState != valueDouble);
857 }
858 
GetValue()859 double Formatter::GetValue()
860 {
861     if ( !ImplGetValue( m_dCurrentValue ) )
862     {
863         double dValue;
864         if (m_bEnableNaN)
865             ::rtl::math::setNan(&dValue);
866         else
867             dValue = m_dDefaultValue;
868         UpdateCurrentValue(dValue);
869     }
870 
871     m_ValueState = valueDouble;
872     return m_dCurrentValue;
873 }
874 
DisableRemainderFactor()875 void Formatter::DisableRemainderFactor()
876 {
877     m_bDisableRemainderFactor = true;
878 }
879 
UseInputStringForFormatting()880 void Formatter::UseInputStringForFormatting()
881 {
882     m_bUseInputStringForFormatting = true;
883 }
884 
885 namespace
886 {
887     class FieldFormatter : public Formatter
888     {
889     private:
890         FormattedField& m_rSpinButton;
891     public:
FieldFormatter(FormattedField & rSpinButton)892         FieldFormatter(FormattedField& rSpinButton)
893             : m_rSpinButton(rSpinButton)
894         {
895         }
896 
897         // Formatter overrides
GetEntrySelection() const898         virtual Selection GetEntrySelection() const override
899         {
900             return m_rSpinButton.GetSelection();
901         }
902 
GetEntryText() const903         virtual OUString GetEntryText() const override
904         {
905             return m_rSpinButton.GetText();
906         }
907 
SetEntryText(const OUString & rText,const Selection & rSel)908         void SetEntryText(const OUString& rText, const Selection& rSel) override
909         {
910             m_rSpinButton.SpinField::SetText(rText, rSel);
911         }
912 
SetEntryTextColor(const Color * pColor)913         virtual void SetEntryTextColor(const Color* pColor) override
914         {
915             if (pColor)
916                 m_rSpinButton.SetControlForeground(*pColor);
917             else
918                 m_rSpinButton.SetControlForeground();
919         }
920 
GetEntrySelectionOptions() const921         virtual SelectionOptions GetEntrySelectionOptions() const override
922         {
923             return m_rSpinButton.GetSettings().GetStyleSettings().GetSelectionOptions();
924         }
925 
FieldModified()926         virtual void FieldModified() override
927         {
928             m_rSpinButton.SpinField::Modify();
929         }
930     };
931 
932     class DoubleNumericFormatter : public FieldFormatter
933     {
934     private:
935         DoubleNumericField& m_rNumericSpinButton;
936     public:
DoubleNumericFormatter(DoubleNumericField & rNumericSpinButton)937         DoubleNumericFormatter(DoubleNumericField& rNumericSpinButton)
938             : FieldFormatter(rNumericSpinButton)
939             , m_rNumericSpinButton(rNumericSpinButton)
940         {
941         }
942 
CheckText(const OUString & sText) const943         virtual bool CheckText(const OUString& sText) const override
944         {
945             // We'd like to implement this using the NumberFormatter::IsNumberFormat, but unfortunately, this doesn't
946             // recognize fragments of numbers (like, for instance "1e", which happens during entering e.g. "1e10")
947             // Thus, the roundabout way via a regular expression
948             return m_rNumericSpinButton.GetNumberValidator().isValidNumericFragment(sText);
949         }
950 
FormatChanged(FORMAT_CHANGE_TYPE nWhat)951         virtual void FormatChanged(FORMAT_CHANGE_TYPE nWhat) override
952         {
953             m_rNumericSpinButton.ResetConformanceTester();
954             FieldFormatter::FormatChanged(nWhat);
955         }
956     };
957 
958     class DoubleCurrencyFormatter : public FieldFormatter
959     {
960     private:
961         DoubleCurrencyField& m_rCurrencySpinButton;
962         bool m_bChangingFormat;
963     public:
DoubleCurrencyFormatter(DoubleCurrencyField & rNumericSpinButton)964         DoubleCurrencyFormatter(DoubleCurrencyField& rNumericSpinButton)
965             : FieldFormatter(rNumericSpinButton)
966             , m_rCurrencySpinButton(rNumericSpinButton)
967             , m_bChangingFormat(false)
968         {
969         }
970 
FormatChanged(FORMAT_CHANGE_TYPE nWhat)971         virtual void FormatChanged(FORMAT_CHANGE_TYPE nWhat) override
972         {
973             if (m_bChangingFormat)
974             {
975                 FieldFormatter::FormatChanged(nWhat);
976                 return;
977             }
978 
979             switch (nWhat)
980             {
981                 case FORMAT_CHANGE_TYPE::FORMATTER:
982                 case FORMAT_CHANGE_TYPE::PRECISION:
983                 case FORMAT_CHANGE_TYPE::THOUSANDSSEP:
984                     // the aspects which changed don't take our currency settings into account (in fact, they most probably
985                     // destroyed them)
986                     m_rCurrencySpinButton.UpdateCurrencyFormat();
987                     break;
988                 case FORMAT_CHANGE_TYPE::KEYONLY:
989                     OSL_FAIL("DoubleCurrencyField::FormatChanged : somebody modified my key !");
990                     // We always build our own format from the settings we get via special methods (setCurrencySymbol etc.).
991                     // Nobody but ourself should modify the format key directly!
992                     break;
993                 default: break;
994             }
995 
996             FieldFormatter::FormatChanged(nWhat);
997         }
998 
GuardSetFormat(const OUString & rString,LanguageType eLanguage)999         void GuardSetFormat(const OUString& rString, LanguageType eLanguage)
1000         {
1001             // set this new basic format
1002             m_bChangingFormat = true;
1003             SetFormat(rString, eLanguage);
1004             m_bChangingFormat = false;
1005         }
1006 
1007     };
1008 }
1009 
DoubleNumericField(vcl::Window * pParent,WinBits nStyle)1010 DoubleNumericField::DoubleNumericField(vcl::Window* pParent, WinBits nStyle)
1011     : FormattedField(pParent, nStyle)
1012 {
1013     m_xOwnFormatter.reset(new DoubleNumericFormatter(*this));
1014     m_pFormatter = m_xOwnFormatter.get();
1015     ResetConformanceTester();
1016 }
1017 
1018 DoubleNumericField::~DoubleNumericField() = default;
1019 
ResetConformanceTester()1020 void DoubleNumericField::ResetConformanceTester()
1021 {
1022     // the thousands and the decimal separator are language dependent
1023     Formatter& rFormatter = GetFormatter();
1024     const SvNumberformat* pFormatEntry = rFormatter.GetOrCreateFormatter()->GetEntry(rFormatter.GetFormatKey());
1025 
1026     sal_Unicode cSeparatorThousand = ',';
1027     sal_Unicode cSeparatorDecimal = '.';
1028     if (pFormatEntry)
1029     {
1030         LocaleDataWrapper aLocaleInfo( LanguageTag( pFormatEntry->GetLanguage()) );
1031 
1032         OUString sSeparator = aLocaleInfo.getNumThousandSep();
1033         if (!sSeparator.isEmpty())
1034             cSeparatorThousand = sSeparator[0];
1035 
1036         sSeparator = aLocaleInfo.getNumDecimalSep();
1037         if (!sSeparator.isEmpty())
1038             cSeparatorDecimal = sSeparator[0];
1039     }
1040 
1041     m_pNumberValidator.reset(new validation::NumberValidator( cSeparatorThousand, cSeparatorDecimal ));
1042 }
1043 
1044 
DoubleCurrencyField(vcl::Window * pParent,WinBits nStyle)1045 DoubleCurrencyField::DoubleCurrencyField(vcl::Window* pParent, WinBits nStyle)
1046     :FormattedField(pParent, nStyle)
1047 {
1048     m_xOwnFormatter.reset(new DoubleCurrencyFormatter(*this));
1049     m_pFormatter = m_xOwnFormatter.get();
1050 
1051     m_bPrependCurrSym = false;
1052 
1053     // initialize with a system currency format
1054     m_sCurrencySymbol = SvtSysLocale().GetLocaleData().getCurrSymbol();
1055     UpdateCurrencyFormat();
1056 }
1057 
setCurrencySymbol(const OUString & rSymbol)1058 void DoubleCurrencyField::setCurrencySymbol(const OUString& rSymbol)
1059 {
1060     if (m_sCurrencySymbol == rSymbol)
1061         return;
1062 
1063     m_sCurrencySymbol = rSymbol;
1064     UpdateCurrencyFormat();
1065     m_pFormatter->FormatChanged(FORMAT_CHANGE_TYPE::CURRENCY_SYMBOL);
1066 }
1067 
setPrependCurrSym(bool _bPrepend)1068 void DoubleCurrencyField::setPrependCurrSym(bool _bPrepend)
1069 {
1070     if (m_bPrependCurrSym == _bPrepend)
1071          return;
1072 
1073     m_bPrependCurrSym = _bPrepend;
1074     UpdateCurrencyFormat();
1075     m_pFormatter->FormatChanged(FORMAT_CHANGE_TYPE::CURRSYM_POSITION);
1076 }
1077 
UpdateCurrencyFormat()1078 void DoubleCurrencyField::UpdateCurrencyFormat()
1079 {
1080     // the old settings
1081     LanguageType eLanguage;
1082     m_pFormatter->GetFormat(eLanguage);
1083     bool bThSep = m_pFormatter->GetThousandsSep();
1084     sal_uInt16 nDigits = m_pFormatter->GetDecimalDigits();
1085 
1086     // build a new format string with the base class' and my own settings
1087 
1088     /* Strangely with gcc 4.6.3 this needs a temporary LanguageTag, otherwise
1089      * there's
1090      * error: request for member 'getNumThousandSep' in 'aLocaleInfo', which is
1091      * of non-class type 'LocaleDataWrapper(LanguageTag)' */
1092     LanguageTag aLanguageTag( eLanguage);
1093     LocaleDataWrapper aLocaleInfo( aLanguageTag );
1094 
1095     OUStringBuffer sNewFormat;
1096     if (bThSep)
1097     {
1098         sNewFormat.append('#');
1099         sNewFormat.append(aLocaleInfo.getNumThousandSep());
1100         sNewFormat.append("##0");
1101     }
1102     else
1103         sNewFormat.append('0');
1104 
1105     if (nDigits)
1106     {
1107         sNewFormat.append(aLocaleInfo.getNumDecimalSep());
1108 
1109         OUStringBuffer sTemp;
1110         comphelper::string::padToLength(sTemp, nDigits, '0');
1111         sNewFormat.append(sTemp);
1112     }
1113 
1114     if (getPrependCurrSym())
1115     {
1116         OUString sSymbol = getCurrencySymbol();
1117         sSymbol = comphelper::string::strip(sSymbol, ' ');
1118 
1119         OUStringBuffer sTemp("[$");
1120         sTemp.append(sSymbol);
1121         sTemp.append("] ");
1122         sTemp.append(sNewFormat);
1123 
1124         // for negative values : $ -0.00, not -$ 0.00...
1125         // (the real solution would be a possibility to choose a "positive currency format" and a "negative currency format"...
1126         // But not now... (and hey, you could take a formatted field for this...))
1127         // FS - 31.03.00 74642
1128         sTemp.append(";[$");
1129         sTemp.append(sSymbol);
1130         sTemp.append("] -");
1131         sTemp.append(sNewFormat);
1132 
1133         sNewFormat = sTemp;
1134     }
1135     else
1136     {
1137         OUString sTemp = getCurrencySymbol();
1138         sTemp = comphelper::string::strip(sTemp, ' ');
1139 
1140         sNewFormat.append(" [$");
1141         sNewFormat.append(sTemp);
1142         sNewFormat.append(']');
1143     }
1144 
1145     // set this new basic format
1146     static_cast<DoubleCurrencyFormatter*>(m_pFormatter)->GuardSetFormat(sNewFormat.makeStringAndClear(), eLanguage);
1147 }
1148 
FormattedField(vcl::Window * pParent,WinBits nStyle)1149 FormattedField::FormattedField(vcl::Window* pParent, WinBits nStyle)
1150     : SpinField(pParent, nStyle, WindowType::FORMATTEDFIELD)
1151     , m_pFormatter(nullptr)
1152 {
1153 }
1154 
dispose()1155 void FormattedField::dispose()
1156 {
1157     m_pFormatter = nullptr;
1158     m_xOwnFormatter.reset();
1159     SpinField::dispose();
1160 }
1161 
SetText(const OUString & rStr)1162 void FormattedField::SetText(const OUString& rStr)
1163 {
1164     GetFormatter().SetFieldText(rStr, Selection(0, 0));
1165 }
1166 
SetText(const OUString & rStr,const Selection & rNewSelection)1167 void FormattedField::SetText(const OUString& rStr, const Selection& rNewSelection)
1168 {
1169     GetFormatter().SetFieldText(rStr, rNewSelection);
1170     SetSelection(rNewSelection);
1171 }
1172 
set_property(const OString & rKey,const OUString & rValue)1173 bool FormattedField::set_property(const OString &rKey, const OUString &rValue)
1174 {
1175     if (rKey == "digits")
1176         GetFormatter().SetDecimalDigits(rValue.toInt32());
1177     else if (rKey == "wrap")
1178         GetFormatter().SetWrapOnLimits(toBool(rValue));
1179     else
1180         return SpinField::set_property(rKey, rValue);
1181     return true;
1182 }
1183 
Up()1184 void FormattedField::Up()
1185 {
1186     Formatter& rFormatter = GetFormatter();
1187     auto nScale = weld::SpinButton::Power10(rFormatter.GetDecimalDigits());
1188 
1189     sal_Int64 nValue = std::round(rFormatter.GetValue() * nScale);
1190     sal_Int64 nSpinSize = std::round(rFormatter.GetSpinSize() * nScale);
1191     sal_Int64 nRemainder = rFormatter.GetDisableRemainderFactor() ? 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;
1196 
1197     // setValue handles under- and overflows (min/max) automatically
1198     rFormatter.SetValue(static_cast<double>(nValue) / nScale);
1199     SetModifyFlag();
1200     Modify();
1201 
1202     SpinField::Up();
1203 }
1204 
Down()1205 void FormattedField::Down()
1206 {
1207     Formatter& rFormatter = GetFormatter();
1208     auto nScale = weld::SpinButton::Power10(rFormatter.GetDecimalDigits());
1209 
1210     sal_Int64 nValue = std::round(rFormatter.GetValue() * nScale);
1211     sal_Int64 nSpinSize = std::round(rFormatter.GetSpinSize() * nScale);
1212     sal_Int64 nRemainder = rFormatter.GetDisableRemainderFactor() ? 0 : nValue % nSpinSize;
1213     if (nValue >= 0)
1214         nValue = (nRemainder == 0) ? nValue - nSpinSize : nValue - nRemainder;
1215     else
1216         nValue = (nRemainder == 0) ? nValue - nSpinSize : nValue - nSpinSize - nRemainder;
1217 
1218     // setValue handles under- and overflows (min/max) automatically
1219     rFormatter.SetValue(static_cast<double>(nValue) / nScale);
1220     SetModifyFlag();
1221     Modify();
1222 
1223     SpinField::Down();
1224 }
1225 
First()1226 void FormattedField::First()
1227 {
1228     Formatter& rFormatter = GetFormatter();
1229     if (rFormatter.HasMinValue())
1230     {
1231         rFormatter.SetValue(rFormatter.GetMinValue());
1232         SetModifyFlag();
1233         Modify();
1234     }
1235 
1236     SpinField::First();
1237 }
1238 
Last()1239 void FormattedField::Last()
1240 {
1241     Formatter& rFormatter = GetFormatter();
1242     if (rFormatter.HasMaxValue())
1243     {
1244         rFormatter.SetValue(rFormatter.GetMaxValue());
1245         SetModifyFlag();
1246         Modify();
1247     }
1248 
1249     SpinField::Last();
1250 }
1251 
Modify()1252 void FormattedField::Modify()
1253 {
1254     GetFormatter().Modify();
1255 }
1256 
PreNotify(NotifyEvent & rNEvt)1257 bool FormattedField::PreNotify(NotifyEvent& rNEvt)
1258 {
1259     if (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT)
1260         GetFormatter().SetLastSelection(GetSelection());
1261     return SpinField::PreNotify(rNEvt);
1262 }
1263 
EventNotify(NotifyEvent & rNEvt)1264 bool FormattedField::EventNotify(NotifyEvent& rNEvt)
1265 {
1266     if ((rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !IsReadOnly())
1267     {
1268         const KeyEvent& rKEvt = *rNEvt.GetKeyEvent();
1269         sal_uInt16 nMod = rKEvt.GetKeyCode().GetModifier();
1270         switch ( rKEvt.GetKeyCode().GetCode() )
1271         {
1272             case KEY_UP:
1273             case KEY_DOWN:
1274             case KEY_PAGEUP:
1275             case KEY_PAGEDOWN:
1276             {
1277                 Formatter& rFormatter = GetFormatter();
1278                 if (!nMod && rFormatter.GetOrCreateFormatter()->IsTextFormat(rFormatter.GetFormatKey()))
1279                 {
1280                     // the base class would translate this into calls to Up/Down/First/Last,
1281                     // but we don't want this if we are text-formatted
1282                     return true;
1283                 }
1284             }
1285         }
1286     }
1287 
1288     if ((rNEvt.GetType() == MouseNotifyEvent::COMMAND) && !IsReadOnly())
1289     {
1290         const CommandEvent* pCommand = rNEvt.GetCommandEvent();
1291         if (pCommand->GetCommand() == CommandEventId::Wheel)
1292         {
1293             const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData();
1294             Formatter& rFormatter = GetFormatter();
1295             if ((pData->GetMode() == CommandWheelMode::SCROLL) &&
1296                 rFormatter.GetOrCreateFormatter()->IsTextFormat(rFormatter.GetFormatKey()))
1297             {
1298                 // same as above : prevent the base class from doing Up/Down-calls
1299                 // (normally I should put this test into the Up/Down methods itself, shouldn't I ?)
1300                 // FS - 71553 - 19.01.00
1301                 return true;
1302             }
1303         }
1304     }
1305 
1306     if (rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS && m_pFormatter)
1307         m_pFormatter->EntryLostFocus();
1308 
1309     return SpinField::EventNotify( rNEvt );
1310 }
1311 
GetFormatter()1312 Formatter& FormattedField::GetFormatter()
1313 {
1314     if (!m_pFormatter)
1315     {
1316         m_xOwnFormatter.reset(new FieldFormatter(*this));
1317         m_pFormatter = m_xOwnFormatter.get();
1318     }
1319     return *m_pFormatter;
1320 }
1321 
SetFormatter(Formatter * pFormatter)1322 void FormattedField::SetFormatter(Formatter* pFormatter)
1323 {
1324     m_xOwnFormatter.reset();
1325     m_pFormatter = pFormatter;
1326 }
1327 
1328 // currently used by online
SetValueFromString(const OUString & rStr)1329 void FormattedField::SetValueFromString(const OUString& rStr)
1330 {
1331     sal_Int32 nEnd;
1332     rtl_math_ConversionStatus eStatus;
1333     Formatter& rFormatter = GetFormatter();
1334     double fValue = ::rtl::math::stringToDouble(rStr, '.', rFormatter.GetDecimalDigits(), &eStatus, &nEnd );
1335 
1336     if (eStatus == rtl_math_ConversionStatus_Ok &&
1337         nEnd == rStr.getLength())
1338     {
1339         rFormatter.SetValue(fValue);
1340         SetModifyFlag();
1341         Modify();
1342 
1343         // Notify the value has changed
1344         SpinField::Up();
1345     }
1346     else
1347     {
1348         SAL_WARN("vcl", "fail to convert the value: " << rStr);
1349     }
1350 }
1351 
DumpAsPropertyTree(tools::JsonWriter & rJsonWriter)1352 void FormattedField::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
1353 {
1354     SpinField::DumpAsPropertyTree(rJsonWriter);
1355     Formatter& rFormatter = GetFormatter();
1356     rJsonWriter.put("min", rFormatter.GetMinValue());
1357     rJsonWriter.put("max", rFormatter.GetMaxValue());
1358     rJsonWriter.put("value", rFormatter.GetValue());
1359     rJsonWriter.put("step", rFormatter.GetSpinSize());
1360 }
1361 
GetUITestFactory() const1362 FactoryFunction FormattedField::GetUITestFactory() const
1363 {
1364     return FormattedFieldUIObject::create;
1365 }
1366 
1367 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1368