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