1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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 ...
46 // the states of our automat.
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
75 TransitionTable m_aTransitions
;
78 NumberValidator( const sal_Unicode _cThSep
, const sal_Unicode _cDecSep
);
80 bool isValidNumericFragment( const OUString
& _rText
);
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
];
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
;
233 // if we're here, there is no valid transition
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
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()
267 // get the Office's locale and translate
268 LanguageType eSysLanguage
= SvtSysLocale().GetLanguageTag().getLanguageType( false);
269 s_cFormatter
= new SvNumberFormatter(
270 ::comphelper::getProcessComponentContext(),
276 FormattedField::StaticFormatter::StaticFormatter()
281 FormattedField::StaticFormatter::~StaticFormatter()
283 if (--s_nReferences
== 0)
286 s_cFormatter
= nullptr;
291 FormattedField::FormattedField(vcl::Window
* pParent
, WinBits nStyle
)
292 :SpinField(pParent
, nStyle
)
293 ,m_aLastSelection(0,0)
298 ,m_bStrictFormat(true)
299 ,m_bEnableEmptyField(true)
302 ,m_bDisableRemainderFactor(false)
303 ,m_ValueState(valueDirty
)
307 ,m_pFormatter(nullptr)
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
;
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
);
349 ImplGetFormatter()->GetOutputString(m_sCurrentTextValue
,
352 &m_pLastOutputColor
);
355 // calculate the new selection
356 Selection
aSel(GetSelection());
357 Selection
aNewSel(aSel
);
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
;
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();
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
;
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
)
405 m_bEnableNaN
= _bEnable
;
408 void FormattedField::SetAutoColor(bool _bAutomatic
)
410 if (_bAutomatic
== m_bAutoColor
)
413 m_bAutoColor
= _bAutomatic
;
415 { // if auto color is switched on, adjust the current text color, too
416 if (m_pLastOutputColor
)
417 SetControlForeground(*m_pLastOutputColor
);
419 SetControlForeground();
423 void FormattedField::impl_Modify(bool makeValueDirty
)
426 if (!IsStrictFormat())
429 m_ValueState
= valueDirty
;
434 OUString sCheck
= GetText();
435 if (CheckText(sCheck
))
437 m_sLastValidText
= sCheck
;
438 m_aLastSelection
= GetSelection();
440 m_ValueState
= valueDirty
;
444 ImplSetTextImpl(m_sLastValidText
, &m_aLastSelection
);
450 void FormattedField::Modify()
456 void FormattedField::ImplSetTextImpl(const OUString
& rNew
, Selection
const * pNewSel
)
461 if (m_pLastOutputColor
)
462 SetControlForeground(*m_pLastOutputColor
);
464 SetControlForeground();
468 SpinField::SetText(rNew
, *pNewSel
);
471 Selection
aSel(GetSelection());
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
482 { // there wasn't really a previous selection (as there was no previous text)
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);
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
)
542 m_pFormatter
= pFormatter
;
544 // calc the default format key from the Office's UI locale
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
);
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
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
)
595 SvNumFormatType nType
;
596 OUString
rFormat(rFormatString
);
597 if (!ImplGetFormatter()->PutEntry(rFormat
, nCheckPos
, nType
, nNewKey
, eLang
))
599 DBG_ASSERT(nNewKey
!= NUMBERFORMAT_ENTRY_NOT_FOUND
, "FormattedField::SetFormatString : PutEntry returned an invalid key !");
602 if (nNewKey
!= m_nFormatKey
)
603 SetFormatKey(nNewKey
);
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
);
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
)
631 // we need the language for the following
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;
640 SvNumFormatType nType
;
641 ImplGetFormatter()->PutEntry(sFmtDescription
, nCheckPos
, nType
, nNewKey
, eLang
);
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
);
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
)
672 // we need the language for the following
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;
681 SvNumFormatType nType
;
682 ImplGetFormatter()->PutEntry(sFmtDescription
, nCheckPos
, nType
, nNewKey
, eLang
);
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
);
699 void FormattedField::Commit()
701 // remember the old text
702 OUString
sOld( GetText() );
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!)
718 void FormattedField::ReFormat()
720 if (!IsEmptyFieldEnabled() || !GetText().isEmpty())
722 if (TreatingAsNumber())
724 double dValue
= GetValue();
725 if ( m_bEnableNaN
&& ::rtl::math::isNan( dValue
) )
727 ImplSetValue( dValue
, true );
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() )
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
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
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);
783 m_ValueState
= valueDouble
;
787 OUString sNew
= GetTextValue();
789 SetTextFormatted(sNew
);
791 SetTextFormatted(m_sDefaultText
);
792 m_ValueState
= valueString
;
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 !");
811 // for checking the current value at the new border -> ImplSetValue
815 void FormattedField::SetMaxValue(double dMax
)
817 DBG_ASSERT(m_bTreatAsNumber
, "FormattedField::SetMaxValue : only to be used in numeric mode !");
821 // for checking the current value at the new border -> ImplSetValue
825 void FormattedField::SetTextValue(const OUString
& rText
)
831 void FormattedField::EnableEmptyField(bool bEnable
)
833 if (bEnable
== m_bEnableEmptyField
)
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
))
846 if (m_bHasMax
&& (dVal
>m_dMaxValue
))
848 if (!bForce
&& (dVal
== GetValue()))
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))
859 if (ImplGetFormatter()->IsTextFormat(m_nFormatKey
))
861 // first convert the number as string in standard format
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
);
869 if( IsUsingInputStringForFormatting())
871 ImplGetFormatter()->GetInputLineString(dVal
, m_nFormatKey
, sNewText
);
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
)
891 dNewVal
= m_dDefaultValue
;
892 OUString
sText(GetText());
896 bool bUseExternalFormatterValue
= false;
897 if (m_aInputHdl
.IsSet())
900 auto eState
= m_aInputHdl
.Call(&nResult
);
901 bUseExternalFormatterValue
= eState
!= TRISTATE_INDET
;
902 if (bUseExternalFormatterValue
)
904 if (eState
== TRISTATE_TRUE
)
907 dNewVal
/= weld::SpinButton::Power10(GetDecimalDigits());
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
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
;
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
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
))
946 if (m_bHasMin
&& (dNewVal
<m_dMinValue
))
947 dNewVal
= m_dMinValue
;
948 if (m_bHasMax
&& (dNewVal
>m_dMaxValue
))
949 dNewVal
= m_dMaxValue
;
953 void FormattedField::SetValue(double dVal
)
955 ImplSetValue(dVal
, m_ValueState
!= valueDouble
);
958 double FormattedField::GetValue()
961 if ( !ImplGetValue( m_dCurrentValue
) )
964 ::rtl::math::setNan( &m_dCurrentValue
);
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());
983 return SpinField::set_property(rKey
, rValue
);
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
;
995 nValue
= (nRemainder
== 0) ? nValue
+ nSpinSize
: nValue
+ nSpinSize
- nRemainder
;
997 nValue
= (nRemainder
== 0) ? nValue
+ nSpinSize
: nValue
- nRemainder
;
999 // setValue handles under- and overflows (min/max) automatically
1000 SetValue(static_cast<double>(nValue
) / nScale
);
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
;
1015 nValue
= (nRemainder
== 0) ? nValue
- nSpinSize
: nValue
- nRemainder
;
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
);
1027 void FormattedField::First()
1031 SetValue(m_dMinValue
);
1039 void FormattedField::Last()
1043 SetValue(m_dMaxValue
);
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
= '.';
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
);
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
1127 UpdateCurrencyFormat();
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!
1137 FormattedField::FormatChanged(nWhat
);
1140 void DoubleCurrencyField::setCurrencySymbol(const OUString
& rSymbol
)
1142 if (m_sCurrencySymbol
== rSymbol
)
1145 m_sCurrencySymbol
= rSymbol
;
1146 UpdateCurrencyFormat();
1147 FormatChanged(FORMAT_CHANGE_TYPE::CURRENCY_SYMBOL
);
1150 void DoubleCurrencyField::setPrependCurrSym(bool _bPrepend
)
1152 if (m_bPrependCurrSym
== _bPrepend
)
1155 m_bPrependCurrSym
= _bPrepend
;
1156 UpdateCurrencyFormat();
1157 FormatChanged(FORMAT_CHANGE_TYPE::CURRSYM_POSITION
);
1160 void DoubleCurrencyField::UpdateCurrencyFormat()
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
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
;
1180 sNewFormat
.append('#');
1181 sNewFormat
.append(aLocaleInfo
.getNumThousandSep());
1182 sNewFormat
.append("##0");
1185 sNewFormat
.append('0');
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
);
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
);
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: */