bump product version to 6.4.0.3
[LibreOffice.git] / vcl / source / control / fmtfield.cxx
blob7ae57b9f62b7823b9d0dddcd94df85556b37cb23
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <tools/debug.hxx>
21 #include <comphelper/processfactory.hxx>
22 #include <comphelper/string.hxx>
23 #include <unotools/localedatawrapper.hxx>
24 #include <vcl/event.hxx>
25 #include <vcl/settings.hxx>
26 #include <vcl/commandevent.hxx>
27 #include <svl/zformat.hxx>
28 #include <vcl/fmtfield.hxx>
29 #include <vcl/weld.hxx>
30 #include <i18nlangtag/languagetag.hxx>
31 #include <unotools/syslocale.hxx>
32 #include <map>
33 #include <rtl/math.hxx>
34 #include <rtl/ustrbuf.hxx>
35 #include <sal/log.hxx>
36 #include <osl/diagnose.h>
38 using namespace ::com::sun::star::lang;
39 using namespace ::com::sun::star::util;
41 // hmm. No support for regular expression. Well, I always (not really :) wanted to write a finite automat
42 // so here comes a finite automat ...
44 namespace validation
46 // the states of our automat.
47 enum State
49 START, // at the very start of the string
50 NUM_START, // the very start of the number
52 DIGIT_PRE_COMMA, // some pre-comma digits are read, perhaps including some thousand separators
54 DIGIT_POST_COMMA, // reading digits after the comma
55 EXPONENT_START, // at the very start of the exponent value
56 // (means: not including the "e" which denotes the exponent)
57 EXPONENT_DIGIT, // currently reading the digits of the exponent
59 END // reached the end of the string
62 // a row in the transition table (means the set of states to be reached from a given state)
63 typedef ::std::map< sal_Unicode, State > StateTransitions;
65 // a single transition
66 typedef StateTransitions::value_type Transition;
68 // the complete transition table
69 typedef ::std::map< State, StateTransitions > TransitionTable;
71 // the validator class
72 class NumberValidator
74 private:
75 TransitionTable m_aTransitions;
77 public:
78 NumberValidator( const sal_Unicode _cThSep, const sal_Unicode _cDecSep );
80 bool isValidNumericFragment( const OUString& _rText );
82 private:
83 bool implValidateNormalized( const OUString& _rText );
86 static void lcl_insertStopTransition( StateTransitions& _rRow )
88 _rRow.insert( Transition( '_', END ) );
91 static void lcl_insertStartExponentTransition( StateTransitions& _rRow )
93 _rRow.insert( Transition( 'e', EXPONENT_START ) );
96 static void lcl_insertSignTransitions( StateTransitions& _rRow, const State eNextState )
98 _rRow.insert( Transition( '-', eNextState ) );
99 _rRow.insert( Transition( '+', eNextState ) );
102 static void lcl_insertDigitTransitions( StateTransitions& _rRow, const State eNextState )
104 for ( sal_Unicode aChar = '0'; aChar <= '9'; ++aChar )
105 _rRow.insert( Transition( aChar, eNextState ) );
108 static void lcl_insertCommonPreCommaTransitions( StateTransitions& _rRow, const sal_Unicode _cThSep, const sal_Unicode _cDecSep )
110 // digits are allowed
111 lcl_insertDigitTransitions( _rRow, DIGIT_PRE_COMMA );
113 // the thousand separator is allowed
114 _rRow.insert( Transition( _cThSep, DIGIT_PRE_COMMA ) );
116 // a comma is allowed
117 _rRow.insert( Transition( _cDecSep, DIGIT_POST_COMMA ) );
120 NumberValidator::NumberValidator( const sal_Unicode _cThSep, const sal_Unicode _cDecSep )
122 // build up our transition table
124 // how to proceed from START
126 StateTransitions& rRow = m_aTransitions[ START ];
127 rRow.insert( Transition( '_', NUM_START ) );
128 // if we encounter the normalizing character, we want to proceed with the number
131 // how to proceed from NUM_START
133 StateTransitions& rRow = m_aTransitions[ NUM_START ];
135 // a sign is allowed
136 lcl_insertSignTransitions( rRow, DIGIT_PRE_COMMA );
138 // common transitions for the two pre-comma states
139 lcl_insertCommonPreCommaTransitions( rRow, _cThSep, _cDecSep );
141 // the exponent may start here
142 // (this would mean string like "_+e10_", but this is a valid fragment, though no valid number)
143 lcl_insertStartExponentTransition( rRow );
146 // how to proceed from DIGIT_PRE_COMMA
148 StateTransitions& rRow = m_aTransitions[ DIGIT_PRE_COMMA ];
150 // common transitions for the two pre-comma states
151 lcl_insertCommonPreCommaTransitions( rRow, _cThSep, _cDecSep );
153 // the exponent may start here
154 lcl_insertStartExponentTransition( rRow );
156 // the final transition indicating the end of the string
157 // (if there is no comma and no post-comma, then the string may end here)
158 lcl_insertStopTransition( rRow );
161 // how to proceed from DIGIT_POST_COMMA
163 StateTransitions& rRow = m_aTransitions[ DIGIT_POST_COMMA ];
165 // there might be digits, which would keep the state at DIGIT_POST_COMMA
166 lcl_insertDigitTransitions( rRow, DIGIT_POST_COMMA );
168 // the exponent may start here
169 lcl_insertStartExponentTransition( rRow );
171 // the string may end here
172 lcl_insertStopTransition( rRow );
175 // how to proceed from EXPONENT_START
177 StateTransitions& rRow = m_aTransitions[ EXPONENT_START ];
179 // there may be a sign
180 lcl_insertSignTransitions( rRow, EXPONENT_DIGIT );
182 // there may be digits
183 lcl_insertDigitTransitions( rRow, EXPONENT_DIGIT );
185 // the string may end here
186 lcl_insertStopTransition( rRow );
189 // how to proceed from EXPONENT_DIGIT
191 StateTransitions& rRow = m_aTransitions[ EXPONENT_DIGIT ];
193 // there may be digits
194 lcl_insertDigitTransitions( rRow, EXPONENT_DIGIT );
196 // the string may end here
197 lcl_insertStopTransition( rRow );
200 // how to proceed from END
202 /*StateTransitions& rRow =*/ m_aTransitions[ EXPONENT_DIGIT ];
203 // no valid transition to leave this state
204 // (note that we, for consistency, nevertheless want to have a row in the table)
208 bool NumberValidator::implValidateNormalized( const OUString& _rText )
210 const sal_Unicode* pCheckPos = _rText.getStr();
211 State eCurrentState = START;
213 while ( END != eCurrentState )
215 // look up the transition row for the current state
216 TransitionTable::const_iterator aRow = m_aTransitions.find( eCurrentState );
217 DBG_ASSERT( m_aTransitions.end() != aRow,
218 "NumberValidator::implValidateNormalized: invalid transition table (row not found)!" );
220 if ( m_aTransitions.end() != aRow )
222 // look up the current character in this row
223 StateTransitions::const_iterator aTransition = aRow->second.find( *pCheckPos );
224 if ( aRow->second.end() != aTransition )
226 // there is a valid transition for this character
227 eCurrentState = aTransition->second;
228 ++pCheckPos;
229 continue;
233 // if we're here, there is no valid transition
234 break;
237 DBG_ASSERT( ( END != eCurrentState ) || ( 0 == *pCheckPos ),
238 "NumberValidator::implValidateNormalized: inconsistency!" );
239 // if we're at END, then the string should be done, too - the string should be normalized, means ending
240 // a "_" and not containing any other "_" (except at the start), and "_" is the only possibility
241 // to reach the END state
243 // the string is valid if and only if we reached the final state
244 return ( END == eCurrentState );
247 bool NumberValidator::isValidNumericFragment( const OUString& _rText )
249 if ( _rText.isEmpty() )
250 // empty strings are always allowed
251 return true;
253 // normalize the string
254 OUString sNormalized = "_" + _rText + "_";
256 return implValidateNormalized( sNormalized );
260 SvNumberFormatter* FormattedField::StaticFormatter::s_cFormatter = nullptr;
261 sal_uLong FormattedField::StaticFormatter::s_nReferences = 0;
263 SvNumberFormatter* FormattedField::StaticFormatter::GetFormatter()
265 if (!s_cFormatter)
267 // get the Office's locale and translate
268 LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
269 s_cFormatter = new SvNumberFormatter(
270 ::comphelper::getProcessComponentContext(),
271 eSysLanguage);
273 return s_cFormatter;
276 FormattedField::StaticFormatter::StaticFormatter()
278 ++s_nReferences;
281 FormattedField::StaticFormatter::~StaticFormatter()
283 if (--s_nReferences == 0)
285 delete s_cFormatter;
286 s_cFormatter = nullptr;
291 FormattedField::FormattedField(vcl::Window* pParent, WinBits nStyle)
292 :SpinField(pParent, nStyle)
293 ,m_aLastSelection(0,0)
294 ,m_dMinValue(0)
295 ,m_dMaxValue(0)
296 ,m_bHasMin(false)
297 ,m_bHasMax(false)
298 ,m_bStrictFormat(true)
299 ,m_bEnableEmptyField(true)
300 ,m_bAutoColor(false)
301 ,m_bEnableNaN(false)
302 ,m_bDisableRemainderFactor(false)
303 ,m_ValueState(valueDirty)
304 ,m_dCurrentValue(0)
305 ,m_dDefaultValue(0)
306 ,m_nFormatKey(0)
307 ,m_pFormatter(nullptr)
308 ,m_dSpinSize(1)
309 ,m_dSpinFirst(-1000000)
310 ,m_dSpinLast(1000000)
311 ,m_bTreatAsNumber(true)
312 ,m_pLastOutputColor(nullptr)
313 ,m_bUseInputStringForFormatting(false)
317 void FormattedField::SetText(const OUString& rStr)
320 SpinField::SetText(rStr);
321 m_ValueState = valueDirty;
324 void FormattedField::SetText( const OUString& rStr, const Selection& rNewSelection )
327 SpinField::SetText( rStr, rNewSelection );
328 m_ValueState = valueDirty;
331 void FormattedField::SetTextFormatted(const OUString& rStr)
333 SAL_INFO_IF(ImplGetFormatter()->IsTextFormat(m_nFormatKey), "svtools",
334 "FormattedField::SetTextFormatted : valid only with text formats !");
336 m_sCurrentTextValue = rStr;
338 OUString sFormatted;
339 double dNumber = 0.0;
340 // IsNumberFormat changes the format key parameter
341 sal_uInt32 nTempFormatKey = static_cast< sal_uInt32 >( m_nFormatKey );
342 if( IsUsingInputStringForFormatting() &&
343 ImplGetFormatter()->IsNumberFormat(m_sCurrentTextValue, nTempFormatKey, dNumber) )
345 ImplGetFormatter()->GetInputLineString(dNumber, m_nFormatKey, sFormatted);
347 else
349 ImplGetFormatter()->GetOutputString(m_sCurrentTextValue,
350 m_nFormatKey,
351 sFormatted,
352 &m_pLastOutputColor);
355 // calculate the new selection
356 Selection aSel(GetSelection());
357 Selection aNewSel(aSel);
358 aNewSel.Justify();
359 sal_Int32 nNewLen = sFormatted.getLength();
360 sal_Int32 nCurrentLen = GetText().getLength();
361 if ((nNewLen > nCurrentLen) && (aNewSel.Max() == nCurrentLen))
362 { // the new text is longer and the cursor was behind the last char (of the old text)
363 if (aNewSel.Min() == 0)
364 { // the whole text was selected -> select the new text on the whole, too
365 aNewSel.Max() = nNewLen;
366 if (!nCurrentLen)
367 { // there wasn't really a previous selection (as there was no previous text), we're setting a new one -> check the selection options
368 SelectionOptions nSelOptions = GetSettings().GetStyleSettings().GetSelectionOptions();
369 if (nSelOptions & SelectionOptions::ShowFirst)
370 { // selection should be from right to left -> swap min and max
371 aNewSel.Min() = aNewSel.Max();
372 aNewSel.Max() = 0;
376 else if (aNewSel.Max() == aNewSel.Min())
377 { // there was no selection -> set the cursor behind the new last char
378 aNewSel.Max() = nNewLen;
379 aNewSel.Min() = nNewLen;
382 else if (aNewSel.Max() > nNewLen)
383 aNewSel.Max() = nNewLen;
384 else
385 aNewSel = aSel; // don't use the justified version
386 SpinField::SetText(sFormatted, aNewSel);
387 m_ValueState = valueString;
390 OUString const & FormattedField::GetTextValue() const
392 if (m_ValueState != valueString )
394 const_cast<FormattedField*>(this)->m_sCurrentTextValue = GetText();
395 const_cast<FormattedField*>(this)->m_ValueState = valueString;
397 return m_sCurrentTextValue;
400 void FormattedField::EnableNotANumber( bool _bEnable )
402 if ( m_bEnableNaN == _bEnable )
403 return;
405 m_bEnableNaN = _bEnable;
408 void FormattedField::SetAutoColor(bool _bAutomatic)
410 if (_bAutomatic == m_bAutoColor)
411 return;
413 m_bAutoColor = _bAutomatic;
414 if (m_bAutoColor)
415 { // if auto color is switched on, adjust the current text color, too
416 if (m_pLastOutputColor)
417 SetControlForeground(*m_pLastOutputColor);
418 else
419 SetControlForeground();
423 void FormattedField::impl_Modify(bool makeValueDirty)
426 if (!IsStrictFormat())
428 if(makeValueDirty)
429 m_ValueState = valueDirty;
430 SpinField::Modify();
431 return;
434 OUString sCheck = GetText();
435 if (CheckText(sCheck))
437 m_sLastValidText = sCheck;
438 m_aLastSelection = GetSelection();
439 if(makeValueDirty)
440 m_ValueState = valueDirty;
442 else
444 ImplSetTextImpl(m_sLastValidText, &m_aLastSelection);
447 SpinField::Modify();
450 void FormattedField::Modify()
453 impl_Modify();
456 void FormattedField::ImplSetTextImpl(const OUString& rNew, Selection const * pNewSel)
459 if (m_bAutoColor)
461 if (m_pLastOutputColor)
462 SetControlForeground(*m_pLastOutputColor);
463 else
464 SetControlForeground();
467 if (pNewSel)
468 SpinField::SetText(rNew, *pNewSel);
469 else
471 Selection aSel(GetSelection());
472 aSel.Justify();
474 sal_Int32 nNewLen = rNew.getLength();
475 sal_Int32 nCurrentLen = GetText().getLength();
477 if ((nNewLen > nCurrentLen) && (aSel.Max() == nCurrentLen))
478 { // new text is longer and the cursor is behind the last char
479 if (aSel.Min() == 0)
481 if (!nCurrentLen)
482 { // there wasn't really a previous selection (as there was no previous text)
483 aSel.Max() = 0;
485 else
486 { // the whole text was selected -> select the new text on the whole, too
487 aSel.Max() = nNewLen;
490 else if (aSel.Max() == aSel.Min())
491 { // there was no selection -> set the cursor behind the new last char
492 aSel.Max() = nNewLen;
493 aSel.Min() = nNewLen;
496 else if (aSel.Max() > nNewLen)
497 aSel.Max() = nNewLen;
498 SpinField::SetText(rNew, aSel);
501 m_ValueState = valueDirty; // not always necessary, but better re-evaluate for safety reasons
504 bool FormattedField::PreNotify(NotifyEvent& rNEvt)
506 if (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT)
507 m_aLastSelection = GetSelection();
508 return SpinField::PreNotify(rNEvt);
511 void FormattedField::ImplSetFormatKey(sal_uLong nFormatKey)
514 m_nFormatKey = nFormatKey;
515 bool bNeedFormatter = (m_pFormatter == nullptr) && (nFormatKey != 0);
516 if (bNeedFormatter)
518 ImplGetFormatter(); // this creates a standard formatter
520 // It might happen that the standard formatter makes no sense here, but it takes a default
521 // format. Thus, it is possible to set one of the other standard keys (which are spanning
522 // across multiple formatters).
523 m_nFormatKey = nFormatKey;
524 // When calling SetFormatKey without a formatter, the key must be one of the standard values
525 // that is available for all formatters (and, thus, also in this new one).
526 DBG_ASSERT(m_pFormatter->GetEntry(nFormatKey) != nullptr, "FormattedField::ImplSetFormatKey : invalid format key !");
530 void FormattedField::SetFormatKey(sal_uLong nFormatKey)
532 bool bNoFormatter = (m_pFormatter == nullptr);
533 ImplSetFormatKey(nFormatKey);
534 FormatChanged((bNoFormatter && (m_pFormatter != nullptr)) ? FORMAT_CHANGE_TYPE::FORMATTER : FORMAT_CHANGE_TYPE::KEYONLY);
537 void FormattedField::SetFormatter(SvNumberFormatter* pFormatter, bool bResetFormat)
540 if (bResetFormat)
542 m_pFormatter = pFormatter;
544 // calc the default format key from the Office's UI locale
545 if ( m_pFormatter )
547 // get the Office's locale and translate
548 LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
549 // get the standard numeric format for this language
550 m_nFormatKey = m_pFormatter->GetStandardFormat( SvNumFormatType::NUMBER, eSysLanguage );
552 else
553 m_nFormatKey = 0;
555 else
557 LanguageType aOldLang;
558 OUString sOldFormat = GetFormat(aOldLang);
560 sal_uInt32 nDestKey = pFormatter->TestNewString(sOldFormat);
561 if (nDestKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
563 // language of the new formatter
564 const SvNumberformat* pDefaultEntry = pFormatter->GetEntry(0);
565 LanguageType aNewLang = pDefaultEntry ? pDefaultEntry->GetLanguage() : LANGUAGE_DONTKNOW;
567 // convert the old format string into the new language
568 sal_Int32 nCheckPos;
569 SvNumFormatType nType;
570 pFormatter->PutandConvertEntry(sOldFormat, nCheckPos, nType, nDestKey, aOldLang, aNewLang, true);
571 m_nFormatKey = nDestKey;
573 m_pFormatter = pFormatter;
576 FormatChanged(FORMAT_CHANGE_TYPE::FORMATTER);
579 OUString FormattedField::GetFormat(LanguageType& eLang) const
581 const SvNumberformat* pFormatEntry = ImplGetFormatter()->GetEntry(m_nFormatKey);
582 DBG_ASSERT(pFormatEntry != nullptr, "FormattedField::GetFormat: no number format for the given format key.");
583 OUString sFormatString = pFormatEntry ? pFormatEntry->GetFormatstring() : OUString();
584 eLang = pFormatEntry ? pFormatEntry->GetLanguage() : LANGUAGE_DONTKNOW;
586 return sFormatString;
589 bool FormattedField::SetFormat(const OUString& rFormatString, LanguageType eLang)
591 sal_uInt32 nNewKey = ImplGetFormatter()->TestNewString(rFormatString, eLang);
592 if (nNewKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
594 sal_Int32 nCheckPos;
595 SvNumFormatType nType;
596 OUString rFormat(rFormatString);
597 if (!ImplGetFormatter()->PutEntry(rFormat, nCheckPos, nType, nNewKey, eLang))
598 return false;
599 DBG_ASSERT(nNewKey != NUMBERFORMAT_ENTRY_NOT_FOUND, "FormattedField::SetFormatString : PutEntry returned an invalid key !");
602 if (nNewKey != m_nFormatKey)
603 SetFormatKey(nNewKey);
604 return true;
607 bool FormattedField::GetThousandsSep() const
609 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
610 "FormattedField::GetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
612 bool bThousand, IsRed;
613 sal_uInt16 nPrecision, nLeadingCnt;
614 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
616 return bThousand;
619 void FormattedField::SetThousandsSep(bool _bUseSeparator)
621 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
622 "FormattedField::SetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
624 // get the current settings
625 bool bThousand, IsRed;
626 sal_uInt16 nPrecision, nLeadingCnt;
627 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
628 if (bThousand == _bUseSeparator)
629 return;
631 // we need the language for the following
632 LanguageType eLang;
633 GetFormat(eLang);
635 // generate a new format ...
636 OUString sFmtDescription = ImplGetFormatter()->GenerateFormat(m_nFormatKey, eLang, _bUseSeparator, IsRed, nPrecision, nLeadingCnt);
637 // ... and introduce it to the formatter
638 sal_Int32 nCheckPos = 0;
639 sal_uInt32 nNewKey;
640 SvNumFormatType nType;
641 ImplGetFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
643 // set the new key
644 ImplSetFormatKey(nNewKey);
645 FormatChanged(FORMAT_CHANGE_TYPE::THOUSANDSSEP);
648 sal_uInt16 FormattedField::GetDecimalDigits() const
650 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
651 "FormattedField::GetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
653 bool bThousand, IsRed;
654 sal_uInt16 nPrecision, nLeadingCnt;
655 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
657 return nPrecision;
660 void FormattedField::SetDecimalDigits(sal_uInt16 _nPrecision)
662 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
663 "FormattedField::SetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
665 // get the current settings
666 bool bThousand, IsRed;
667 sal_uInt16 nPrecision, nLeadingCnt;
668 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
669 if (nPrecision == _nPrecision)
670 return;
672 // we need the language for the following
673 LanguageType eLang;
674 GetFormat(eLang);
676 // generate a new format ...
677 OUString sFmtDescription = ImplGetFormatter()->GenerateFormat(m_nFormatKey, eLang, bThousand, IsRed, _nPrecision, nLeadingCnt);
678 // ... and introduce it to the formatter
679 sal_Int32 nCheckPos = 0;
680 sal_uInt32 nNewKey;
681 SvNumFormatType nType;
682 ImplGetFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
684 // set the new key
685 ImplSetFormatKey(nNewKey);
686 FormatChanged(FORMAT_CHANGE_TYPE::PRECISION);
689 void FormattedField::FormatChanged( FORMAT_CHANGE_TYPE _nWhat )
691 m_pLastOutputColor = nullptr;
693 if ( (_nWhat == FORMAT_CHANGE_TYPE::FORMATTER) && m_pFormatter )
694 m_pFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_FORMAT_INTL );
696 ReFormat();
699 void FormattedField::Commit()
701 // remember the old text
702 OUString sOld( GetText() );
704 // do the reformat
705 ReFormat();
707 // did the text change?
708 if ( GetText() != sOld )
709 { // consider the field as modified,
710 // but we already have the most recent value;
711 // don't reparse it from the text
712 // (can lead to data loss when the format is lossy,
713 // as is e.g. our default date format: 2-digit year!)
714 impl_Modify(false);
718 void FormattedField::ReFormat()
720 if (!IsEmptyFieldEnabled() || !GetText().isEmpty())
722 if (TreatingAsNumber())
724 double dValue = GetValue();
725 if ( m_bEnableNaN && ::rtl::math::isNan( dValue ) )
726 return;
727 ImplSetValue( dValue, true );
729 else
730 SetTextFormatted(GetTextValue());
734 bool FormattedField::EventNotify(NotifyEvent& rNEvt)
737 if ((rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !IsReadOnly())
739 const KeyEvent& rKEvt = *rNEvt.GetKeyEvent();
740 sal_uInt16 nMod = rKEvt.GetKeyCode().GetModifier();
741 switch ( rKEvt.GetKeyCode().GetCode() )
743 case KEY_UP:
744 case KEY_DOWN:
745 case KEY_PAGEUP:
746 case KEY_PAGEDOWN:
747 if (!nMod && ImplGetFormatter()->IsTextFormat(m_nFormatKey))
749 // the base class would translate this into calls to Up/Down/First/Last,
750 // but we don't want this if we are text-formatted
751 return true;
756 if ((rNEvt.GetType() == MouseNotifyEvent::COMMAND) && !IsReadOnly())
758 const CommandEvent* pCommand = rNEvt.GetCommandEvent();
759 if (pCommand->GetCommand() == CommandEventId::Wheel)
761 const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData();
762 if ((pData->GetMode() == CommandWheelMode::SCROLL) && ImplGetFormatter()->IsTextFormat(m_nFormatKey))
764 // same as above : prevent the base class from doing Up/Down-calls
765 // (normally I should put this test into the Up/Down methods itself, shouldn't I ?)
766 // FS - 71553 - 19.01.00
767 return true;
772 if (rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS)
774 // special treatment for empty texts
775 if (GetText().isEmpty())
777 if (!IsEmptyFieldEnabled())
779 if (TreatingAsNumber())
781 ImplSetValue(m_dCurrentValue, true);
782 Modify();
783 m_ValueState = valueDouble;
785 else
787 OUString sNew = GetTextValue();
788 if (!sNew.isEmpty())
789 SetTextFormatted(sNew);
790 else
791 SetTextFormatted(m_sDefaultText);
792 m_ValueState = valueString;
796 else
798 Commit();
802 return SpinField::EventNotify( rNEvt );
805 void FormattedField::SetMinValue(double dMin)
807 DBG_ASSERT(m_bTreatAsNumber, "FormattedField::SetMinValue : only to be used in numeric mode !");
809 m_dMinValue = dMin;
810 m_bHasMin = true;
811 // for checking the current value at the new border -> ImplSetValue
812 ReFormat();
815 void FormattedField::SetMaxValue(double dMax)
817 DBG_ASSERT(m_bTreatAsNumber, "FormattedField::SetMaxValue : only to be used in numeric mode !");
819 m_dMaxValue = dMax;
820 m_bHasMax = true;
821 // for checking the current value at the new border -> ImplSetValue
822 ReFormat();
825 void FormattedField::SetTextValue(const OUString& rText)
827 SetText(rText);
828 ReFormat();
831 void FormattedField::EnableEmptyField(bool bEnable)
833 if (bEnable == m_bEnableEmptyField)
834 return;
836 m_bEnableEmptyField = bEnable;
837 if (!m_bEnableEmptyField && GetText().isEmpty())
838 ImplSetValue(m_dCurrentValue, true);
841 void FormattedField::ImplSetValue(double dVal, bool bForce)
844 if (m_bHasMin && (dVal<m_dMinValue))
845 dVal = m_dMinValue;
846 if (m_bHasMax && (dVal>m_dMaxValue))
847 dVal = m_dMaxValue;
848 if (!bForce && (dVal == GetValue()))
849 return;
851 DBG_ASSERT(ImplGetFormatter() != nullptr, "FormattedField::ImplSetValue : can't set a value without a formatter !");
853 m_ValueState = valueDouble;
854 m_dCurrentValue = dVal;
856 if (!m_aOutputHdl.IsSet() || !m_aOutputHdl.Call(*this))
858 OUString sNewText;
859 if (ImplGetFormatter()->IsTextFormat(m_nFormatKey))
861 // first convert the number as string in standard format
862 OUString sTemp;
863 ImplGetFormatter()->GetOutputString(dVal, 0, sTemp, &m_pLastOutputColor);
864 // then encode the string in the corresponding text format
865 ImplGetFormatter()->GetOutputString(sTemp, m_nFormatKey, sNewText, &m_pLastOutputColor);
867 else
869 if( IsUsingInputStringForFormatting())
871 ImplGetFormatter()->GetInputLineString(dVal, m_nFormatKey, sNewText);
873 else
875 ImplGetFormatter()->GetOutputString(dVal, m_nFormatKey, sNewText, &m_pLastOutputColor);
878 ImplSetTextImpl(sNewText, nullptr);
879 DBG_ASSERT(CheckText(sNewText), "FormattedField::ImplSetValue : formatted string doesn't match the criteria !");
882 m_ValueState = valueDouble;
885 bool FormattedField::ImplGetValue(double& dNewVal)
887 dNewVal = m_dCurrentValue;
888 if (m_ValueState == valueDouble)
889 return true;
891 dNewVal = m_dDefaultValue;
892 OUString sText(GetText());
893 if (sText.isEmpty())
894 return true;
896 bool bUseExternalFormatterValue = false;
897 if (m_aInputHdl.IsSet())
899 sal_Int64 nResult;
900 auto eState = m_aInputHdl.Call(&nResult);
901 bUseExternalFormatterValue = eState != TRISTATE_INDET;
902 if (bUseExternalFormatterValue)
904 if (eState == TRISTATE_TRUE)
906 dNewVal = nResult;
907 dNewVal /= weld::SpinButton::Power10(GetDecimalDigits());
909 else
910 dNewVal = m_dCurrentValue;
914 if (!bUseExternalFormatterValue)
916 DBG_ASSERT(ImplGetFormatter() != nullptr, "FormattedField::ImplGetValue : can't give you a current value without a formatter !");
918 sal_uInt32 nFormatKey = m_nFormatKey; // IsNumberFormat changes the FormatKey!
920 if (ImplGetFormatter()->IsTextFormat(nFormatKey) && m_bTreatAsNumber)
921 // for detection of values like "1,1" in fields that are formatted as text
922 nFormatKey = 0;
924 // special treatment for percentage formatting
925 if (ImplGetFormatter()->GetType(m_nFormatKey) == SvNumFormatType::PERCENT)
927 // the language of our format
928 LanguageType eLanguage = m_pFormatter->GetEntry(m_nFormatKey)->GetLanguage();
929 // the default number format for this language
930 sal_uLong nStandardNumericFormat = m_pFormatter->GetStandardFormat(SvNumFormatType::NUMBER, eLanguage);
932 sal_uInt32 nTempFormat = nStandardNumericFormat;
933 double dTemp;
934 if (m_pFormatter->IsNumberFormat(sText, nTempFormat, dTemp) &&
935 SvNumFormatType::NUMBER == m_pFormatter->GetType(nTempFormat))
936 // the string is equivalent to a number formatted one (has no % sign) -> append it
937 sText += "%";
938 // (with this, an input of '3' becomes '3%', which then by the formatter is translated
939 // into 0.03. Without this, the formatter would give us the double 3 for an input '3',
940 // which equals 300 percent.
942 if (!ImplGetFormatter()->IsNumberFormat(sText, nFormatKey, dNewVal))
943 return false;
946 if (m_bHasMin && (dNewVal<m_dMinValue))
947 dNewVal = m_dMinValue;
948 if (m_bHasMax && (dNewVal>m_dMaxValue))
949 dNewVal = m_dMaxValue;
950 return true;
953 void FormattedField::SetValue(double dVal)
955 ImplSetValue(dVal, m_ValueState != valueDouble);
958 double FormattedField::GetValue()
961 if ( !ImplGetValue( m_dCurrentValue ) )
963 if ( m_bEnableNaN )
964 ::rtl::math::setNan( &m_dCurrentValue );
965 else
966 m_dCurrentValue = m_dDefaultValue;
969 m_ValueState = valueDouble;
970 return m_dCurrentValue;
973 void FormattedField::DisableRemainderFactor()
975 m_bDisableRemainderFactor = true;
978 bool FormattedField::set_property(const OString &rKey, const OUString &rValue)
980 if (rKey == "digits")
981 SetDecimalDigits(rValue.toInt32());
982 else
983 return SpinField::set_property(rKey, rValue);
984 return true;
987 void FormattedField::Up()
989 auto nScale = weld::SpinButton::Power10(GetDecimalDigits());
991 sal_Int64 nValue = std::round(GetValue() * nScale);
992 sal_Int64 nSpinSize = std::round(m_dSpinSize * nScale);
993 sal_Int64 nRemainder = m_bDisableRemainderFactor ? 0 : nValue % nSpinSize;
994 if (nValue >= 0)
995 nValue = (nRemainder == 0) ? nValue + nSpinSize : nValue + nSpinSize - nRemainder;
996 else
997 nValue = (nRemainder == 0) ? nValue + nSpinSize : nValue - nRemainder;
999 // setValue handles under- and overflows (min/max) automatically
1000 SetValue(static_cast<double>(nValue) / nScale);
1001 SetModifyFlag();
1002 Modify();
1004 SpinField::Up();
1007 void FormattedField::Down()
1009 auto nScale = weld::SpinButton::Power10(GetDecimalDigits());
1011 sal_Int64 nValue = std::round(GetValue() * nScale);
1012 sal_Int64 nSpinSize = std::round(m_dSpinSize * nScale);
1013 sal_Int64 nRemainder = m_bDisableRemainderFactor ? 0 : nValue % nSpinSize;
1014 if (nValue >= 0)
1015 nValue = (nRemainder == 0) ? nValue - nSpinSize : nValue - nRemainder;
1016 else
1017 nValue = (nRemainder == 0) ? nValue - nSpinSize : nValue - nSpinSize - nRemainder;
1019 // setValue handles under- and overflows (min/max) automatically
1020 SetValue(static_cast<double>(nValue) / nScale);
1021 SetModifyFlag();
1022 Modify();
1024 SpinField::Down();
1027 void FormattedField::First()
1029 if (m_bHasMin)
1031 SetValue(m_dMinValue);
1032 SetModifyFlag();
1033 Modify();
1036 SpinField::First();
1039 void FormattedField::Last()
1041 if (m_bHasMax)
1043 SetValue(m_dMaxValue);
1044 SetModifyFlag();
1045 Modify();
1048 SpinField::Last();
1051 void FormattedField::UseInputStringForFormatting()
1053 m_bUseInputStringForFormatting = true;
1056 DoubleNumericField::DoubleNumericField(vcl::Window* pParent, WinBits nStyle)
1057 : FormattedField(pParent, nStyle)
1059 ResetConformanceTester();
1062 DoubleNumericField::~DoubleNumericField() = default;
1064 void DoubleNumericField::FormatChanged(FORMAT_CHANGE_TYPE nWhat)
1066 ResetConformanceTester();
1067 FormattedField::FormatChanged(nWhat);
1070 bool DoubleNumericField::CheckText(const OUString& sText) const
1072 // We'd like to implement this using the NumberFormatter::IsNumberFormat, but unfortunately, this doesn't
1073 // recognize fragments of numbers (like, for instance "1e", which happens during entering e.g. "1e10")
1074 // Thus, the roundabout way via a regular expression
1075 return m_pNumberValidator->isValidNumericFragment( sText );
1078 void DoubleNumericField::ResetConformanceTester()
1080 // the thousands and the decimal separator are language dependent
1081 const SvNumberformat* pFormatEntry = ImplGetFormatter()->GetEntry(m_nFormatKey);
1083 sal_Unicode cSeparatorThousand = ',';
1084 sal_Unicode cSeparatorDecimal = '.';
1085 if (pFormatEntry)
1087 LocaleDataWrapper aLocaleInfo( LanguageTag( pFormatEntry->GetLanguage()) );
1089 OUString sSeparator = aLocaleInfo.getNumThousandSep();
1090 if (!sSeparator.isEmpty())
1091 cSeparatorThousand = sSeparator[0];
1093 sSeparator = aLocaleInfo.getNumDecimalSep();
1094 if (!sSeparator.isEmpty())
1095 cSeparatorDecimal = sSeparator[0];
1098 m_pNumberValidator.reset(new validation::NumberValidator( cSeparatorThousand, cSeparatorDecimal ));
1101 DoubleCurrencyField::DoubleCurrencyField(vcl::Window* pParent, WinBits nStyle)
1102 :FormattedField(pParent, nStyle)
1103 ,m_bChangingFormat(false)
1105 m_bPrependCurrSym = false;
1107 // initialize with a system currency format
1108 m_sCurrencySymbol = SvtSysLocale().GetLocaleData().getCurrSymbol();
1109 UpdateCurrencyFormat();
1112 void DoubleCurrencyField::FormatChanged(FORMAT_CHANGE_TYPE nWhat)
1114 if (m_bChangingFormat)
1116 FormattedField::FormatChanged(nWhat);
1117 return;
1120 switch (nWhat)
1122 case FORMAT_CHANGE_TYPE::FORMATTER:
1123 case FORMAT_CHANGE_TYPE::PRECISION:
1124 case FORMAT_CHANGE_TYPE::THOUSANDSSEP:
1125 // the aspects which changed don't take our currency settings into account (in fact, they most probably
1126 // destroyed them)
1127 UpdateCurrencyFormat();
1128 break;
1129 case FORMAT_CHANGE_TYPE::KEYONLY:
1130 OSL_FAIL("DoubleCurrencyField::FormatChanged : somebody modified my key !");
1131 // We always build our own format from the settings we get via special methods (setCurrencySymbol etc.).
1132 // Nobody but ourself should modify the format key directly!
1133 break;
1134 default: break;
1137 FormattedField::FormatChanged(nWhat);
1140 void DoubleCurrencyField::setCurrencySymbol(const OUString& rSymbol)
1142 if (m_sCurrencySymbol == rSymbol)
1143 return;
1145 m_sCurrencySymbol = rSymbol;
1146 UpdateCurrencyFormat();
1147 FormatChanged(FORMAT_CHANGE_TYPE::CURRENCY_SYMBOL);
1150 void DoubleCurrencyField::setPrependCurrSym(bool _bPrepend)
1152 if (m_bPrependCurrSym == _bPrepend)
1153 return;
1155 m_bPrependCurrSym = _bPrepend;
1156 UpdateCurrencyFormat();
1157 FormatChanged(FORMAT_CHANGE_TYPE::CURRSYM_POSITION);
1160 void DoubleCurrencyField::UpdateCurrencyFormat()
1162 // the old settings
1163 LanguageType eLanguage;
1164 GetFormat(eLanguage);
1165 bool bThSep = GetThousandsSep();
1166 sal_uInt16 nDigits = GetDecimalDigits();
1168 // build a new format string with the base class' and my own settings
1170 /* Strangely with gcc 4.6.3 this needs a temporary LanguageTag, otherwise
1171 * there's
1172 * error: request for member 'getNumThousandSep' in 'aLocaleInfo', which is
1173 * of non-class type 'LocaleDataWrapper(LanguageTag)' */
1174 LanguageTag aLanguageTag( eLanguage);
1175 LocaleDataWrapper aLocaleInfo( aLanguageTag );
1177 OUStringBuffer sNewFormat;
1178 if (bThSep)
1180 sNewFormat.append('#');
1181 sNewFormat.append(aLocaleInfo.getNumThousandSep());
1182 sNewFormat.append("##0");
1184 else
1185 sNewFormat.append('0');
1187 if (nDigits)
1189 sNewFormat.append(aLocaleInfo.getNumDecimalSep());
1191 OUStringBuffer sTemp;
1192 comphelper::string::padToLength(sTemp, nDigits, '0');
1193 sNewFormat.append(sTemp);
1196 if (getPrependCurrSym())
1198 OUString sSymbol = getCurrencySymbol();
1199 sSymbol = comphelper::string::stripStart(sSymbol, ' ');
1200 sSymbol = comphelper::string::stripEnd(sSymbol, ' ');
1202 OUStringBuffer sTemp("[$");
1203 sTemp.append(sSymbol);
1204 sTemp.append("] ");
1205 sTemp.append(sNewFormat);
1207 // for negative values : $ -0.00, not -$ 0.00...
1208 // (the real solution would be a possibility to choose a "positive currency format" and a "negative currency format"...
1209 // But not now... (and hey, you could take a formatted field for this...))
1210 // FS - 31.03.00 74642
1211 sTemp.append(";[$");
1212 sTemp.append(sSymbol);
1213 sTemp.append("] -");
1214 sTemp.append(sNewFormat);
1216 sNewFormat = sTemp;
1218 else
1220 OUString sTemp = getCurrencySymbol();
1221 sTemp = comphelper::string::stripStart(sTemp, ' ');
1222 sTemp = comphelper::string::stripEnd(sTemp, ' ');
1224 sNewFormat.append(" [$");
1225 sNewFormat.append(sTemp);
1226 sNewFormat.append(']');
1229 // set this new basic format
1230 m_bChangingFormat = true;
1231 SetFormat(sNewFormat.makeStringAndClear(), eLanguage);
1232 m_bChangingFormat = false;
1235 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */