1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: fmtfield.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_svtools.hxx"
35 #include <tools/debug.hxx>
36 #include <comphelper/processfactory.hxx>
37 #include <unotools/localedatawrapper.hxx>
38 #include <vcl/svapp.hxx>
39 #include <svtools/zformat.hxx>
40 #include <svtools/fmtfield.hxx>
41 #include <i18npool/mslangid.hxx>
42 #include <com/sun/star/lang/Locale.hpp>
43 #include <com/sun/star/util/SearchOptions.hpp>
44 #include <com/sun/star/util/SearchAlgorithms.hpp>
45 #include <com/sun/star/util/SearchResult.hpp>
46 #include <com/sun/star/util/SearchFlags.hpp>
47 #include <com/sun/star/lang/Locale.hpp>
48 #include <svtools/syslocale.hxx>
50 #ifndef REGEXP_SUPPORT
54 #if !defined INCLUDED_RTL_MATH_HXX
55 #include <rtl/math.hxx>
58 using namespace ::com::sun::star::lang
;
59 using namespace ::com::sun::star::util
;
64 //==============================================================================
65 // regular expression to validate complete numbers, plus every fragment which can occur during the input
66 // of a complete number
67 // [+/-][{digit}*.]*{digit}*[,{digit}*][e[+/-]{digit}*]
68 const char __FAR_DATA szNumericInput
[] = "_[-+]?([0-9]*\\,)*[0-9]*(\\.[0-9]*)?(e[-+]?[0-9]*)?_";
69 // (the two _ are for normalizing it: With this, we can ensure that a to-be-checked text is always
70 // matched as a _whole_)
73 // hmm. No support for regular expression. Well, I always (not really :) wanted to write a finite automat
74 // so here comes a finite automat ...
78 // the states of our automat.
81 START
, // at the very start of the string
82 NUM_START
, // the very start of the number
84 DIGIT_PRE_COMMA
, // some pre-comma digits are read, perhaps including some thousand separators
86 DIGIT_POST_COMMA
, // reading digits after the comma
87 EXPONENT_START
, // at the very start of the exponent value
88 // (means: not including the "e" which denotes the exponent)
89 EXPONENT_DIGIT
, // currently reading the digits of the exponent
91 END
// reached the end of the string
94 // a row in the transition table (means the set of states to be reached from a given state)
95 typedef ::std::map
< sal_Unicode
, State
> StateTransitions
;
97 // a single transition
98 typedef StateTransitions::value_type Transition
;
100 // the complete transition table
101 typedef ::std::map
< State
, StateTransitions
> TransitionTable
;
103 // the validator class
104 class NumberValidator
107 TransitionTable m_aTransitions
;
108 const sal_Unicode m_cThSep
;
109 const sal_Unicode m_cDecSep
;
112 NumberValidator( const sal_Unicode _cThSep
, const sal_Unicode _cDecSep
);
114 sal_Bool
isValidNumericFragment( const String
& _rText
);
117 sal_Bool
implValidateNormalized( const String
& _rText
);
120 //--------------------------------------------------------------------------
121 //..........................................................................
122 static void lcl_insertStopTransition( StateTransitions
& _rRow
)
124 _rRow
.insert( Transition( '_', END
) );
127 //..........................................................................
128 static void lcl_insertStartExponentTransition( StateTransitions
& _rRow
)
130 _rRow
.insert( Transition( 'e', EXPONENT_START
) );
133 //..........................................................................
134 static void lcl_insertSignTransitions( StateTransitions
& _rRow
, const State eNextState
)
136 _rRow
.insert( Transition( '-', eNextState
) );
137 _rRow
.insert( Transition( '+', eNextState
) );
140 //..........................................................................
141 static void lcl_insertDigitTransitions( StateTransitions
& _rRow
, const State eNextState
)
143 for ( sal_Unicode aChar
= '0'; aChar
<= '9'; ++aChar
)
144 _rRow
.insert( Transition( aChar
, eNextState
) );
147 //..........................................................................
148 static void lcl_insertCommonPreCommaTransitions( StateTransitions
& _rRow
, const sal_Unicode _cThSep
, const sal_Unicode _cDecSep
)
150 // digits are allowed
151 lcl_insertDigitTransitions( _rRow
, DIGIT_PRE_COMMA
);
153 // the thousand separator is allowed
154 _rRow
.insert( Transition( _cThSep
, DIGIT_PRE_COMMA
) );
156 // a comma is allowed
157 _rRow
.insert( Transition( _cDecSep
, DIGIT_POST_COMMA
) );
160 //--------------------------------------------------------------------------
161 NumberValidator::NumberValidator( const sal_Unicode _cThSep
, const sal_Unicode _cDecSep
)
163 ,m_cDecSep( _cDecSep
)
165 // build up our transition table
167 // how to procede from START
169 StateTransitions
& rRow
= m_aTransitions
[ START
];
170 rRow
.insert( Transition( '_', NUM_START
) );
171 // if we encounter the normalizing character, we want to procede with the number
174 // how to procede from NUM_START
176 StateTransitions
& rRow
= m_aTransitions
[ NUM_START
];
179 lcl_insertSignTransitions( rRow
, DIGIT_PRE_COMMA
);
181 // common transitions for the two pre-comma states
182 lcl_insertCommonPreCommaTransitions( rRow
, m_cThSep
, m_cDecSep
);
184 // the exponent may start here
185 // (this would mean string like "_+e10_", but this is a valid fragment, though no valid number)
186 lcl_insertStartExponentTransition( rRow
);
189 // how to procede from DIGIT_PRE_COMMA
191 StateTransitions
& rRow
= m_aTransitions
[ DIGIT_PRE_COMMA
];
193 // common transitions for the two pre-comma states
194 lcl_insertCommonPreCommaTransitions( rRow
, m_cThSep
, m_cDecSep
);
196 // the exponent may start here
197 lcl_insertStartExponentTransition( rRow
);
199 // the final transition indicating the end of the string
200 // (if there is no comma and no post-comma, then the string may end here)
201 lcl_insertStopTransition( rRow
);
204 // how to procede from DIGIT_POST_COMMA
206 StateTransitions
& rRow
= m_aTransitions
[ DIGIT_POST_COMMA
];
208 // there might be digits, which would keep the state at DIGIT_POST_COMMA
209 lcl_insertDigitTransitions( rRow
, DIGIT_POST_COMMA
);
211 // the exponent may start here
212 lcl_insertStartExponentTransition( rRow
);
214 // the string may end here
215 lcl_insertStopTransition( rRow
);
218 // how to procede from EXPONENT_START
220 StateTransitions
& rRow
= m_aTransitions
[ EXPONENT_START
];
222 // there may be a sign
223 lcl_insertSignTransitions( rRow
, EXPONENT_DIGIT
);
225 // there may be digits
226 lcl_insertDigitTransitions( rRow
, EXPONENT_DIGIT
);
228 // the string may end here
229 lcl_insertStopTransition( rRow
);
232 // how to procede from EXPONENT_DIGIT
234 StateTransitions
& rRow
= m_aTransitions
[ EXPONENT_DIGIT
];
236 // there may be digits
237 lcl_insertDigitTransitions( rRow
, EXPONENT_DIGIT
);
239 // the string may end here
240 lcl_insertStopTransition( rRow
);
243 // how to procede from END
245 /*StateTransitions& rRow =*/ m_aTransitions
[ EXPONENT_DIGIT
];
246 // no valid transition to leave this state
247 // (note that we, for consistency, nevertheless want to have a row in the table)
251 //--------------------------------------------------------------------------
252 sal_Bool
NumberValidator::implValidateNormalized( const String
& _rText
)
254 const sal_Unicode
* pCheckPos
= _rText
.GetBuffer();
255 State eCurrentState
= START
;
257 while ( END
!= eCurrentState
)
259 // look up the transition row for the current state
260 TransitionTable::const_iterator aRow
= m_aTransitions
.find( eCurrentState
);
261 DBG_ASSERT( m_aTransitions
.end() != aRow
,
262 "NumberValidator::implValidateNormalized: invalid transition table (row not found)!" );
264 if ( m_aTransitions
.end() != aRow
)
266 // look up the current character in this row
267 StateTransitions::const_iterator aTransition
= aRow
->second
.find( *pCheckPos
);
268 if ( aRow
->second
.end() != aTransition
)
270 // there is a valid transition for this character
271 eCurrentState
= aTransition
->second
;
277 // if we're here, there is no valid transition
281 DBG_ASSERT( ( END
!= eCurrentState
) || ( 0 == *pCheckPos
),
282 "NumberValidator::implValidateNormalized: inconsistency!" );
283 // if we're at END, then the string should be done, too - the string should be normalized, means ending
284 // a "_" and not containing any other "_" (except at the start), and "_" is the only possibility
285 // to reach the END state
287 // the string is valid if and only if we reached the final state
288 return ( END
== eCurrentState
);
291 //--------------------------------------------------------------------------
292 sal_Bool
NumberValidator::isValidNumericFragment( const String
& _rText
)
295 // empty strings are always allowed
298 // normalize the string
299 String
sNormalized( RTL_CONSTASCII_STRINGPARAM( "_") );
300 sNormalized
.Append( _rText
);
301 sNormalized
.AppendAscii( "_" );
303 return implValidateNormalized( sNormalized
);
309 //==============================================================================
310 SvNumberFormatter
* FormattedField::StaticFormatter::s_cFormatter
= NULL
;
311 ULONG
FormattedField::StaticFormatter::s_nReferences
= 0;
313 //------------------------------------------------------------------------------
314 SvNumberFormatter
* FormattedField::StaticFormatter::GetFormatter()
318 // get the Office's locale and translate
319 LanguageType eSysLanguage
= MsLangId::convertLocaleToLanguage(
320 SvtSysLocale().GetLocaleData().getLocale() );
321 s_cFormatter
= new SvNumberFormatter(
322 ::comphelper::getProcessServiceFactory(),
328 //------------------------------------------------------------------------------
329 FormattedField::StaticFormatter::StaticFormatter()
334 //------------------------------------------------------------------------------
335 FormattedField::StaticFormatter::~StaticFormatter()
337 if (--s_nReferences
== 0)
344 //==============================================================================
345 DBG_NAME(FormattedField
);
347 #define INIT_MEMBERS() \
348 m_aLastSelection(0,0) \
353 ,m_bStrictFormat(TRUE) \
354 ,m_bValueDirty(TRUE) \
355 ,m_bEnableEmptyField(TRUE) \
356 ,m_bAutoColor(FALSE) \
357 ,m_bEnableNaN(FALSE) \
358 ,m_dCurrentValue(0) \
359 ,m_dDefaultValue(0) \
361 ,m_pFormatter(NULL) \
363 ,m_dSpinFirst(-1000000) \
364 ,m_dSpinLast(1000000) \
365 ,m_bTreatAsNumber(TRUE) \
366 ,m_pLastOutputColor(NULL) \
367 ,m_bUseInputStringForFormatting(false)
369 //------------------------------------------------------------------------------
370 FormattedField::FormattedField(Window
* pParent
, WinBits nStyle
, SvNumberFormatter
* pInitialFormatter
, INT32 nFormatKey
)
371 :SpinField(pParent
, nStyle
)
374 DBG_CTOR(FormattedField
, NULL
);
376 if (pInitialFormatter
)
378 m_pFormatter
= pInitialFormatter
;
379 m_nFormatKey
= nFormatKey
;
383 //------------------------------------------------------------------------------
384 FormattedField::FormattedField(Window
* pParent
, const ResId
& rResId
, SvNumberFormatter
* pInitialFormatter
, INT32 nFormatKey
)
385 :SpinField(pParent
, rResId
)
388 DBG_CTOR(FormattedField
, NULL
);
390 if (pInitialFormatter
)
392 m_pFormatter
= pInitialFormatter
;
393 m_nFormatKey
= nFormatKey
;
397 //------------------------------------------------------------------------------
398 FormattedField::~FormattedField()
400 DBG_DTOR(FormattedField
, NULL
);
403 //------------------------------------------------------------------------------
404 void FormattedField::SetValidateText(const XubString
& rText
, const String
* pErrorText
)
406 DBG_CHKTHIS(FormattedField
, NULL
);
408 if (CheckText(rText
))
412 ImplSetTextImpl(*pErrorText
, NULL
);
414 ImplSetValue(m_dDefaultValue
, TRUE
);
417 //------------------------------------------------------------------------------
418 void FormattedField::SetText(const XubString
& rStr
)
420 DBG_CHKTHIS(FormattedField
, NULL
);
422 SpinField::SetText(rStr
);
423 m_bValueDirty
= TRUE
;
426 //------------------------------------------------------------------------------
427 void FormattedField::SetText( const XubString
& rStr
, const Selection
& rNewSelection
)
429 DBG_CHKTHIS(FormattedField
, NULL
);
431 SpinField::SetText( rStr
, rNewSelection
);
432 m_bValueDirty
= TRUE
;
435 //------------------------------------------------------------------------------
436 void FormattedField::SetTextFormatted(const XubString
& rStr
)
438 DBG_CHKTHIS(FormattedField
, NULL
);
441 if (ImplGetFormatter()->IsTextFormat(m_nFormatKey
))
442 DBG_WARNING("FormattedField::SetTextFormatted : valid only with text formats !");
445 m_sCurrentTextValue
= rStr
;
448 double dNumber
= 0.0;
449 // IsNumberFormat changes the format key parameter
450 sal_uInt32 nTempFormatKey
= static_cast< sal_uInt32
>( m_nFormatKey
);
451 if( IsUsingInputStringForFormatting() &&
452 ImplGetFormatter()->IsNumberFormat(m_sCurrentTextValue
, nTempFormatKey
, dNumber
) )
453 ImplGetFormatter()->GetInputLineString(dNumber
, m_nFormatKey
, sFormatted
);
455 ImplGetFormatter()->GetOutputString(m_sCurrentTextValue
, m_nFormatKey
, sFormatted
, &m_pLastOutputColor
);
457 // calculate the new selection
458 Selection
aSel(GetSelection());
459 Selection
aNewSel(aSel
);
461 USHORT nNewLen
= sFormatted
.Len();
462 USHORT nCurrentLen
= GetText().Len();
463 if ((nNewLen
> nCurrentLen
) && (aNewSel
.Max() == nCurrentLen
))
464 { // the new text is longer and the cursor was behind the last char (of the old text)
465 if (aNewSel
.Min() == 0)
466 { // the whole text was selected -> select the new text on the whole, too
467 aNewSel
.Max() = nNewLen
;
469 { // there wasn't really a previous selection (as there was no previous text), we're setting a new one -> check the selection options
470 ULONG nSelOptions
= GetSettings().GetStyleSettings().GetSelectionOptions();
471 if (nSelOptions
& SELECTION_OPTION_SHOWFIRST
)
472 { // selection should be from right to left -> swap min and max
473 aNewSel
.Min() = aNewSel
.Max();
478 else if (aNewSel
.Max() == aNewSel
.Min())
479 { // there was no selection -> set the cursor behind the new last char
480 aNewSel
.Max() = nNewLen
;
481 aNewSel
.Min() = nNewLen
;
484 else if (aNewSel
.Max() > nNewLen
)
485 aNewSel
.Max() = nNewLen
;
487 aNewSel
= aSel
; // don't use the justified version
488 SpinField::SetText(sFormatted
, aNewSel
);
489 m_bValueDirty
= FALSE
;
492 //------------------------------------------------------------------------------
493 String
FormattedField::GetTextValue() const
497 ((FormattedField
*)this)->m_sCurrentTextValue
= GetText();
498 ((FormattedField
*)this)->m_bValueDirty
= FALSE
;
500 return m_sCurrentTextValue
;
503 //------------------------------------------------------------------------------
504 void FormattedField::EnableNotANumber( BOOL _bEnable
)
506 if ( m_bEnableNaN
== _bEnable
)
509 m_bEnableNaN
= _bEnable
;
512 //------------------------------------------------------------------------------
513 void FormattedField::SetAutoColor(BOOL _bAutomatic
)
515 if (_bAutomatic
== m_bAutoColor
)
518 m_bAutoColor
= _bAutomatic
;
520 { // if auto color is switched on, adjust the current text color, too
521 if (m_pLastOutputColor
)
522 SetControlForeground(*m_pLastOutputColor
);
524 SetControlForeground();
528 //------------------------------------------------------------------------------
529 void FormattedField::Modify()
531 DBG_CHKTHIS(FormattedField
, NULL
);
533 if (!IsStrictFormat())
535 m_bValueDirty
= TRUE
;
540 String sCheck
= GetText();
541 if (CheckText(sCheck
))
543 m_sLastValidText
= sCheck
;
544 m_aLastSelection
= GetSelection();
545 m_bValueDirty
= TRUE
;
549 ImplSetTextImpl(m_sLastValidText
, &m_aLastSelection
);
555 //------------------------------------------------------------------------------
556 void FormattedField::ImplSetTextImpl(const XubString
& rNew
, Selection
* pNewSel
)
558 DBG_CHKTHIS(FormattedField
, NULL
);
562 if (m_pLastOutputColor
)
563 SetControlForeground(*m_pLastOutputColor
);
565 SetControlForeground();
569 SpinField::SetText(rNew
, *pNewSel
);
572 Selection
aSel(GetSelection());
575 USHORT nNewLen
= rNew
.Len();
576 USHORT nCurrentLen
= GetText().Len();
578 if ((nNewLen
> nCurrentLen
) && (aSel
.Max() == nCurrentLen
))
579 { // new new text is longer and the cursor is behind the last char
581 { // the whole text was selected -> select the new text on the whole, too
582 aSel
.Max() = nNewLen
;
584 { // there wasn't really a previous selection (as there was no previous text), we're setting a new one -> check the selection options
585 ULONG nSelOptions
= GetSettings().GetStyleSettings().GetSelectionOptions();
586 if (nSelOptions
& SELECTION_OPTION_SHOWFIRST
)
587 { // selection should be from right to left -> swap min and max
588 aSel
.Min() = aSel
.Max();
593 else if (aSel
.Max() == aSel
.Min())
594 { // there was no selection -> set the cursor behind the new last char
595 aSel
.Max() = nNewLen
;
596 aSel
.Min() = nNewLen
;
599 else if (aSel
.Max() > nNewLen
)
600 aSel
.Max() = nNewLen
;
601 SpinField::SetText(rNew
, aSel
);
604 m_bValueDirty
= TRUE
;
605 // muss nicht stimmen, aber sicherheitshalber ...
608 //------------------------------------------------------------------------------
609 long FormattedField::PreNotify(NotifyEvent
& rNEvt
)
611 DBG_CHKTHIS(FormattedField
, NULL
);
612 if (rNEvt
.GetType() == EVENT_KEYINPUT
)
613 m_aLastSelection
= GetSelection();
614 return SpinField::PreNotify(rNEvt
);
617 //------------------------------------------------------------------------------
618 void FormattedField::ImplSetFormatKey(ULONG nFormatKey
)
620 DBG_CHKTHIS(FormattedField
, NULL
);
622 m_nFormatKey
= nFormatKey
;
623 BOOL bNeedFormatter
= (m_pFormatter
== NULL
) && (nFormatKey
!= 0);
626 ImplGetFormatter(); // damit wird ein Standard-Formatter angelegt
628 m_nFormatKey
= nFormatKey
;
629 // kann sein, dass das in dem Standard-Formatter keinen Sinn macht, aber der nimmt dann ein Default-Format an.
630 // Auf diese Weise kann ich einfach einen der - formatteruebergreifended gleichen - Standard-Keys setzen.
631 DBG_ASSERT(m_pFormatter
->GetEntry(nFormatKey
) != NULL
, "FormattedField::ImplSetFormatKey : invalid format key !");
632 // Wenn SetFormatKey aufgerufen wird, ohne dass ein Formatter existiert, muss der Key einer der Standard-Werte
633 // sein, der in allen Formattern (also auch in meinem neu angelegten) vorhanden ist.
637 //------------------------------------------------------------------------------
638 void FormattedField::SetFormatKey(ULONG nFormatKey
)
640 DBG_CHKTHIS(FormattedField
, NULL
);
641 BOOL bNoFormatter
= (m_pFormatter
== NULL
);
642 ImplSetFormatKey(nFormatKey
);
643 FormatChanged((bNoFormatter
&& (m_pFormatter
!= NULL
)) ? FCT_FORMATTER
: FCT_KEYONLY
);
646 //------------------------------------------------------------------------------
647 void FormattedField::SetFormatter(SvNumberFormatter
* pFormatter
, BOOL bResetFormat
)
649 DBG_CHKTHIS(FormattedField
, NULL
);
653 m_pFormatter
= pFormatter
;
655 // calc the default format key from the Office's UI locale
658 // get the Office's locale and translate
659 LanguageType eSysLanguage
= MsLangId::convertLocaleToLanguage(
660 SvtSysLocale().GetLocaleData().getLocale() );
661 // get the standard numeric format for this language
662 m_nFormatKey
= m_pFormatter
->GetStandardFormat( NUMBERFORMAT_NUMBER
, eSysLanguage
);
669 XubString sOldFormat
;
670 LanguageType aOldLang
;
671 GetFormat(sOldFormat
, aOldLang
);
673 sal_uInt32 nDestKey
= pFormatter
->TestNewString(sOldFormat
);
674 if (nDestKey
== NUMBERFORMAT_ENTRY_NOT_FOUND
)
676 // die Sprache des neuen Formatters
677 const SvNumberformat
* pDefaultEntry
= pFormatter
->GetEntry(0);
678 LanguageType aNewLang
= pDefaultEntry
? pDefaultEntry
->GetLanguage() : LANGUAGE_DONTKNOW
;
680 // den alten Format-String in die neue Sprache konvertieren
683 pFormatter
->PutandConvertEntry(sOldFormat
, nCheckPos
, nType
, nDestKey
, aOldLang
, aNewLang
);
684 m_nFormatKey
= nDestKey
;
686 m_pFormatter
= pFormatter
;
689 FormatChanged(FCT_FORMATTER
);
692 //------------------------------------------------------------------------------
693 void FormattedField::GetFormat(XubString
& rFormatString
, LanguageType
& eLang
) const
695 DBG_CHKTHIS(FormattedField
, NULL
);
696 const SvNumberformat
* pFormatEntry
= ImplGetFormatter()->GetEntry(m_nFormatKey
);
697 DBG_ASSERT(pFormatEntry
!= NULL
, "FormattedField::GetFormat: no number format for the given format key.");
698 rFormatString
= pFormatEntry
? pFormatEntry
->GetFormatstring() : XubString();
699 eLang
= pFormatEntry
? pFormatEntry
->GetLanguage() : LANGUAGE_DONTKNOW
;
702 //------------------------------------------------------------------------------
703 BOOL
FormattedField::SetFormat(const XubString
& rFormatString
, LanguageType eLang
)
705 DBG_CHKTHIS(FormattedField
, NULL
);
706 sal_uInt32 nNewKey
= ImplGetFormatter()->TestNewString(rFormatString
, eLang
);
707 if (nNewKey
== NUMBERFORMAT_ENTRY_NOT_FOUND
)
711 XubString
rFormat(rFormatString
);
712 if (!ImplGetFormatter()->PutEntry(rFormat
, nCheckPos
, nType
, nNewKey
, eLang
))
714 DBG_ASSERT(nNewKey
!= NUMBERFORMAT_ENTRY_NOT_FOUND
, "FormattedField::SetFormatString : PutEntry returned an invalid key !");
717 if (nNewKey
!= m_nFormatKey
)
718 SetFormatKey(nNewKey
);
722 //------------------------------------------------------------------------------
723 BOOL
FormattedField::GetThousandsSep() const
725 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey
),
726 "FormattedField::GetThousandsSep : your'e sure what your'e doing when setting the precision of a text format ?");
728 BOOL bThousand
, IsRed
;
729 USHORT nPrecision
, nAnzLeading
;
730 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey
, bThousand
, IsRed
, nPrecision
, nAnzLeading
);
735 //------------------------------------------------------------------------------
736 void FormattedField::SetThousandsSep(BOOL _bUseSeparator
)
738 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey
),
739 "FormattedField::SetThousandsSep : your'e sure what your'e doing when setting the precision of a text format ?");
741 // get the current settings
742 BOOL bThousand
, IsRed
;
743 USHORT nPrecision
, nAnzLeading
;
744 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey
, bThousand
, IsRed
, nPrecision
, nAnzLeading
);
745 if (bThousand
== _bUseSeparator
)
748 // we need the language for the following
750 String sFmtDescription
;
751 GetFormat(sFmtDescription
, eLang
);
753 // generate a new format ...
754 ImplGetFormatter()->GenerateFormat(sFmtDescription
, m_nFormatKey
, eLang
, _bUseSeparator
, IsRed
, nPrecision
, nAnzLeading
);
755 // ... and introduce it to the formatter
759 ImplGetFormatter()->PutEntry(sFmtDescription
, nCheckPos
, nType
, nNewKey
, eLang
);
762 ImplSetFormatKey(nNewKey
);
763 FormatChanged(FCT_THOUSANDSSEP
);
766 //------------------------------------------------------------------------------
767 USHORT
FormattedField::GetDecimalDigits() const
769 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey
),
770 "FormattedField::GetDecimalDigits : your'e sure what your'e doing when setting the precision of a text format ?");
772 BOOL bThousand
, IsRed
;
773 USHORT nPrecision
, nAnzLeading
;
774 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey
, bThousand
, IsRed
, nPrecision
, nAnzLeading
);
779 //------------------------------------------------------------------------------
780 void FormattedField::SetDecimalDigits(USHORT _nPrecision
)
782 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey
),
783 "FormattedField::SetDecimalDigits : your'e sure what your'e doing when setting the precision of a text format ?");
785 // get the current settings
786 BOOL bThousand
, IsRed
;
787 USHORT nPrecision
, nAnzLeading
;
788 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey
, bThousand
, IsRed
, nPrecision
, nAnzLeading
);
789 if (nPrecision
== _nPrecision
)
792 // we need the language for the following
794 String sFmtDescription
;
795 GetFormat(sFmtDescription
, eLang
);
797 // generate a new format ...
798 ImplGetFormatter()->GenerateFormat(sFmtDescription
, m_nFormatKey
, eLang
, bThousand
, IsRed
, _nPrecision
, nAnzLeading
);
799 // ... and introduce it to the formatter
803 ImplGetFormatter()->PutEntry(sFmtDescription
, nCheckPos
, nType
, nNewKey
, eLang
);
806 ImplSetFormatKey(nNewKey
);
807 FormatChanged(FCT_PRECISION
);
810 //------------------------------------------------------------------------------
811 void FormattedField::FormatChanged( FORMAT_CHANGE_TYPE _nWhat
)
813 DBG_CHKTHIS(FormattedField
, NULL
);
814 m_pLastOutputColor
= NULL
;
816 if ( ( 0 != ( _nWhat
& FCT_FORMATTER
) ) && m_pFormatter
)
817 m_pFormatter
->SetEvalDateFormat( NF_EVALDATEFORMAT_INTL_FORMAT
);
818 // 95845 - 03.04.2002 - fs@openoffice.org
823 //------------------------------------------------------------------------------
824 void FormattedField::Commit()
826 // remember the old text
827 String
sOld( GetText() );
832 // did the text change?
833 if ( GetText() != sOld
)
834 { // consider the field as modified
836 // but we have the most recent value now
837 m_bValueDirty
= FALSE
;
841 //------------------------------------------------------------------------------
842 void FormattedField::ReFormat()
844 if (!IsEmptyFieldEnabled() || GetText().Len())
846 if (TreatingAsNumber())
848 double dValue
= GetValue();
849 if ( m_bEnableNaN
&& ::rtl::math::isNan( dValue
) )
851 ImplSetValue( dValue
, TRUE
);
854 SetTextFormatted(GetTextValue());
858 //------------------------------------------------------------------------------
859 long FormattedField::Notify(NotifyEvent
& rNEvt
)
861 DBG_CHKTHIS(FormattedField
, NULL
);
863 if ((rNEvt
.GetType() == EVENT_KEYINPUT
) && !IsReadOnly())
865 const KeyEvent
& rKEvt
= *rNEvt
.GetKeyEvent();
866 USHORT nMod
= rKEvt
.GetKeyCode().GetModifier();
867 switch ( rKEvt
.GetKeyCode().GetCode() )
873 if (!nMod
&& ImplGetFormatter()->IsTextFormat(m_nFormatKey
))
875 // the base class would translate this into calls to Up/Down/First/Last,
876 // but we don't want this if we are text-formatted
882 if ((rNEvt
.GetType() == EVENT_COMMAND
) && !IsReadOnly())
884 const CommandEvent
* pCommand
= rNEvt
.GetCommandEvent();
885 if (pCommand
->GetCommand() == COMMAND_WHEEL
)
887 const CommandWheelData
* pData
= rNEvt
.GetCommandEvent()->GetWheelData();
888 if ((pData
->GetMode() == COMMAND_WHEEL_SCROLL
) && ImplGetFormatter()->IsTextFormat(m_nFormatKey
))
890 // same as above : prevent the base class from doing Up/Down-calls
891 // (normally I should put this test into the Up/Down methods itself, shouldn't I ?)
892 // FS - 71553 - 19.01.00
898 if (rNEvt
.GetType() == EVENT_LOSEFOCUS
)
900 // Sonderbehandlung fuer leere Texte
901 if (GetText().Len() == 0)
903 if (!IsEmptyFieldEnabled())
905 if (TreatingAsNumber())
907 ImplSetValue(m_dCurrentValue
, TRUE
);
912 String sNew
= GetTextValue();
914 SetTextFormatted(sNew
);
916 SetTextFormatted(m_sDefaultText
);
918 m_bValueDirty
= FALSE
;
927 return SpinField::Notify( rNEvt
);
930 //------------------------------------------------------------------------------
931 void FormattedField::SetMinValue(double dMin
)
933 DBG_CHKTHIS(FormattedField
, NULL
);
934 DBG_ASSERT(m_bTreatAsNumber
, "FormattedField::SetMinValue : only to be used in numeric mode !");
938 // fuer die Ueberpruefung des aktuellen Wertes an der neuen Grenze -> ImplSetValue
942 //------------------------------------------------------------------------------
943 void FormattedField::SetMaxValue(double dMax
)
945 DBG_CHKTHIS(FormattedField
, NULL
);
946 DBG_ASSERT(m_bTreatAsNumber
, "FormattedField::SetMaxValue : only to be used in numeric mode !");
950 // fuer die Ueberpruefung des aktuellen Wertes an der neuen Grenze -> ImplSetValue
954 //------------------------------------------------------------------------------
955 void FormattedField::SetTextValue(const XubString
& rText
)
957 DBG_CHKTHIS(FormattedField
, NULL
);
962 //------------------------------------------------------------------------------
963 void FormattedField::EnableEmptyField(BOOL bEnable
)
965 DBG_CHKTHIS(FormattedField
, NULL
);
966 if (bEnable
== m_bEnableEmptyField
)
969 m_bEnableEmptyField
= bEnable
;
970 if (!m_bEnableEmptyField
&& GetText().Len()==0)
971 ImplSetValue(m_dCurrentValue
, TRUE
);
974 //------------------------------------------------------------------------------
975 void FormattedField::ImplSetValue(double dVal
, BOOL bForce
)
977 DBG_CHKTHIS(FormattedField
, NULL
);
979 if (m_bHasMin
&& (dVal
<m_dMinValue
))
981 if (m_bHasMax
&& (dVal
>m_dMaxValue
))
983 if (!bForce
&& (dVal
== GetValue()))
986 DBG_ASSERT(ImplGetFormatter() != NULL
, "FormattedField::ImplSetValue : can't set a value without a formatter !");
988 m_bValueDirty
= FALSE
;
989 m_dCurrentValue
= dVal
;
992 if (ImplGetFormatter()->IsTextFormat(m_nFormatKey
))
994 // zuerst die Zahl als String im Standard-Format
996 ImplGetFormatter()->GetOutputString(dVal
, 0, sTemp
, &m_pLastOutputColor
);
997 // dann den String entsprechend dem Text-Format
998 ImplGetFormatter()->GetOutputString(sTemp
, m_nFormatKey
, sNewText
, &m_pLastOutputColor
);
1002 if( IsUsingInputStringForFormatting())
1003 ImplGetFormatter()->GetInputLineString(dVal
, m_nFormatKey
, sNewText
);
1005 ImplGetFormatter()->GetOutputString(dVal
, m_nFormatKey
, sNewText
, &m_pLastOutputColor
);
1008 ImplSetTextImpl(sNewText
, NULL
);
1009 m_bValueDirty
= FALSE
;
1010 DBG_ASSERT(CheckText(sNewText
), "FormattedField::ImplSetValue : formatted string doesn't match the criteria !");
1013 //------------------------------------------------------------------------------
1014 BOOL
FormattedField::ImplGetValue(double& dNewVal
)
1016 DBG_CHKTHIS(FormattedField
, NULL
);
1018 dNewVal
= m_dCurrentValue
;
1022 dNewVal
= m_dDefaultValue
;
1023 String
sText(GetText());
1027 DBG_ASSERT(ImplGetFormatter() != NULL
, "FormattedField::ImplGetValue : can't give you a current value without a formatter !");
1029 sal_uInt32 nFormatKey
= m_nFormatKey
; // IsNumberFormat veraendert den FormatKey ...
1031 if (ImplGetFormatter()->IsTextFormat(nFormatKey
) && m_bTreatAsNumber
)
1032 // damit wir in einem als Text formatierten Feld trotzdem eine Eingabe wie '1,1' erkennen ...
1035 // Sonderbehandlung fuer %-Formatierung
1036 if (ImplGetFormatter()->GetType(m_nFormatKey
) == NUMBERFORMAT_PERCENT
)
1038 // the language of our format
1039 LanguageType eLanguage
= m_pFormatter
->GetEntry(m_nFormatKey
)->GetLanguage();
1040 // the default number format for this language
1041 ULONG nStandardNumericFormat
= m_pFormatter
->GetStandardFormat(NUMBERFORMAT_NUMBER
, eLanguage
);
1043 sal_uInt32 nTempFormat
= nStandardNumericFormat
;
1045 if (m_pFormatter
->IsNumberFormat(sText
, nTempFormat
, dTemp
) &&
1046 NUMBERFORMAT_NUMBER
== m_pFormatter
->GetType(nTempFormat
))
1047 // der String entspricht einer Number-Formatierung, hat also nur kein %
1050 // (with this, a input of '3' becomes '3%', which then by the formatter is translated
1051 // into 0.03. Without this, the formatter would give us the double 3 for an input '3',
1052 // which equals 300 percent.
1054 if (!ImplGetFormatter()->IsNumberFormat(sText
, nFormatKey
, dNewVal
))
1058 if (m_bHasMin
&& (dNewVal
<m_dMinValue
))
1059 dNewVal
= m_dMinValue
;
1060 if (m_bHasMax
&& (dNewVal
>m_dMaxValue
))
1061 dNewVal
= m_dMaxValue
;
1065 //------------------------------------------------------------------------------
1066 void FormattedField::SetValue(double dVal
)
1068 DBG_CHKTHIS(FormattedField
, NULL
);
1069 ImplSetValue(dVal
, m_bValueDirty
);
1072 //------------------------------------------------------------------------------
1073 double FormattedField::GetValue()
1075 DBG_CHKTHIS(FormattedField
, NULL
);
1077 if ( !ImplGetValue( m_dCurrentValue
) )
1080 ::rtl::math::setNan( &m_dCurrentValue
);
1082 m_dCurrentValue
= m_dDefaultValue
;
1085 m_bValueDirty
= FALSE
;
1086 return m_dCurrentValue
;
1089 //------------------------------------------------------------------------------
1090 void FormattedField::Up()
1092 DBG_CHKTHIS(FormattedField
, NULL
);
1093 SetValue(GetValue() + m_dSpinSize
);
1094 // das setValue handelt Bereichsueberschreitungen (min/max) automatisch
1101 //------------------------------------------------------------------------------
1102 void FormattedField::Down()
1104 DBG_CHKTHIS(FormattedField
, NULL
);
1105 SetValue(GetValue() - m_dSpinSize
);
1112 //------------------------------------------------------------------------------
1113 void FormattedField::First()
1115 DBG_CHKTHIS(FormattedField
, NULL
);
1118 SetValue(m_dMinValue
);
1126 //------------------------------------------------------------------------------
1127 void FormattedField::Last()
1129 DBG_CHKTHIS(FormattedField
, NULL
);
1132 SetValue(m_dMaxValue
);
1140 //------------------------------------------------------------------------------
1141 void FormattedField::UseInputStringForFormatting( bool bUseInputStr
/* = true */ )
1143 m_bUseInputStringForFormatting
= bUseInputStr
;
1146 //------------------------------------------------------------------------------
1147 bool FormattedField::IsUsingInputStringForFormatting() const
1149 return m_bUseInputStringForFormatting
;
1153 //==============================================================================
1154 //------------------------------------------------------------------------------
1155 DoubleNumericField::~DoubleNumericField()
1157 #ifdef REGEXP_SUPPORT
1158 delete m_pConformanceTester
;
1160 delete m_pNumberValidator
;
1164 //------------------------------------------------------------------------------
1165 void DoubleNumericField::FormatChanged(FORMAT_CHANGE_TYPE nWhat
)
1167 ResetConformanceTester();
1168 FormattedField::FormatChanged(nWhat
);
1171 //------------------------------------------------------------------------------
1172 BOOL
DoubleNumericField::CheckText(const XubString
& sText
) const
1174 // We'd like to implement this using the NumberFormatter::IsNumberFormat, but unfortunately, this doesn't
1175 // recognize fragments of numbers (like, for instance "1e", which happens during entering e.g. "1e10")
1176 // Thus, the roundabout way via a regular expression
1178 #ifdef REGEXP_SUPPORT
1182 String sForceComplete
= '_';
1183 sForceComplete
+= sText
;
1184 sForceComplete
+= '_';
1186 USHORT nStart
= 0, nEnd
= sForceComplete
.Len();
1187 BOOL bFound
= m_pConformanceTester
->SearchFrwrd(sForceComplete
, &nStart
, &nEnd
);
1189 if (bFound
&& (nStart
== 0) && (nEnd
== sForceComplete
.Len()))
1194 return m_pNumberValidator
->isValidNumericFragment( sText
);
1198 //------------------------------------------------------------------------------
1199 void DoubleNumericField::ResetConformanceTester()
1201 // the thousands and the decimal separator are language dependent
1202 const SvNumberformat
* pFormatEntry
= ImplGetFormatter()->GetEntry(m_nFormatKey
);
1204 sal_Unicode cSeparatorThousand
= ',';
1205 sal_Unicode cSeparatorDecimal
= '.';
1209 MsLangId::convertLanguageToLocale( pFormatEntry
->GetLanguage(), aLocale
);
1210 LocaleDataWrapper
aLocaleInfo(::comphelper::getProcessServiceFactory(), aLocale
);
1212 String sSeparator
= aLocaleInfo
.getNumThousandSep();
1213 if (sSeparator
.Len())
1214 cSeparatorThousand
= sSeparator
.GetBuffer()[0];
1216 sSeparator
= aLocaleInfo
.getNumDecimalSep();
1217 if (sSeparator
.Len())
1218 cSeparatorDecimal
= sSeparator
.GetBuffer()[0];
1221 #ifdef REGEXP_SUPPORT
1222 String sDescription
= String::CreateFromAscii(szNumericInput
);
1224 String
sReplaceWith((sal_Unicode
)'\\');
1225 sReplaceWith
+= cSeparatorThousand
;
1226 sDescription
.SearchAndReplaceAscii("\\,", sReplaceWith
);
1228 sReplaceWith
= (sal_Unicode
)'\\';
1229 sReplaceWith
+= cSeparatorDecimal
;
1230 sDescription
.SearchAndReplaceAscii("\\.", sReplaceWith
);
1232 delete m_pConformanceTester
;
1234 SearchOptions aParam
;
1235 aParam
.algorithmType
= SearchAlgorithms_REGEXP
;
1236 aParam
.searchFlag
= SearchFlags::ALL_IGNORE_CASE
;
1237 aParam
.searchString
= sDescription
;
1238 aParam
.transliterateFlags
= 0;
1240 String sLanguage
, sCountry
;
1241 ConvertLanguageToIsoNames( pFormatEntry
? pFormatEntry
->GetLanguage() : LANGUAGE_ENGLISH_US
, sLanguage
, sCountry
);
1242 aParam
.Locale
.Language
= sLanguage
;
1243 aParam
.Locale
.Country
= sCountry
;
1245 m_pConformanceTester
= new ::utl::TextSearch(aParam
);
1247 delete m_pNumberValidator
;
1248 m_pNumberValidator
= new validation::NumberValidator( cSeparatorThousand
, cSeparatorDecimal
);
1253 //==============================================================================
1255 //------------------------------------------------------------------------------
1256 DoubleCurrencyField::DoubleCurrencyField(Window
* pParent
, WinBits nStyle
)
1257 :FormattedField(pParent
, nStyle
)
1258 ,m_bChangingFormat(FALSE
)
1260 m_bPrependCurrSym
= FALSE
;
1262 // initialize with a system currency format
1263 m_sCurrencySymbol
= SvtSysLocale().GetLocaleData().getCurrSymbol();
1264 UpdateCurrencyFormat();
1267 //------------------------------------------------------------------------------
1268 DoubleCurrencyField::DoubleCurrencyField(Window
* pParent
, const ResId
& rResId
)
1269 :FormattedField(pParent
, rResId
)
1270 ,m_bChangingFormat(FALSE
)
1272 m_bPrependCurrSym
= FALSE
;
1274 // initialize with a system currency format
1275 m_sCurrencySymbol
= SvtSysLocale().GetLocaleData().getCurrSymbol();
1276 UpdateCurrencyFormat();
1279 //------------------------------------------------------------------------------
1280 void DoubleCurrencyField::FormatChanged(FORMAT_CHANGE_TYPE nWhat
)
1282 if (m_bChangingFormat
)
1284 FormattedField::FormatChanged(nWhat
);
1292 case FCT_THOUSANDSSEP
:
1293 // the aspects which changed don't take our currency settings into account (in fact, they most probably
1295 UpdateCurrencyFormat();
1298 DBG_ERROR("DoubleCurrencyField::FormatChanged : somebody modified my key !");
1299 // We always build our own format from the settings we get via special methods (setCurrencySymbol etc.).
1300 // Nobody but ourself should modifiy the format key directly !
1304 FormattedField::FormatChanged(nWhat
);
1307 //------------------------------------------------------------------------------
1308 void DoubleCurrencyField::setCurrencySymbol(const String
& _sSymbol
)
1310 if (m_sCurrencySymbol
== _sSymbol
)
1313 m_sCurrencySymbol
= _sSymbol
;
1314 UpdateCurrencyFormat();
1315 FormatChanged(FCT_CURRENCY_SYMBOL
);
1318 //------------------------------------------------------------------------------
1319 void DoubleCurrencyField::setPrependCurrSym(BOOL _bPrepend
)
1321 if (m_bPrependCurrSym
== _bPrepend
)
1324 m_bPrependCurrSym
= _bPrepend
;
1325 UpdateCurrencyFormat();
1326 FormatChanged(FCT_CURRSYM_POSITION
);
1329 //------------------------------------------------------------------------------
1330 void DoubleCurrencyField::UpdateCurrencyFormat()
1333 XubString sOldFormat
;
1334 LanguageType eLanguage
;
1335 GetFormat(sOldFormat
, eLanguage
);
1336 BOOL bThSep
= GetThousandsSep();
1337 USHORT nDigits
= GetDecimalDigits();
1339 // build a new format string with the base class' and my own settings
1341 MsLangId::convertLanguageToLocale( eLanguage
, aLocale
);
1342 LocaleDataWrapper
aLocaleInfo(::comphelper::getProcessServiceFactory(), aLocale
);
1344 XubString sNewFormat
;
1348 sNewFormat
+= aLocaleInfo
.getNumThousandSep();
1349 sNewFormat
.AppendAscii("##0");
1356 sNewFormat
+= aLocaleInfo
.getNumDecimalSep();
1359 sTemp
.Fill(nDigits
, '0');
1360 sNewFormat
+= sTemp
;
1363 if (getPrependCurrSym())
1365 XubString sSymbol
= getCurrencySymbol();
1366 sSymbol
.EraseLeadingChars(' ');
1367 sSymbol
.EraseTrailingChars(' ');
1369 XubString sTemp
= String::CreateFromAscii("[$");
1371 sTemp
.AppendAscii("] ");
1372 sTemp
+= sNewFormat
;
1374 // for negative values : $ -0.00, not -$ 0.00 ...
1375 // (the real solution would be a possibility to choose a "positive currency format" and a "negative currency format" ...
1376 // But not now ... (and hey, you could take a formatted field for this ....))
1377 // FS - 31.03.00 74642
1378 sTemp
.AppendAscii(";[$");
1380 sTemp
.AppendAscii("] -");
1381 sTemp
+= sNewFormat
;
1387 XubString sTemp
= getCurrencySymbol();
1388 sTemp
.EraseLeadingChars(' ');
1389 sTemp
.EraseTrailingChars(' ');
1391 sNewFormat
+= String::CreateFromAscii(" [$");
1392 sNewFormat
+= sTemp
;
1396 // set this new basic format
1397 m_bChangingFormat
= TRUE
;
1398 SetFormat(sNewFormat
, eLanguage
);
1399 m_bChangingFormat
= FALSE
;