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/svapp.hxx>
25 #include <vcl/builderfactory.hxx>
26 #include <vcl/settings.hxx>
27 #include <svl/zformat.hxx>
28 #include <svtools/fmtfield.hxx>
29 #include <i18nlangtag/languagetag.hxx>
30 #include <com/sun/star/lang/Locale.hpp>
31 #include <com/sun/star/util/SearchOptions.hpp>
32 #include <com/sun/star/util/SearchAlgorithms.hpp>
33 #include <com/sun/star/util/SearchResult.hpp>
34 #include <com/sun/star/util/SearchFlags.hpp>
35 #include <unotools/syslocale.hxx>
37 #include <rtl/math.hxx>
38 #include <rtl/ustrbuf.hxx>
40 using namespace ::com::sun::star::lang
;
41 using namespace ::com::sun::star::util
;
43 // hmm. No support for regular expression. Well, I always (not really :) wanted to write a finite automat
44 // so here comes a finite automat ...
48 // the states of our automat.
51 START
, // at the very start of the string
52 NUM_START
, // the very start of the number
54 DIGIT_PRE_COMMA
, // some pre-comma digits are read, perhaps including some thousand separators
56 DIGIT_POST_COMMA
, // reading digits after the comma
57 EXPONENT_START
, // at the very start of the exponent value
58 // (means: not including the "e" which denotes the exponent)
59 EXPONENT_DIGIT
, // currently reading the digits of the exponent
61 END
// reached the end of the string
64 // a row in the transition table (means the set of states to be reached from a given state)
65 typedef ::std::map
< sal_Unicode
, State
> StateTransitions
;
67 // a single transition
68 typedef StateTransitions::value_type Transition
;
70 // the complete transition table
71 typedef ::std::map
< State
, StateTransitions
> TransitionTable
;
73 // the validator class
77 TransitionTable m_aTransitions
;
78 const sal_Unicode m_cThSep
;
79 const sal_Unicode m_cDecSep
;
82 NumberValidator( const sal_Unicode _cThSep
, const sal_Unicode _cDecSep
);
84 bool isValidNumericFragment( const OUString
& _rText
);
87 bool implValidateNormalized( const OUString
& _rText
);
90 static void lcl_insertStopTransition( StateTransitions
& _rRow
)
92 _rRow
.insert( Transition( '_', END
) );
95 static void lcl_insertStartExponentTransition( StateTransitions
& _rRow
)
97 _rRow
.insert( Transition( 'e', EXPONENT_START
) );
100 static void lcl_insertSignTransitions( StateTransitions
& _rRow
, const State eNextState
)
102 _rRow
.insert( Transition( '-', eNextState
) );
103 _rRow
.insert( Transition( '+', eNextState
) );
106 static void lcl_insertDigitTransitions( StateTransitions
& _rRow
, const State eNextState
)
108 for ( sal_Unicode aChar
= '0'; aChar
<= '9'; ++aChar
)
109 _rRow
.insert( Transition( aChar
, eNextState
) );
112 static void lcl_insertCommonPreCommaTransitions( StateTransitions
& _rRow
, const sal_Unicode _cThSep
, const sal_Unicode _cDecSep
)
114 // digits are allowed
115 lcl_insertDigitTransitions( _rRow
, DIGIT_PRE_COMMA
);
117 // the thousand separator is allowed
118 _rRow
.insert( Transition( _cThSep
, DIGIT_PRE_COMMA
) );
120 // a comma is allowed
121 _rRow
.insert( Transition( _cDecSep
, DIGIT_POST_COMMA
) );
124 NumberValidator::NumberValidator( const sal_Unicode _cThSep
, const sal_Unicode _cDecSep
)
126 ,m_cDecSep( _cDecSep
)
128 // build up our transition table
130 // how to proceed from START
132 StateTransitions
& rRow
= m_aTransitions
[ START
];
133 rRow
.insert( Transition( '_', NUM_START
) );
134 // if we encounter the normalizing character, we want to proceed with the number
137 // how to proceed from NUM_START
139 StateTransitions
& rRow
= m_aTransitions
[ NUM_START
];
142 lcl_insertSignTransitions( rRow
, DIGIT_PRE_COMMA
);
144 // common transitions for the two pre-comma states
145 lcl_insertCommonPreCommaTransitions( rRow
, m_cThSep
, m_cDecSep
);
147 // the exponent may start here
148 // (this would mean string like "_+e10_", but this is a valid fragment, though no valid number)
149 lcl_insertStartExponentTransition( rRow
);
152 // how to proceed from DIGIT_PRE_COMMA
154 StateTransitions
& rRow
= m_aTransitions
[ DIGIT_PRE_COMMA
];
156 // common transitions for the two pre-comma states
157 lcl_insertCommonPreCommaTransitions( rRow
, m_cThSep
, m_cDecSep
);
159 // the exponent may start here
160 lcl_insertStartExponentTransition( rRow
);
162 // the final transition indicating the end of the string
163 // (if there is no comma and no post-comma, then the string may end here)
164 lcl_insertStopTransition( rRow
);
167 // how to proceed from DIGIT_POST_COMMA
169 StateTransitions
& rRow
= m_aTransitions
[ DIGIT_POST_COMMA
];
171 // there might be digits, which would keep the state at DIGIT_POST_COMMA
172 lcl_insertDigitTransitions( rRow
, DIGIT_POST_COMMA
);
174 // the exponent may start here
175 lcl_insertStartExponentTransition( rRow
);
177 // the string may end here
178 lcl_insertStopTransition( rRow
);
181 // how to proceed from EXPONENT_START
183 StateTransitions
& rRow
= m_aTransitions
[ EXPONENT_START
];
185 // there may be a sign
186 lcl_insertSignTransitions( rRow
, EXPONENT_DIGIT
);
188 // there may be digits
189 lcl_insertDigitTransitions( rRow
, EXPONENT_DIGIT
);
191 // the string may end here
192 lcl_insertStopTransition( rRow
);
195 // how to proceed from EXPONENT_DIGIT
197 StateTransitions
& rRow
= m_aTransitions
[ EXPONENT_DIGIT
];
199 // there may be digits
200 lcl_insertDigitTransitions( rRow
, EXPONENT_DIGIT
);
202 // the string may end here
203 lcl_insertStopTransition( rRow
);
206 // how to proceed from END
208 /*StateTransitions& rRow =*/ m_aTransitions
[ EXPONENT_DIGIT
];
209 // no valid transition to leave this state
210 // (note that we, for consistency, nevertheless want to have a row in the table)
214 bool NumberValidator::implValidateNormalized( const OUString
& _rText
)
216 const sal_Unicode
* pCheckPos
= _rText
.getStr();
217 State eCurrentState
= START
;
219 while ( END
!= eCurrentState
)
221 // look up the transition row for the current state
222 TransitionTable::const_iterator aRow
= m_aTransitions
.find( eCurrentState
);
223 DBG_ASSERT( m_aTransitions
.end() != aRow
,
224 "NumberValidator::implValidateNormalized: invalid transition table (row not found)!" );
226 if ( m_aTransitions
.end() != aRow
)
228 // look up the current character in this row
229 StateTransitions::const_iterator aTransition
= aRow
->second
.find( *pCheckPos
);
230 if ( aRow
->second
.end() != aTransition
)
232 // there is a valid transition for this character
233 eCurrentState
= aTransition
->second
;
239 // if we're here, there is no valid transition
243 DBG_ASSERT( ( END
!= eCurrentState
) || ( 0 == *pCheckPos
),
244 "NumberValidator::implValidateNormalized: inconsistency!" );
245 // if we're at END, then the string should be done, too - the string should be normalized, means ending
246 // a "_" and not containing any other "_" (except at the start), and "_" is the only possibility
247 // to reach the END state
249 // the string is valid if and only if we reached the final state
250 return ( END
== eCurrentState
);
253 bool NumberValidator::isValidNumericFragment( const OUString
& _rText
)
255 if ( _rText
.isEmpty() )
256 // empty strings are always allowed
259 // normalize the string
260 OUString
sNormalized( "_" );
261 sNormalized
+= _rText
;
264 return implValidateNormalized( sNormalized
);
268 SvNumberFormatter
* FormattedField::StaticFormatter::s_cFormatter
= NULL
;
269 sal_uLong
FormattedField::StaticFormatter::s_nReferences
= 0;
271 SvNumberFormatter
* FormattedField::StaticFormatter::GetFormatter()
275 // get the Office's locale and translate
276 LanguageType eSysLanguage
= SvtSysLocale().GetLanguageTag().getLanguageType( false);
277 s_cFormatter
= new SvNumberFormatter(
278 ::comphelper::getProcessComponentContext(),
284 FormattedField::StaticFormatter::StaticFormatter()
289 FormattedField::StaticFormatter::~StaticFormatter()
291 if (--s_nReferences
== 0)
299 FormattedField::FormattedField(vcl::Window
* pParent
, WinBits nStyle
, SvNumberFormatter
* pInitialFormatter
, sal_Int32 nFormatKey
)
300 :SpinField(pParent
, nStyle
)
301 ,m_aLastSelection(0,0)
306 ,m_bStrictFormat(true)
307 ,m_bEnableEmptyField(true)
310 ,m_ValueState(valueDirty
)
316 ,m_dSpinFirst(-1000000)
317 ,m_dSpinLast(1000000)
318 ,m_bTreatAsNumber(true)
319 ,m_pLastOutputColor(NULL
)
320 ,m_bUseInputStringForFormatting(false)
323 if (pInitialFormatter
)
325 m_pFormatter
= pInitialFormatter
;
326 m_nFormatKey
= nFormatKey
;
330 VCL_BUILDER_FACTORY_ARGS(FormattedField
, WB_BORDER
| WB_SPIN
)
332 void FormattedField::SetText(const OUString
& rStr
)
335 SpinField::SetText(rStr
);
336 m_ValueState
= valueDirty
;
339 void FormattedField::SetText( const OUString
& rStr
, const Selection
& rNewSelection
)
342 SpinField::SetText( rStr
, rNewSelection
);
343 m_ValueState
= valueDirty
;
346 void FormattedField::SetTextFormatted(const OUString
& rStr
)
350 if (ImplGetFormatter()->IsTextFormat(m_nFormatKey
))
351 DBG_WARNING("FormattedField::SetTextFormatted : valid only with text formats !");
354 m_sCurrentTextValue
= rStr
;
357 double dNumber
= 0.0;
358 // IsNumberFormat changes the format key parameter
359 sal_uInt32 nTempFormatKey
= static_cast< sal_uInt32
>( m_nFormatKey
);
360 if( IsUsingInputStringForFormatting() &&
361 ImplGetFormatter()->IsNumberFormat(m_sCurrentTextValue
, nTempFormatKey
, dNumber
) )
363 ImplGetFormatter()->GetInputLineString(dNumber
, m_nFormatKey
, sFormatted
);
367 ImplGetFormatter()->GetOutputString(m_sCurrentTextValue
,
370 &m_pLastOutputColor
);
373 // calculate the new selection
374 Selection
aSel(GetSelection());
375 Selection
aNewSel(aSel
);
377 sal_Int32 nNewLen
= sFormatted
.getLength();
378 sal_Int32 nCurrentLen
= GetText().getLength();
379 if ((nNewLen
> nCurrentLen
) && (aNewSel
.Max() == nCurrentLen
))
380 { // the new text is longer and the cursor was behind the last char (of the old text)
381 if (aNewSel
.Min() == 0)
382 { // the whole text was selected -> select the new text on the whole, too
383 aNewSel
.Max() = nNewLen
;
385 { // there wasn't really a previous selection (as there was no previous text), we're setting a new one -> check the selection options
386 SelectionOptions nSelOptions
= GetSettings().GetStyleSettings().GetSelectionOptions();
387 if (nSelOptions
& SelectionOptions::ShowFirst
)
388 { // selection should be from right to left -> swap min and max
389 aNewSel
.Min() = aNewSel
.Max();
394 else if (aNewSel
.Max() == aNewSel
.Min())
395 { // there was no selection -> set the cursor behind the new last char
396 aNewSel
.Max() = nNewLen
;
397 aNewSel
.Min() = nNewLen
;
400 else if (aNewSel
.Max() > nNewLen
)
401 aNewSel
.Max() = nNewLen
;
403 aNewSel
= aSel
; // don't use the justified version
404 SpinField::SetText(sFormatted
, aNewSel
);
405 m_ValueState
= valueString
;
408 OUString
FormattedField::GetTextValue() const
410 if (m_ValueState
!= valueString
)
412 const_cast<FormattedField
*>(this)->m_sCurrentTextValue
= GetText();
413 const_cast<FormattedField
*>(this)->m_ValueState
= valueString
;
415 return m_sCurrentTextValue
;
418 void FormattedField::EnableNotANumber( bool _bEnable
)
420 if ( m_bEnableNaN
== _bEnable
)
423 m_bEnableNaN
= _bEnable
;
426 void FormattedField::SetAutoColor(bool _bAutomatic
)
428 if (_bAutomatic
== m_bAutoColor
)
431 m_bAutoColor
= _bAutomatic
;
433 { // if auto color is switched on, adjust the current text color, too
434 if (m_pLastOutputColor
)
435 SetControlForeground(*m_pLastOutputColor
);
437 SetControlForeground();
441 void FormattedField::impl_Modify(bool makeValueDirty
)
444 if (!IsStrictFormat())
447 m_ValueState
= valueDirty
;
452 OUString sCheck
= GetText();
453 if (CheckText(sCheck
))
455 m_sLastValidText
= sCheck
;
456 m_aLastSelection
= GetSelection();
458 m_ValueState
= valueDirty
;
462 ImplSetTextImpl(m_sLastValidText
, &m_aLastSelection
);
468 void FormattedField::Modify()
474 void FormattedField::ImplSetTextImpl(const OUString
& rNew
, Selection
* pNewSel
)
479 if (m_pLastOutputColor
)
480 SetControlForeground(*m_pLastOutputColor
);
482 SetControlForeground();
486 SpinField::SetText(rNew
, *pNewSel
);
489 Selection
aSel(GetSelection());
492 sal_Int32 nNewLen
= rNew
.getLength();
493 sal_Int32 nCurrentLen
= GetText().getLength();
495 if ((nNewLen
> nCurrentLen
) && (aSel
.Max() == nCurrentLen
))
496 { // new new text is longer and the cursor is behind the last char
498 { // the whole text was selected -> select the new text on the whole, too
499 aSel
.Max() = nNewLen
;
501 { // there wasn't really a previous selection (as there was no previous text), we're setting a new one -> check the selection options
502 SelectionOptions nSelOptions
= GetSettings().GetStyleSettings().GetSelectionOptions();
503 if (nSelOptions
& SelectionOptions::ShowFirst
)
504 { // selection should be from right to left -> swap min and max
505 aSel
.Min() = aSel
.Max();
510 else if (aSel
.Max() == aSel
.Min())
511 { // there was no selection -> set the cursor behind the new last char
512 aSel
.Max() = nNewLen
;
513 aSel
.Min() = nNewLen
;
516 else if (aSel
.Max() > nNewLen
)
517 aSel
.Max() = nNewLen
;
518 SpinField::SetText(rNew
, aSel
);
521 m_ValueState
= valueDirty
; // not always necessary, but better re-evaluate for safety reasons
524 bool FormattedField::PreNotify(NotifyEvent
& rNEvt
)
526 if (rNEvt
.GetType() == MouseNotifyEvent::KEYINPUT
)
527 m_aLastSelection
= GetSelection();
528 return SpinField::PreNotify(rNEvt
);
531 void FormattedField::ImplSetFormatKey(sal_uLong nFormatKey
)
534 m_nFormatKey
= nFormatKey
;
535 bool bNeedFormatter
= (m_pFormatter
== NULL
) && (nFormatKey
!= 0);
538 ImplGetFormatter(); // this creates a standard formatter
540 // It might happen that the standard formatter makes no sense here, but it takes a default
541 // format. Thus, it is possible to set one of the other standard keys (which are spanning
542 // across multiple formatters).
543 m_nFormatKey
= nFormatKey
;
544 // When calling SetFormatKey without a formatter, the key must be one of the standard values
545 // that is available for all formatters (and, thus, also in this new one).
546 DBG_ASSERT(m_pFormatter
->GetEntry(nFormatKey
) != NULL
, "FormattedField::ImplSetFormatKey : invalid format key !");
550 void FormattedField::SetFormatKey(sal_uLong nFormatKey
)
552 bool bNoFormatter
= (m_pFormatter
== NULL
);
553 ImplSetFormatKey(nFormatKey
);
554 FormatChanged((bNoFormatter
&& (m_pFormatter
!= NULL
)) ? FORMAT_CHANGE_TYPE::FORMATTER
: FORMAT_CHANGE_TYPE::KEYONLY
);
557 void FormattedField::SetFormatter(SvNumberFormatter
* pFormatter
, bool bResetFormat
)
562 m_pFormatter
= pFormatter
;
564 // calc the default format key from the Office's UI locale
567 // get the Office's locale and translate
568 LanguageType eSysLanguage
= SvtSysLocale().GetLanguageTag().getLanguageType( false);
569 // get the standard numeric format for this language
570 m_nFormatKey
= m_pFormatter
->GetStandardFormat( css::util::NumberFormat::NUMBER
, eSysLanguage
);
577 LanguageType aOldLang
;
578 OUString sOldFormat
= GetFormat(aOldLang
);
580 sal_uInt32 nDestKey
= pFormatter
->TestNewString(sOldFormat
);
581 if (nDestKey
== NUMBERFORMAT_ENTRY_NOT_FOUND
)
583 // language of the new formatter
584 const SvNumberformat
* pDefaultEntry
= pFormatter
->GetEntry(0);
585 LanguageType aNewLang
= pDefaultEntry
? pDefaultEntry
->GetLanguage() : LANGUAGE_DONTKNOW
;
587 // convert the old format string into the new language
590 pFormatter
->PutandConvertEntry(sOldFormat
, nCheckPos
, nType
, nDestKey
, aOldLang
, aNewLang
);
591 m_nFormatKey
= nDestKey
;
593 m_pFormatter
= pFormatter
;
596 FormatChanged(FORMAT_CHANGE_TYPE::FORMATTER
);
599 OUString
FormattedField::GetFormat(LanguageType
& eLang
) const
601 const SvNumberformat
* pFormatEntry
= ImplGetFormatter()->GetEntry(m_nFormatKey
);
602 DBG_ASSERT(pFormatEntry
!= NULL
, "FormattedField::GetFormat: no number format for the given format key.");
603 OUString sFormatString
= pFormatEntry
? pFormatEntry
->GetFormatstring() : OUString();
604 eLang
= pFormatEntry
? pFormatEntry
->GetLanguage() : LANGUAGE_DONTKNOW
;
606 return sFormatString
;
609 bool FormattedField::SetFormat(const OUString
& rFormatString
, LanguageType eLang
)
611 sal_uInt32 nNewKey
= ImplGetFormatter()->TestNewString(rFormatString
, eLang
);
612 if (nNewKey
== NUMBERFORMAT_ENTRY_NOT_FOUND
)
616 OUString
rFormat(rFormatString
);
617 if (!ImplGetFormatter()->PutEntry(rFormat
, nCheckPos
, nType
, nNewKey
, eLang
))
619 DBG_ASSERT(nNewKey
!= NUMBERFORMAT_ENTRY_NOT_FOUND
, "FormattedField::SetFormatString : PutEntry returned an invalid key !");
622 if (nNewKey
!= m_nFormatKey
)
623 SetFormatKey(nNewKey
);
627 bool FormattedField::GetThousandsSep() const
629 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey
),
630 "FormattedField::GetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
632 bool bThousand
, IsRed
;
633 sal_uInt16 nPrecision
, nAnzLeading
;
634 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey
, bThousand
, IsRed
, nPrecision
, nAnzLeading
);
639 void FormattedField::SetThousandsSep(bool _bUseSeparator
)
641 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey
),
642 "FormattedField::SetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
644 // get the current settings
645 bool bThousand
, IsRed
;
646 sal_uInt16 nPrecision
, nAnzLeading
;
647 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey
, bThousand
, IsRed
, nPrecision
, nAnzLeading
);
648 if (bThousand
== (bool)_bUseSeparator
)
651 // we need the language for the following
655 // generate a new format ...
656 OUString sFmtDescription
= ImplGetFormatter()->GenerateFormat(m_nFormatKey
, eLang
, _bUseSeparator
, IsRed
, nPrecision
, nAnzLeading
);
657 // ... and introduce it to the formatter
658 sal_Int32 nCheckPos
= 0;
661 ImplGetFormatter()->PutEntry(sFmtDescription
, nCheckPos
, nType
, nNewKey
, eLang
);
664 ImplSetFormatKey(nNewKey
);
665 FormatChanged(FORMAT_CHANGE_TYPE::THOUSANDSSEP
);
668 sal_uInt16
FormattedField::GetDecimalDigits() const
670 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey
),
671 "FormattedField::GetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
673 bool bThousand
, IsRed
;
674 sal_uInt16 nPrecision
, nAnzLeading
;
675 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey
, bThousand
, IsRed
, nPrecision
, nAnzLeading
);
680 void FormattedField::SetDecimalDigits(sal_uInt16 _nPrecision
)
682 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey
),
683 "FormattedField::SetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
685 // get the current settings
686 bool bThousand
, IsRed
;
687 sal_uInt16 nPrecision
, nAnzLeading
;
688 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey
, bThousand
, IsRed
, nPrecision
, nAnzLeading
);
689 if (nPrecision
== _nPrecision
)
692 // we need the language for the following
696 // generate a new format ...
697 OUString sFmtDescription
= ImplGetFormatter()->GenerateFormat(m_nFormatKey
, eLang
, bThousand
, IsRed
, _nPrecision
, nAnzLeading
);
698 // ... and introduce it to the formatter
699 sal_Int32 nCheckPos
= 0;
702 ImplGetFormatter()->PutEntry(sFmtDescription
, nCheckPos
, nType
, nNewKey
, eLang
);
705 ImplSetFormatKey(nNewKey
);
706 FormatChanged(FORMAT_CHANGE_TYPE::PRECISION
);
709 void FormattedField::FormatChanged( FORMAT_CHANGE_TYPE _nWhat
)
711 m_pLastOutputColor
= NULL
;
713 if ( (_nWhat
== FORMAT_CHANGE_TYPE::FORMATTER
) && m_pFormatter
)
714 m_pFormatter
->SetEvalDateFormat( NF_EVALDATEFORMAT_INTL_FORMAT
);
719 void FormattedField::Commit()
721 // remember the old text
722 OUString
sOld( GetText() );
727 // did the text change?
728 if ( GetText() != sOld
)
729 { // consider the field as modified,
730 // but we already have the most recent value;
731 // don't reparse it from the text
732 // (can lead to data loss when the format is lossy,
733 // as is e.g. our default date format: 2-digit year!)
738 void FormattedField::ReFormat()
740 if (!IsEmptyFieldEnabled() || !GetText().isEmpty())
742 if (TreatingAsNumber())
744 double dValue
= GetValue();
745 if ( m_bEnableNaN
&& ::rtl::math::isNan( dValue
) )
747 ImplSetValue( dValue
, true );
750 SetTextFormatted(GetTextValue());
754 bool FormattedField::Notify(NotifyEvent
& rNEvt
)
757 if ((rNEvt
.GetType() == MouseNotifyEvent::KEYINPUT
) && !IsReadOnly())
759 const KeyEvent
& rKEvt
= *rNEvt
.GetKeyEvent();
760 sal_uInt16 nMod
= rKEvt
.GetKeyCode().GetModifier();
761 switch ( rKEvt
.GetKeyCode().GetCode() )
767 if (!nMod
&& ImplGetFormatter()->IsTextFormat(m_nFormatKey
))
769 // the base class would translate this into calls to Up/Down/First/Last,
770 // but we don't want this if we are text-formatted
776 if ((rNEvt
.GetType() == MouseNotifyEvent::COMMAND
) && !IsReadOnly())
778 const CommandEvent
* pCommand
= rNEvt
.GetCommandEvent();
779 if (pCommand
->GetCommand() == CommandEventId::Wheel
)
781 const CommandWheelData
* pData
= rNEvt
.GetCommandEvent()->GetWheelData();
782 if ((pData
->GetMode() == CommandWheelMode::SCROLL
) && ImplGetFormatter()->IsTextFormat(m_nFormatKey
))
784 // same as above : prevent the base class from doing Up/Down-calls
785 // (normally I should put this test into the Up/Down methods itself, shouldn't I ?)
786 // FS - 71553 - 19.01.00
792 if (rNEvt
.GetType() == MouseNotifyEvent::LOSEFOCUS
)
794 // Sonderbehandlung fuer leere Texte
795 if (GetText().isEmpty())
797 if (!IsEmptyFieldEnabled())
799 if (TreatingAsNumber())
801 ImplSetValue(m_dCurrentValue
, true);
803 m_ValueState
= valueDouble
;
807 OUString sNew
= GetTextValue();
809 SetTextFormatted(sNew
);
811 SetTextFormatted(m_sDefaultText
);
812 m_ValueState
= valueString
;
822 return SpinField::Notify( rNEvt
);
825 void FormattedField::SetMinValue(double dMin
)
827 DBG_ASSERT(m_bTreatAsNumber
, "FormattedField::SetMinValue : only to be used in numeric mode !");
831 // for checking the current value at the new border -> ImplSetValue
835 void FormattedField::SetMaxValue(double dMax
)
837 DBG_ASSERT(m_bTreatAsNumber
, "FormattedField::SetMaxValue : only to be used in numeric mode !");
841 // for checking the current value at the new border -> ImplSetValue
845 void FormattedField::SetTextValue(const OUString
& rText
)
851 void FormattedField::EnableEmptyField(bool bEnable
)
853 if (bEnable
== m_bEnableEmptyField
)
856 m_bEnableEmptyField
= bEnable
;
857 if (!m_bEnableEmptyField
&& GetText().isEmpty())
858 ImplSetValue(m_dCurrentValue
, true);
861 void FormattedField::ImplSetValue(double dVal
, bool bForce
)
864 if (m_bHasMin
&& (dVal
<m_dMinValue
))
866 if (m_bHasMax
&& (dVal
>m_dMaxValue
))
868 if (!bForce
&& (dVal
== GetValue()))
871 DBG_ASSERT(ImplGetFormatter() != NULL
, "FormattedField::ImplSetValue : can't set a value without a formatter !");
873 m_ValueState
= valueDouble
;
874 m_dCurrentValue
= dVal
;
877 if (ImplGetFormatter()->IsTextFormat(m_nFormatKey
))
879 // first convert the number as string in standard format
881 ImplGetFormatter()->GetOutputString(dVal
, 0, sTemp
, &m_pLastOutputColor
);
882 // then encode the string in the corresponding text format
883 ImplGetFormatter()->GetOutputString(sTemp
, m_nFormatKey
, sNewText
, &m_pLastOutputColor
);
887 if( IsUsingInputStringForFormatting())
889 ImplGetFormatter()->GetInputLineString(dVal
, m_nFormatKey
, sNewText
);
893 ImplGetFormatter()->GetOutputString(dVal
, m_nFormatKey
, sNewText
, &m_pLastOutputColor
);
897 ImplSetTextImpl(sNewText
, NULL
);
898 m_ValueState
= valueDouble
;
899 DBG_ASSERT(CheckText(sNewText
), "FormattedField::ImplSetValue : formatted string doesn't match the criteria !");
902 bool FormattedField::ImplGetValue(double& dNewVal
)
905 dNewVal
= m_dCurrentValue
;
906 if (m_ValueState
== valueDouble
)
909 dNewVal
= m_dDefaultValue
;
910 OUString
sText(GetText());
914 DBG_ASSERT(ImplGetFormatter() != NULL
, "FormattedField::ImplGetValue : can't give you a current value without a formatter !");
916 sal_uInt32 nFormatKey
= m_nFormatKey
; // IsNumberFormat changes the FormatKey!
918 if (ImplGetFormatter()->IsTextFormat(nFormatKey
) && m_bTreatAsNumber
)
919 // for detection of values like "1,1" in fields that are formatted as text
922 // special treatment for percentage formatting
923 if (ImplGetFormatter()->GetType(m_nFormatKey
) == css::util::NumberFormat::PERCENT
)
925 // the language of our format
926 LanguageType eLanguage
= m_pFormatter
->GetEntry(m_nFormatKey
)->GetLanguage();
927 // the default number format for this language
928 sal_uLong nStandardNumericFormat
= m_pFormatter
->GetStandardFormat(css::util::NumberFormat::NUMBER
, eLanguage
);
930 sal_uInt32 nTempFormat
= nStandardNumericFormat
;
932 if (m_pFormatter
->IsNumberFormat(sText
, nTempFormat
, dTemp
) &&
933 css::util::NumberFormat::NUMBER
== m_pFormatter
->GetType(nTempFormat
))
934 // the string is equivalent to a number formatted one (has no % sign) -> append it
936 // (with this, a input of '3' becomes '3%', which then by the formatter is translated
937 // into 0.03. Without this, the formatter would give us the double 3 for an input '3',
938 // which equals 300 percent.
940 if (!ImplGetFormatter()->IsNumberFormat(sText
, nFormatKey
, dNewVal
))
943 if (m_bHasMin
&& (dNewVal
<m_dMinValue
))
944 dNewVal
= m_dMinValue
;
945 if (m_bHasMax
&& (dNewVal
>m_dMaxValue
))
946 dNewVal
= m_dMaxValue
;
950 void FormattedField::SetValue(double dVal
)
952 ImplSetValue(dVal
, m_ValueState
!= valueDouble
);
955 double FormattedField::GetValue()
958 if ( !ImplGetValue( m_dCurrentValue
) )
961 ::rtl::math::setNan( &m_dCurrentValue
);
963 m_dCurrentValue
= m_dDefaultValue
;
966 m_ValueState
= valueDouble
;
967 return m_dCurrentValue
;
970 void FormattedField::Up()
972 // setValue handles under- and overflows (min/max) automatically
973 SetValue(GetValue() + m_dSpinSize
);
980 void FormattedField::Down()
982 SetValue(GetValue() - m_dSpinSize
);
989 void FormattedField::First()
993 SetValue(m_dMinValue
);
1001 void FormattedField::Last()
1005 SetValue(m_dMaxValue
);
1013 void FormattedField::UseInputStringForFormatting( bool bUseInputStr
/* = true */ )
1015 m_bUseInputStringForFormatting
= bUseInputStr
;
1019 DoubleNumericField::~DoubleNumericField()
1024 void DoubleNumericField::dispose()
1026 delete m_pNumberValidator
;
1027 FormattedField::dispose();
1030 void DoubleNumericField::FormatChanged(FORMAT_CHANGE_TYPE nWhat
)
1032 ResetConformanceTester();
1033 FormattedField::FormatChanged(nWhat
);
1036 bool DoubleNumericField::CheckText(const OUString
& sText
) const
1038 // We'd like to implement this using the NumberFormatter::IsNumberFormat, but unfortunately, this doesn't
1039 // recognize fragments of numbers (like, for instance "1e", which happens during entering e.g. "1e10")
1040 // Thus, the roundabout way via a regular expression
1041 return m_pNumberValidator
->isValidNumericFragment( sText
);
1044 void DoubleNumericField::ResetConformanceTester()
1046 // the thousands and the decimal separator are language dependent
1047 const SvNumberformat
* pFormatEntry
= ImplGetFormatter()->GetEntry(m_nFormatKey
);
1049 sal_Unicode cSeparatorThousand
= ',';
1050 sal_Unicode cSeparatorDecimal
= '.';
1053 LocaleDataWrapper
aLocaleInfo( LanguageTag( pFormatEntry
->GetLanguage()) );
1055 OUString sSeparator
= aLocaleInfo
.getNumThousandSep();
1056 if (!sSeparator
.isEmpty())
1057 cSeparatorThousand
= sSeparator
[0];
1059 sSeparator
= aLocaleInfo
.getNumDecimalSep();
1060 if (!sSeparator
.isEmpty())
1061 cSeparatorDecimal
= sSeparator
[0];
1064 delete m_pNumberValidator
;
1065 m_pNumberValidator
= new validation::NumberValidator( cSeparatorThousand
, cSeparatorDecimal
);
1068 DoubleCurrencyField::DoubleCurrencyField(vcl::Window
* pParent
, WinBits nStyle
)
1069 :FormattedField(pParent
, nStyle
)
1070 ,m_bChangingFormat(false)
1072 m_bPrependCurrSym
= false;
1074 // initialize with a system currency format
1075 m_sCurrencySymbol
= SvtSysLocale().GetLocaleData().getCurrSymbol();
1076 UpdateCurrencyFormat();
1079 void DoubleCurrencyField::FormatChanged(FORMAT_CHANGE_TYPE nWhat
)
1081 if (m_bChangingFormat
)
1083 FormattedField::FormatChanged(nWhat
);
1089 case FORMAT_CHANGE_TYPE::FORMATTER
:
1090 case FORMAT_CHANGE_TYPE::PRECISION
:
1091 case FORMAT_CHANGE_TYPE::THOUSANDSSEP
:
1092 // the aspects which changed don't take our currency settings into account (in fact, they most probably
1094 UpdateCurrencyFormat();
1096 case FORMAT_CHANGE_TYPE::KEYONLY
:
1097 OSL_FAIL("DoubleCurrencyField::FormatChanged : somebody modified my key !");
1098 // We always build our own format from the settings we get via special methods (setCurrencySymbol etc.).
1099 // Nobody but ourself should modifiy the format key directly !
1104 FormattedField::FormatChanged(nWhat
);
1107 void DoubleCurrencyField::setCurrencySymbol(const OUString
& rSymbol
)
1109 if (m_sCurrencySymbol
== rSymbol
)
1112 m_sCurrencySymbol
= rSymbol
;
1113 UpdateCurrencyFormat();
1114 FormatChanged(FORMAT_CHANGE_TYPE::CURRENCY_SYMBOL
);
1117 void DoubleCurrencyField::setPrependCurrSym(bool _bPrepend
)
1119 if (m_bPrependCurrSym
== _bPrepend
)
1122 m_bPrependCurrSym
= _bPrepend
;
1123 UpdateCurrencyFormat();
1124 FormatChanged(FORMAT_CHANGE_TYPE::CURRSYM_POSITION
);
1127 void DoubleCurrencyField::UpdateCurrencyFormat()
1130 LanguageType eLanguage
;
1131 GetFormat(eLanguage
);
1132 bool bThSep
= GetThousandsSep();
1133 sal_uInt16 nDigits
= GetDecimalDigits();
1135 // build a new format string with the base class' and my own settings
1137 /* Strangely with gcc 4.6.3 this needs a temporary LanguageTag, otherwise
1139 * error: request for member 'getNumThousandSep' in 'aLocaleInfo', which is
1140 * of non-class type 'LocaleDataWrapper(LanguageTag)' */
1141 LanguageTag
aLanguageTag( eLanguage
);
1142 LocaleDataWrapper
aLocaleInfo( aLanguageTag
);
1144 OUStringBuffer sNewFormat
;
1147 sNewFormat
.append('#');
1148 sNewFormat
.append(aLocaleInfo
.getNumThousandSep());
1149 sNewFormat
.append("##0");
1152 sNewFormat
.append('0');
1156 sNewFormat
.append(aLocaleInfo
.getNumDecimalSep());
1158 OUStringBuffer sTemp
;
1159 comphelper::string::padToLength(sTemp
, nDigits
, '0');
1160 sNewFormat
.append(sTemp
);
1163 if (getPrependCurrSym())
1165 OUString sSymbol
= getCurrencySymbol();
1166 sSymbol
= comphelper::string::stripStart(sSymbol
, ' ');
1167 sSymbol
= comphelper::string::stripEnd(sSymbol
, ' ');
1169 OUStringBuffer
sTemp("[$");
1170 sTemp
.append(sSymbol
);
1172 sTemp
.append(sNewFormat
);
1174 // for negative values : $ -0.00, not -$ 0.00 ...
1175 // (the real solution would be a possibility to choose a "positive currency format" and a "negative currency format" ...
1176 // But not now ... (and hey, you could take a formatted field for this ....))
1177 // FS - 31.03.00 74642
1178 sTemp
.append(";[$");
1179 sTemp
.append(sSymbol
);
1180 sTemp
.append("] -");
1181 sTemp
.append(sNewFormat
);
1187 OUString sTemp
= getCurrencySymbol();
1188 sTemp
= comphelper::string::stripStart(sTemp
, ' ');
1189 sTemp
= comphelper::string::stripEnd(sTemp
, ' ');
1191 sNewFormat
.append(" [$");
1192 sNewFormat
.append(sTemp
);
1193 sNewFormat
.append(']');
1196 // set this new basic format
1197 m_bChangingFormat
= true;
1198 SetFormat(sNewFormat
.makeStringAndClear(), eLanguage
);
1199 m_bChangingFormat
= false;
1202 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */