update credits
[LibreOffice.git] / svtools / source / control / fmtfield.cxx
blob7e04e8ca54ecd4b841c0f3e5b7827a3a2dc7561a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <stdio.h>
22 #include <tools/debug.hxx>
23 #include <comphelper/processfactory.hxx>
24 #include <comphelper/string.hxx>
25 #include <unotools/localedatawrapper.hxx>
26 #include <vcl/svapp.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>
36 #include <map>
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 ...
46 namespace validation
48 // the states of our automat.
49 enum State
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
74 class NumberValidator
76 private:
77 TransitionTable m_aTransitions;
78 const sal_Unicode m_cThSep;
79 const sal_Unicode m_cDecSep;
81 public:
82 NumberValidator( const sal_Unicode _cThSep, const sal_Unicode _cDecSep );
84 sal_Bool isValidNumericFragment( const String& _rText );
86 private:
87 sal_Bool implValidateNormalized( const String& _rText );
90 //--------------------------------------------------------------------------
91 //..........................................................................
92 static void lcl_insertStopTransition( StateTransitions& _rRow )
94 _rRow.insert( Transition( '_', END ) );
97 //..........................................................................
98 static void lcl_insertStartExponentTransition( StateTransitions& _rRow )
100 _rRow.insert( Transition( 'e', EXPONENT_START ) );
103 //..........................................................................
104 static void lcl_insertSignTransitions( StateTransitions& _rRow, const State eNextState )
106 _rRow.insert( Transition( '-', eNextState ) );
107 _rRow.insert( Transition( '+', eNextState ) );
110 //..........................................................................
111 static void lcl_insertDigitTransitions( StateTransitions& _rRow, const State eNextState )
113 for ( sal_Unicode aChar = '0'; aChar <= '9'; ++aChar )
114 _rRow.insert( Transition( aChar, eNextState ) );
117 //..........................................................................
118 static void lcl_insertCommonPreCommaTransitions( StateTransitions& _rRow, const sal_Unicode _cThSep, const sal_Unicode _cDecSep )
120 // digits are allowed
121 lcl_insertDigitTransitions( _rRow, DIGIT_PRE_COMMA );
123 // the thousand separator is allowed
124 _rRow.insert( Transition( _cThSep, DIGIT_PRE_COMMA ) );
126 // a comma is allowed
127 _rRow.insert( Transition( _cDecSep, DIGIT_POST_COMMA ) );
130 //--------------------------------------------------------------------------
131 NumberValidator::NumberValidator( const sal_Unicode _cThSep, const sal_Unicode _cDecSep )
132 :m_cThSep( _cThSep )
133 ,m_cDecSep( _cDecSep )
135 // build up our transition table
137 // how to procede from START
139 StateTransitions& rRow = m_aTransitions[ START ];
140 rRow.insert( Transition( '_', NUM_START ) );
141 // if we encounter the normalizing character, we want to procede with the number
144 // how to procede from NUM_START
146 StateTransitions& rRow = m_aTransitions[ NUM_START ];
148 // a sign is allowed
149 lcl_insertSignTransitions( rRow, DIGIT_PRE_COMMA );
151 // common transitions for the two pre-comma states
152 lcl_insertCommonPreCommaTransitions( rRow, m_cThSep, m_cDecSep );
154 // the exponent may start here
155 // (this would mean string like "_+e10_", but this is a valid fragment, though no valid number)
156 lcl_insertStartExponentTransition( rRow );
159 // how to procede from DIGIT_PRE_COMMA
161 StateTransitions& rRow = m_aTransitions[ DIGIT_PRE_COMMA ];
163 // common transitions for the two pre-comma states
164 lcl_insertCommonPreCommaTransitions( rRow, m_cThSep, m_cDecSep );
166 // the exponent may start here
167 lcl_insertStartExponentTransition( rRow );
169 // the final transition indicating the end of the string
170 // (if there is no comma and no post-comma, then the string may end here)
171 lcl_insertStopTransition( rRow );
174 // how to procede from DIGIT_POST_COMMA
176 StateTransitions& rRow = m_aTransitions[ DIGIT_POST_COMMA ];
178 // there might be digits, which would keep the state at DIGIT_POST_COMMA
179 lcl_insertDigitTransitions( rRow, DIGIT_POST_COMMA );
181 // the exponent may start here
182 lcl_insertStartExponentTransition( rRow );
184 // the string may end here
185 lcl_insertStopTransition( rRow );
188 // how to procede from EXPONENT_START
190 StateTransitions& rRow = m_aTransitions[ EXPONENT_START ];
192 // there may be a sign
193 lcl_insertSignTransitions( rRow, EXPONENT_DIGIT );
195 // there may be digits
196 lcl_insertDigitTransitions( rRow, EXPONENT_DIGIT );
198 // the string may end here
199 lcl_insertStopTransition( rRow );
202 // how to procede from EXPONENT_DIGIT
204 StateTransitions& rRow = m_aTransitions[ EXPONENT_DIGIT ];
206 // there may be digits
207 lcl_insertDigitTransitions( rRow, EXPONENT_DIGIT );
209 // the string may end here
210 lcl_insertStopTransition( rRow );
213 // how to procede from END
215 /*StateTransitions& rRow =*/ m_aTransitions[ EXPONENT_DIGIT ];
216 // no valid transition to leave this state
217 // (note that we, for consistency, nevertheless want to have a row in the table)
221 //--------------------------------------------------------------------------
222 sal_Bool NumberValidator::implValidateNormalized( const String& _rText )
224 const sal_Unicode* pCheckPos = _rText.GetBuffer();
225 State eCurrentState = START;
227 while ( END != eCurrentState )
229 // look up the transition row for the current state
230 TransitionTable::const_iterator aRow = m_aTransitions.find( eCurrentState );
231 DBG_ASSERT( m_aTransitions.end() != aRow,
232 "NumberValidator::implValidateNormalized: invalid transition table (row not found)!" );
234 if ( m_aTransitions.end() != aRow )
236 // look up the current character in this row
237 StateTransitions::const_iterator aTransition = aRow->second.find( *pCheckPos );
238 if ( aRow->second.end() != aTransition )
240 // there is a valid transition for this character
241 eCurrentState = aTransition->second;
242 ++pCheckPos;
243 continue;
247 // if we're here, there is no valid transition
248 break;
251 DBG_ASSERT( ( END != eCurrentState ) || ( 0 == *pCheckPos ),
252 "NumberValidator::implValidateNormalized: inconsistency!" );
253 // if we're at END, then the string should be done, too - the string should be normalized, means ending
254 // a "_" and not containing any other "_" (except at the start), and "_" is the only possibility
255 // to reach the END state
257 // the string is valid if and only if we reached the final state
258 return ( END == eCurrentState );
261 //--------------------------------------------------------------------------
262 sal_Bool NumberValidator::isValidNumericFragment( const String& _rText )
264 if ( !_rText.Len() )
265 // empty strings are always allowed
266 return sal_True;
268 // normalize the string
269 String sNormalized( RTL_CONSTASCII_USTRINGPARAM("_") );
270 sNormalized.Append( _rText );
271 sNormalized.AppendAscii( "_" );
273 return implValidateNormalized( sNormalized );
277 //==============================================================================
278 SvNumberFormatter* FormattedField::StaticFormatter::s_cFormatter = NULL;
279 sal_uLong FormattedField::StaticFormatter::s_nReferences = 0;
281 //------------------------------------------------------------------------------
282 SvNumberFormatter* FormattedField::StaticFormatter::GetFormatter()
284 if (!s_cFormatter)
286 // get the Office's locale and translate
287 LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
288 s_cFormatter = new SvNumberFormatter(
289 ::comphelper::getProcessComponentContext(),
290 eSysLanguage);
292 return s_cFormatter;
295 //------------------------------------------------------------------------------
296 FormattedField::StaticFormatter::StaticFormatter()
298 ++s_nReferences;
301 //------------------------------------------------------------------------------
302 FormattedField::StaticFormatter::~StaticFormatter()
304 if (--s_nReferences == 0)
306 delete s_cFormatter;
307 s_cFormatter = NULL;
311 //==============================================================================
312 DBG_NAME(FormattedField);
314 #define INIT_MEMBERS() \
315 m_aLastSelection(0,0) \
316 ,m_dMinValue(0) \
317 ,m_dMaxValue(0) \
318 ,m_bHasMin(sal_False) \
319 ,m_bHasMax(sal_False) \
320 ,m_bStrictFormat(sal_True) \
321 ,m_bValueDirty(sal_True) \
322 ,m_bEnableEmptyField(sal_True) \
323 ,m_bAutoColor(sal_False) \
324 ,m_bEnableNaN(sal_False) \
325 ,m_dCurrentValue(0) \
326 ,m_dDefaultValue(0) \
327 ,m_nFormatKey(0) \
328 ,m_pFormatter(NULL) \
329 ,m_dSpinSize(1) \
330 ,m_dSpinFirst(-1000000) \
331 ,m_dSpinLast(1000000) \
332 ,m_bTreatAsNumber(sal_True) \
333 ,m_pLastOutputColor(NULL) \
334 ,m_bUseInputStringForFormatting(false)
336 //------------------------------------------------------------------------------
337 FormattedField::FormattedField(Window* pParent, WinBits nStyle, SvNumberFormatter* pInitialFormatter, sal_Int32 nFormatKey)
338 :SpinField(pParent, nStyle)
339 ,INIT_MEMBERS()
341 DBG_CTOR(FormattedField, NULL);
343 if (pInitialFormatter)
345 m_pFormatter = pInitialFormatter;
346 m_nFormatKey = nFormatKey;
350 //------------------------------------------------------------------------------
351 FormattedField::FormattedField(Window* pParent, const ResId& rResId, SvNumberFormatter* pInitialFormatter, sal_Int32 nFormatKey)
352 :SpinField(pParent, rResId)
353 ,INIT_MEMBERS()
355 DBG_CTOR(FormattedField, NULL);
357 if (pInitialFormatter)
359 m_pFormatter = pInitialFormatter;
360 m_nFormatKey = nFormatKey;
364 //------------------------------------------------------------------------------
365 FormattedField::~FormattedField()
367 DBG_DTOR(FormattedField, NULL);
370 //------------------------------------------------------------------------------
371 void FormattedField::SetText(const OUString& rStr)
373 DBG_CHKTHIS(FormattedField, NULL);
375 SpinField::SetText(rStr);
376 m_bValueDirty = sal_True;
379 //------------------------------------------------------------------------------
380 void FormattedField::SetText( const OUString& rStr, const Selection& rNewSelection )
382 DBG_CHKTHIS(FormattedField, NULL);
384 SpinField::SetText( rStr, rNewSelection );
385 m_bValueDirty = sal_True;
388 //------------------------------------------------------------------------------
389 void FormattedField::SetTextFormatted(const OUString& rStr)
391 DBG_CHKTHIS(FormattedField, NULL);
393 #if defined DBG_UTIL
394 if (ImplGetFormatter()->IsTextFormat(m_nFormatKey))
395 DBG_WARNING("FormattedField::SetTextFormatted : valid only with text formats !");
396 #endif
398 m_sCurrentTextValue = rStr;
400 OUString sFormatted;
401 double dNumber = 0.0;
402 // IsNumberFormat changes the format key parameter
403 sal_uInt32 nTempFormatKey = static_cast< sal_uInt32 >( m_nFormatKey );
404 if( IsUsingInputStringForFormatting() &&
405 ImplGetFormatter()->IsNumberFormat(m_sCurrentTextValue, nTempFormatKey, dNumber) )
407 ImplGetFormatter()->GetInputLineString(dNumber, m_nFormatKey, sFormatted);
409 else
411 OUString sTempIn(m_sCurrentTextValue);
412 OUString sTempOut(sFormatted);
413 ImplGetFormatter()->GetOutputString(sTempIn, m_nFormatKey, sTempOut, &m_pLastOutputColor);
414 m_sCurrentTextValue = sTempIn;
415 sFormatted = sTempOut;
418 // calculate the new selection
419 Selection aSel(GetSelection());
420 Selection aNewSel(aSel);
421 aNewSel.Justify();
422 sal_Int32 nNewLen = sFormatted.getLength();
423 sal_Int32 nCurrentLen = GetText().getLength();
424 if ((nNewLen > nCurrentLen) && (aNewSel.Max() == nCurrentLen))
425 { // the new text is longer and the cursor was behind the last char (of the old text)
426 if (aNewSel.Min() == 0)
427 { // the whole text was selected -> select the new text on the whole, too
428 aNewSel.Max() = nNewLen;
429 if (!nCurrentLen)
430 { // there wasn't really a previous selection (as there was no previous text), we're setting a new one -> check the selection options
431 sal_uLong nSelOptions = GetSettings().GetStyleSettings().GetSelectionOptions();
432 if (nSelOptions & SELECTION_OPTION_SHOWFIRST)
433 { // selection should be from right to left -> swap min and max
434 aNewSel.Min() = aNewSel.Max();
435 aNewSel.Max() = 0;
439 else if (aNewSel.Max() == aNewSel.Min())
440 { // there was no selection -> set the cursor behind the new last char
441 aNewSel.Max() = nNewLen;
442 aNewSel.Min() = nNewLen;
445 else if (aNewSel.Max() > nNewLen)
446 aNewSel.Max() = nNewLen;
447 else
448 aNewSel = aSel; // don't use the justified version
449 SpinField::SetText(sFormatted, aNewSel);
450 m_bValueDirty = sal_False;
453 //------------------------------------------------------------------------------
454 String FormattedField::GetTextValue() const
456 if (m_bValueDirty)
458 ((FormattedField*)this)->m_sCurrentTextValue = GetText();
459 ((FormattedField*)this)->m_bValueDirty = sal_False;
461 return m_sCurrentTextValue;
464 //------------------------------------------------------------------------------
465 void FormattedField::EnableNotANumber( sal_Bool _bEnable )
467 if ( m_bEnableNaN == _bEnable )
468 return;
470 m_bEnableNaN = _bEnable;
473 //------------------------------------------------------------------------------
474 void FormattedField::SetAutoColor(sal_Bool _bAutomatic)
476 if (_bAutomatic == m_bAutoColor)
477 return;
479 m_bAutoColor = _bAutomatic;
480 if (m_bAutoColor)
481 { // if auto color is switched on, adjust the current text color, too
482 if (m_pLastOutputColor)
483 SetControlForeground(*m_pLastOutputColor);
484 else
485 SetControlForeground();
489 //------------------------------------------------------------------------------
490 void FormattedField::impl_Modify(bool makeValueDirty)
492 DBG_CHKTHIS(FormattedField, NULL);
494 if (!IsStrictFormat())
496 if(makeValueDirty)
497 m_bValueDirty = sal_True;
498 SpinField::Modify();
499 return;
502 String sCheck = GetText();
503 if (CheckText(sCheck))
505 m_sLastValidText = sCheck;
506 m_aLastSelection = GetSelection();
507 if(makeValueDirty)
508 m_bValueDirty = sal_True;
510 else
512 ImplSetTextImpl(m_sLastValidText, &m_aLastSelection);
515 SpinField::Modify();
518 //------------------------------------------------------------------------------
519 void FormattedField::Modify()
521 DBG_CHKTHIS(FormattedField, NULL);
523 impl_Modify();
526 //------------------------------------------------------------------------------
527 void FormattedField::ImplSetTextImpl(const OUString& rNew, Selection* pNewSel)
529 DBG_CHKTHIS(FormattedField, NULL);
531 if (m_bAutoColor)
533 if (m_pLastOutputColor)
534 SetControlForeground(*m_pLastOutputColor);
535 else
536 SetControlForeground();
539 if (pNewSel)
540 SpinField::SetText(rNew, *pNewSel);
541 else
543 Selection aSel(GetSelection());
544 aSel.Justify();
546 sal_Int32 nNewLen = rNew.getLength();
547 sal_Int32 nCurrentLen = GetText().getLength();
549 if ((nNewLen > nCurrentLen) && (aSel.Max() == nCurrentLen))
550 { // new new text is longer and the cursor is behind the last char
551 if (aSel.Min() == 0)
552 { // the whole text was selected -> select the new text on the whole, too
553 aSel.Max() = nNewLen;
554 if (!nCurrentLen)
555 { // there wasn't really a previous selection (as there was no previous text), we're setting a new one -> check the selection options
556 sal_uLong nSelOptions = GetSettings().GetStyleSettings().GetSelectionOptions();
557 if (nSelOptions & SELECTION_OPTION_SHOWFIRST)
558 { // selection should be from right to left -> swap min and max
559 aSel.Min() = aSel.Max();
560 aSel.Max() = 0;
564 else if (aSel.Max() == aSel.Min())
565 { // there was no selection -> set the cursor behind the new last char
566 aSel.Max() = nNewLen;
567 aSel.Min() = nNewLen;
570 else if (aSel.Max() > nNewLen)
571 aSel.Max() = nNewLen;
572 SpinField::SetText(rNew, aSel);
575 m_bValueDirty = sal_True;
576 // muss nicht stimmen, aber sicherheitshalber ...
579 //------------------------------------------------------------------------------
580 long FormattedField::PreNotify(NotifyEvent& rNEvt)
582 DBG_CHKTHIS(FormattedField, NULL);
583 if (rNEvt.GetType() == EVENT_KEYINPUT)
584 m_aLastSelection = GetSelection();
585 return SpinField::PreNotify(rNEvt);
588 //------------------------------------------------------------------------------
589 void FormattedField::ImplSetFormatKey(sal_uLong nFormatKey)
591 DBG_CHKTHIS(FormattedField, NULL);
593 m_nFormatKey = nFormatKey;
594 sal_Bool bNeedFormatter = (m_pFormatter == NULL) && (nFormatKey != 0);
595 if (bNeedFormatter)
597 ImplGetFormatter(); // damit wird ein Standard-Formatter angelegt
599 m_nFormatKey = nFormatKey;
600 // kann sein, dass das in dem Standard-Formatter keinen Sinn macht, aber der nimmt dann ein Default-Format an.
601 // Auf diese Weise kann ich einfach einen der - formatteruebergreifended gleichen - Standard-Keys setzen.
602 DBG_ASSERT(m_pFormatter->GetEntry(nFormatKey) != NULL, "FormattedField::ImplSetFormatKey : invalid format key !");
603 // Wenn SetFormatKey aufgerufen wird, ohne dass ein Formatter existiert, muss der Key einer der Standard-Werte
604 // sein, der in allen Formattern (also auch in meinem neu angelegten) vorhanden ist.
608 //------------------------------------------------------------------------------
609 void FormattedField::SetFormatKey(sal_uLong nFormatKey)
611 DBG_CHKTHIS(FormattedField, NULL);
612 sal_Bool bNoFormatter = (m_pFormatter == NULL);
613 ImplSetFormatKey(nFormatKey);
614 FormatChanged((bNoFormatter && (m_pFormatter != NULL)) ? FCT_FORMATTER : FCT_KEYONLY);
617 //------------------------------------------------------------------------------
618 void FormattedField::SetFormatter(SvNumberFormatter* pFormatter, sal_Bool bResetFormat)
620 DBG_CHKTHIS(FormattedField, NULL);
622 if (bResetFormat)
624 m_pFormatter = pFormatter;
626 // calc the default format key from the Office's UI locale
627 if ( m_pFormatter )
629 // get the Office's locale and translate
630 LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
631 // get the standard numeric format for this language
632 m_nFormatKey = m_pFormatter->GetStandardFormat( NUMBERFORMAT_NUMBER, eSysLanguage );
634 else
635 m_nFormatKey = 0;
637 else
639 LanguageType aOldLang;
640 OUString sOldFormat = GetFormat(aOldLang);
642 sal_uInt32 nDestKey = pFormatter->TestNewString(sOldFormat);
643 if (nDestKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
645 // die Sprache des neuen Formatters
646 const SvNumberformat* pDefaultEntry = pFormatter->GetEntry(0);
647 LanguageType aNewLang = pDefaultEntry ? pDefaultEntry->GetLanguage() : LANGUAGE_DONTKNOW;
649 // den alten Format-String in die neue Sprache konvertieren
650 sal_Int32 nCheckPos;
651 short nType;
652 pFormatter->PutandConvertEntry(sOldFormat, nCheckPos, nType, nDestKey, aOldLang, aNewLang);
653 m_nFormatKey = nDestKey;
655 m_pFormatter = pFormatter;
658 FormatChanged(FCT_FORMATTER);
661 //------------------------------------------------------------------------------
662 OUString FormattedField::GetFormat(LanguageType& eLang) const
664 DBG_CHKTHIS(FormattedField, NULL);
665 const SvNumberformat* pFormatEntry = ImplGetFormatter()->GetEntry(m_nFormatKey);
666 DBG_ASSERT(pFormatEntry != NULL, "FormattedField::GetFormat: no number format for the given format key.");
667 OUString sFormatString = pFormatEntry ? pFormatEntry->GetFormatstring() : OUString();
668 eLang = pFormatEntry ? pFormatEntry->GetLanguage() : LANGUAGE_DONTKNOW;
670 return sFormatString;
673 //------------------------------------------------------------------------------
674 sal_Bool FormattedField::SetFormat(const OUString& rFormatString, LanguageType eLang)
676 DBG_CHKTHIS(FormattedField, NULL);
677 sal_uInt32 nNewKey = ImplGetFormatter()->TestNewString(rFormatString, eLang);
678 if (nNewKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
680 sal_Int32 nCheckPos;
681 short nType;
682 OUString rFormat(rFormatString);
683 if (!ImplGetFormatter()->PutEntry(rFormat, nCheckPos, nType, nNewKey, eLang))
684 return sal_False;
685 DBG_ASSERT(nNewKey != NUMBERFORMAT_ENTRY_NOT_FOUND, "FormattedField::SetFormatString : PutEntry returned an invalid key !");
688 if (nNewKey != m_nFormatKey)
689 SetFormatKey(nNewKey);
690 return sal_True;
693 //------------------------------------------------------------------------------
694 sal_Bool FormattedField::GetThousandsSep() const
696 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
697 "FormattedField::GetThousandsSep : your'e sure what your'e doing when setting the precision of a text format ?");
699 bool bThousand, IsRed;
700 sal_uInt16 nPrecision, nAnzLeading;
701 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nAnzLeading);
703 return bThousand;
706 //------------------------------------------------------------------------------
707 void FormattedField::SetThousandsSep(sal_Bool _bUseSeparator)
709 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
710 "FormattedField::SetThousandsSep : your'e sure what your'e doing when setting the precision of a text format ?");
712 // get the current settings
713 bool bThousand, IsRed;
714 sal_uInt16 nPrecision, nAnzLeading;
715 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nAnzLeading);
716 if (bThousand == (bool)_bUseSeparator)
717 return;
719 // we need the language for the following
720 LanguageType eLang;
721 GetFormat(eLang);
723 // generate a new format ...
724 OUString sFmtDescription = ImplGetFormatter()->GenerateFormat(m_nFormatKey, eLang, _bUseSeparator, IsRed, nPrecision, nAnzLeading);
725 // ... and introduce it to the formatter
726 sal_Int32 nCheckPos = 0;
727 sal_uInt32 nNewKey;
728 short nType;
729 ImplGetFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
731 // set the new key
732 ImplSetFormatKey(nNewKey);
733 FormatChanged(FCT_THOUSANDSSEP);
736 //------------------------------------------------------------------------------
737 sal_uInt16 FormattedField::GetDecimalDigits() const
739 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
740 "FormattedField::GetDecimalDigits : your'e sure what your'e doing when setting the precision of a text format ?");
742 bool bThousand, IsRed;
743 sal_uInt16 nPrecision, nAnzLeading;
744 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nAnzLeading);
746 return nPrecision;
749 //------------------------------------------------------------------------------
750 void FormattedField::SetDecimalDigits(sal_uInt16 _nPrecision)
752 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
753 "FormattedField::SetDecimalDigits : your'e sure what your'e doing when setting the precision of a text format ?");
755 // get the current settings
756 bool bThousand, IsRed;
757 sal_uInt16 nPrecision, nAnzLeading;
758 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nAnzLeading);
759 if (nPrecision == _nPrecision)
760 return;
762 // we need the language for the following
763 LanguageType eLang;
764 GetFormat(eLang);
766 // generate a new format ...
767 OUString sFmtDescription = ImplGetFormatter()->GenerateFormat(m_nFormatKey, eLang, bThousand, IsRed, _nPrecision, nAnzLeading);
768 // ... and introduce it to the formatter
769 sal_Int32 nCheckPos = 0;
770 sal_uInt32 nNewKey;
771 short nType;
772 ImplGetFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
774 // set the new key
775 ImplSetFormatKey(nNewKey);
776 FormatChanged(FCT_PRECISION);
779 //------------------------------------------------------------------------------
780 void FormattedField::FormatChanged( FORMAT_CHANGE_TYPE _nWhat )
782 DBG_CHKTHIS(FormattedField, NULL);
783 m_pLastOutputColor = NULL;
785 if ( ( 0 != ( _nWhat & FCT_FORMATTER ) ) && m_pFormatter )
786 m_pFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_INTL_FORMAT );
788 ReFormat();
791 //------------------------------------------------------------------------------
792 void FormattedField::Commit()
794 // remember the old text
795 OUString sOld( GetText() );
797 // do the reformat
798 ReFormat();
800 // did the text change?
801 if ( GetText() != sOld )
802 { // consider the field as modified,
803 // but we already have the most recent value;
804 // don't reparse it from the text
805 // (can lead to data loss when the format is lossy,
806 // as is e.g. our default date format: 2-digit year!)
807 impl_Modify(false);
811 //------------------------------------------------------------------------------
812 void FormattedField::ReFormat()
814 if (!IsEmptyFieldEnabled() || !GetText().isEmpty())
816 if (TreatingAsNumber())
818 double dValue = GetValue();
819 if ( m_bEnableNaN && ::rtl::math::isNan( dValue ) )
820 return;
821 ImplSetValue( dValue, sal_True );
823 else
824 SetTextFormatted(GetTextValue());
828 //------------------------------------------------------------------------------
829 long FormattedField::Notify(NotifyEvent& rNEvt)
831 DBG_CHKTHIS(FormattedField, NULL);
833 if ((rNEvt.GetType() == EVENT_KEYINPUT) && !IsReadOnly())
835 const KeyEvent& rKEvt = *rNEvt.GetKeyEvent();
836 sal_uInt16 nMod = rKEvt.GetKeyCode().GetModifier();
837 switch ( rKEvt.GetKeyCode().GetCode() )
839 case KEY_UP:
840 case KEY_DOWN:
841 case KEY_PAGEUP:
842 case KEY_PAGEDOWN:
843 if (!nMod && ImplGetFormatter()->IsTextFormat(m_nFormatKey))
845 // the base class would translate this into calls to Up/Down/First/Last,
846 // but we don't want this if we are text-formatted
847 return 1;
852 if ((rNEvt.GetType() == EVENT_COMMAND) && !IsReadOnly())
854 const CommandEvent* pCommand = rNEvt.GetCommandEvent();
855 if (pCommand->GetCommand() == COMMAND_WHEEL)
857 const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData();
858 if ((pData->GetMode() == COMMAND_WHEEL_SCROLL) && ImplGetFormatter()->IsTextFormat(m_nFormatKey))
860 // same as above : prevent the base class from doing Up/Down-calls
861 // (normally I should put this test into the Up/Down methods itself, shouldn't I ?)
862 // FS - 71553 - 19.01.00
863 return 1;
868 if (rNEvt.GetType() == EVENT_LOSEFOCUS)
870 // Sonderbehandlung fuer leere Texte
871 if (GetText().isEmpty())
873 if (!IsEmptyFieldEnabled())
875 if (TreatingAsNumber())
877 ImplSetValue(m_dCurrentValue, sal_True);
878 Modify();
880 else
882 String sNew = GetTextValue();
883 if (sNew.Len())
884 SetTextFormatted(sNew);
885 else
886 SetTextFormatted(m_sDefaultText);
888 m_bValueDirty = sal_False;
891 else
893 Commit();
897 return SpinField::Notify( rNEvt );
900 //------------------------------------------------------------------------------
901 void FormattedField::SetMinValue(double dMin)
903 DBG_CHKTHIS(FormattedField, NULL);
904 DBG_ASSERT(m_bTreatAsNumber, "FormattedField::SetMinValue : only to be used in numeric mode !");
906 m_dMinValue = dMin;
907 m_bHasMin = sal_True;
908 // fuer die Ueberpruefung des aktuellen Wertes an der neuen Grenze -> ImplSetValue
909 ReFormat();
912 //------------------------------------------------------------------------------
913 void FormattedField::SetMaxValue(double dMax)
915 DBG_CHKTHIS(FormattedField, NULL);
916 DBG_ASSERT(m_bTreatAsNumber, "FormattedField::SetMaxValue : only to be used in numeric mode !");
918 m_dMaxValue = dMax;
919 m_bHasMax = sal_True;
920 // fuer die Ueberpruefung des aktuellen Wertes an der neuen Grenze -> ImplSetValue
921 ReFormat();
924 //------------------------------------------------------------------------------
925 void FormattedField::SetTextValue(const OUString& rText)
927 DBG_CHKTHIS(FormattedField, NULL);
928 SetText(rText);
929 ReFormat();
932 //------------------------------------------------------------------------------
933 void FormattedField::EnableEmptyField(sal_Bool bEnable)
935 DBG_CHKTHIS(FormattedField, NULL);
936 if (bEnable == m_bEnableEmptyField)
937 return;
939 m_bEnableEmptyField = bEnable;
940 if (!m_bEnableEmptyField && GetText().isEmpty())
941 ImplSetValue(m_dCurrentValue, sal_True);
944 //------------------------------------------------------------------------------
945 void FormattedField::ImplSetValue(double dVal, sal_Bool bForce)
947 DBG_CHKTHIS(FormattedField, NULL);
949 if (m_bHasMin && (dVal<m_dMinValue))
950 dVal = m_dMinValue;
951 if (m_bHasMax && (dVal>m_dMaxValue))
952 dVal = m_dMaxValue;
953 if (!bForce && (dVal == GetValue()))
954 return;
956 DBG_ASSERT(ImplGetFormatter() != NULL, "FormattedField::ImplSetValue : can't set a value without a formatter !");
958 m_bValueDirty = sal_False;
959 m_dCurrentValue = dVal;
961 String sNewText;
962 if (ImplGetFormatter()->IsTextFormat(m_nFormatKey))
964 // zuerst die Zahl als String im Standard-Format
965 String sTemp;
966 ImplGetFormatter()->GetOutputString(dVal, 0, sTemp, &m_pLastOutputColor);
967 // dann den String entsprechend dem Text-Format
969 OUString sTempIn(sTemp);
970 OUString sTempOut;
971 ImplGetFormatter()->GetOutputString(sTempIn, m_nFormatKey, sTempOut, &m_pLastOutputColor);
972 sNewText = sTempOut;
975 else
977 if( IsUsingInputStringForFormatting())
979 ImplGetFormatter()->GetInputLineString(dVal, m_nFormatKey, sNewText);
981 else
983 ImplGetFormatter()->GetOutputString(dVal, m_nFormatKey, sNewText, &m_pLastOutputColor);
987 ImplSetTextImpl(sNewText, NULL);
988 m_bValueDirty = sal_False;
989 DBG_ASSERT(CheckText(sNewText), "FormattedField::ImplSetValue : formatted string doesn't match the criteria !");
992 //------------------------------------------------------------------------------
993 sal_Bool FormattedField::ImplGetValue(double& dNewVal)
995 DBG_CHKTHIS(FormattedField, NULL);
997 dNewVal = m_dCurrentValue;
998 if (!m_bValueDirty)
999 return sal_True;
1001 dNewVal = m_dDefaultValue;
1002 String sText(GetText());
1003 if (!sText.Len())
1004 return sal_True;
1006 DBG_ASSERT(ImplGetFormatter() != NULL, "FormattedField::ImplGetValue : can't give you a current value without a formatter !");
1008 sal_uInt32 nFormatKey = m_nFormatKey; // IsNumberFormat veraendert den FormatKey ...
1010 if (ImplGetFormatter()->IsTextFormat(nFormatKey) && m_bTreatAsNumber)
1011 // damit wir in einem als Text formatierten Feld trotzdem eine Eingabe wie '1,1' erkennen ...
1012 nFormatKey = 0;
1014 // Sonderbehandlung fuer %-Formatierung
1015 if (ImplGetFormatter()->GetType(m_nFormatKey) == NUMBERFORMAT_PERCENT)
1017 // the language of our format
1018 LanguageType eLanguage = m_pFormatter->GetEntry(m_nFormatKey)->GetLanguage();
1019 // the default number format for this language
1020 sal_uLong nStandardNumericFormat = m_pFormatter->GetStandardFormat(NUMBERFORMAT_NUMBER, eLanguage);
1022 sal_uInt32 nTempFormat = nStandardNumericFormat;
1023 double dTemp;
1024 if (m_pFormatter->IsNumberFormat(sText, nTempFormat, dTemp) &&
1025 NUMBERFORMAT_NUMBER == m_pFormatter->GetType(nTempFormat))
1026 // der String entspricht einer Number-Formatierung, hat also nur kein %
1027 // -> append it
1028 sText += '%';
1029 // (with this, a input of '3' becomes '3%', which then by the formatter is translated
1030 // into 0.03. Without this, the formatter would give us the double 3 for an input '3',
1031 // which equals 300 percent.
1033 if (!ImplGetFormatter()->IsNumberFormat(sText, nFormatKey, dNewVal))
1034 return sal_False;
1037 if (m_bHasMin && (dNewVal<m_dMinValue))
1038 dNewVal = m_dMinValue;
1039 if (m_bHasMax && (dNewVal>m_dMaxValue))
1040 dNewVal = m_dMaxValue;
1041 return sal_True;
1044 //------------------------------------------------------------------------------
1045 void FormattedField::SetValue(double dVal)
1047 DBG_CHKTHIS(FormattedField, NULL);
1048 ImplSetValue(dVal, m_bValueDirty);
1051 //------------------------------------------------------------------------------
1052 double FormattedField::GetValue()
1054 DBG_CHKTHIS(FormattedField, NULL);
1056 if ( !ImplGetValue( m_dCurrentValue ) )
1058 if ( m_bEnableNaN )
1059 ::rtl::math::setNan( &m_dCurrentValue );
1060 else
1061 m_dCurrentValue = m_dDefaultValue;
1064 m_bValueDirty = sal_False;
1065 return m_dCurrentValue;
1068 //------------------------------------------------------------------------------
1069 void FormattedField::Up()
1071 DBG_CHKTHIS(FormattedField, NULL);
1072 SetValue(GetValue() + m_dSpinSize);
1073 // das setValue handelt Bereichsueberschreitungen (min/max) automatisch
1074 SetModifyFlag();
1075 Modify();
1077 SpinField::Up();
1080 //------------------------------------------------------------------------------
1081 void FormattedField::Down()
1083 DBG_CHKTHIS(FormattedField, NULL);
1084 SetValue(GetValue() - m_dSpinSize);
1085 SetModifyFlag();
1086 Modify();
1088 SpinField::Down();
1091 //------------------------------------------------------------------------------
1092 void FormattedField::First()
1094 DBG_CHKTHIS(FormattedField, NULL);
1095 if (m_bHasMin)
1097 SetValue(m_dMinValue);
1098 SetModifyFlag();
1099 Modify();
1102 SpinField::First();
1105 //------------------------------------------------------------------------------
1106 void FormattedField::Last()
1108 DBG_CHKTHIS(FormattedField, NULL);
1109 if (m_bHasMax)
1111 SetValue(m_dMaxValue);
1112 SetModifyFlag();
1113 Modify();
1116 SpinField::Last();
1119 //------------------------------------------------------------------------------
1120 void FormattedField::UseInputStringForFormatting( bool bUseInputStr /* = true */ )
1122 m_bUseInputStringForFormatting = bUseInputStr;
1125 //------------------------------------------------------------------------------
1126 bool FormattedField::IsUsingInputStringForFormatting() const
1128 return m_bUseInputStringForFormatting;
1132 //==============================================================================
1133 //------------------------------------------------------------------------------
1134 DoubleNumericField::~DoubleNumericField()
1136 delete m_pNumberValidator;
1139 //------------------------------------------------------------------------------
1140 void DoubleNumericField::FormatChanged(FORMAT_CHANGE_TYPE nWhat)
1142 ResetConformanceTester();
1143 FormattedField::FormatChanged(nWhat);
1146 //------------------------------------------------------------------------------
1147 sal_Bool DoubleNumericField::CheckText(const OUString& sText) const
1149 // We'd like to implement this using the NumberFormatter::IsNumberFormat, but unfortunately, this doesn't
1150 // recognize fragments of numbers (like, for instance "1e", which happens during entering e.g. "1e10")
1151 // Thus, the roundabout way via a regular expression
1152 return m_pNumberValidator->isValidNumericFragment( sText );
1155 //------------------------------------------------------------------------------
1156 void DoubleNumericField::ResetConformanceTester()
1158 // the thousands and the decimal separator are language dependent
1159 const SvNumberformat* pFormatEntry = ImplGetFormatter()->GetEntry(m_nFormatKey);
1161 sal_Unicode cSeparatorThousand = ',';
1162 sal_Unicode cSeparatorDecimal = '.';
1163 if (pFormatEntry)
1165 LocaleDataWrapper aLocaleInfo( LanguageTag( pFormatEntry->GetLanguage()) );
1167 String sSeparator = aLocaleInfo.getNumThousandSep();
1168 if (sSeparator.Len())
1169 cSeparatorThousand = sSeparator.GetBuffer()[0];
1171 sSeparator = aLocaleInfo.getNumDecimalSep();
1172 if (sSeparator.Len())
1173 cSeparatorDecimal = sSeparator.GetBuffer()[0];
1176 delete m_pNumberValidator;
1177 m_pNumberValidator = new validation::NumberValidator( cSeparatorThousand, cSeparatorDecimal );
1181 //==============================================================================
1183 //------------------------------------------------------------------------------
1184 DoubleCurrencyField::DoubleCurrencyField(Window* pParent, WinBits nStyle)
1185 :FormattedField(pParent, nStyle)
1186 ,m_bChangingFormat(sal_False)
1188 m_bPrependCurrSym = sal_False;
1190 // initialize with a system currency format
1191 m_sCurrencySymbol = SvtSysLocale().GetLocaleData().getCurrSymbol();
1192 UpdateCurrencyFormat();
1195 //------------------------------------------------------------------------------
1196 void DoubleCurrencyField::FormatChanged(FORMAT_CHANGE_TYPE nWhat)
1198 if (m_bChangingFormat)
1200 FormattedField::FormatChanged(nWhat);
1201 return;
1204 switch (nWhat)
1206 case FCT_FORMATTER:
1207 case FCT_PRECISION:
1208 case FCT_THOUSANDSSEP:
1209 // the aspects which changed don't take our currency settings into account (in fact, they most probably
1210 // destroyed them)
1211 UpdateCurrencyFormat();
1212 break;
1213 case FCT_KEYONLY:
1214 OSL_FAIL("DoubleCurrencyField::FormatChanged : somebody modified my key !");
1215 // We always build our own format from the settings we get via special methods (setCurrencySymbol etc.).
1216 // Nobody but ourself should modifiy the format key directly !
1217 break;
1220 FormattedField::FormatChanged(nWhat);
1223 //------------------------------------------------------------------------------
1224 void DoubleCurrencyField::setCurrencySymbol(const OUString& rSymbol)
1226 if (m_sCurrencySymbol == rSymbol)
1227 return;
1229 m_sCurrencySymbol = rSymbol;
1230 UpdateCurrencyFormat();
1231 FormatChanged(FCT_CURRENCY_SYMBOL);
1234 //------------------------------------------------------------------------------
1235 void DoubleCurrencyField::setPrependCurrSym(sal_Bool _bPrepend)
1237 if (m_bPrependCurrSym == _bPrepend)
1238 return;
1240 m_bPrependCurrSym = _bPrepend;
1241 UpdateCurrencyFormat();
1242 FormatChanged(FCT_CURRSYM_POSITION);
1245 //------------------------------------------------------------------------------
1246 void DoubleCurrencyField::UpdateCurrencyFormat()
1248 // the old settings
1249 LanguageType eLanguage;
1250 GetFormat(eLanguage);
1251 sal_Bool bThSep = GetThousandsSep();
1252 sal_uInt16 nDigits = GetDecimalDigits();
1254 // build a new format string with the base class' and my own settings
1256 /* Strangely with gcc 4.6.3 this needs a temporary LanguageTag, otherwise
1257 * there's
1258 * error: request for member ‘getNumThousandSep’ in ‘aLocaleInfo’, which is
1259 * of non-class type ‘LocaleDataWrapper(LanguageTag)’ */
1260 LanguageTag aLanguageTag( eLanguage);
1261 LocaleDataWrapper aLocaleInfo( aLanguageTag );
1263 OUStringBuffer sNewFormat;
1264 if (bThSep)
1266 sNewFormat.append('#');
1267 sNewFormat.append(aLocaleInfo.getNumThousandSep());
1268 sNewFormat.append("##0");
1270 else
1271 sNewFormat.append('0');
1273 if (nDigits)
1275 sNewFormat.append(aLocaleInfo.getNumDecimalSep());
1277 OUStringBuffer sTemp;
1278 comphelper::string::padToLength(sTemp, nDigits, '0');
1279 sNewFormat.append(sTemp);
1282 if (getPrependCurrSym())
1284 OUString sSymbol = getCurrencySymbol();
1285 sSymbol = comphelper::string::stripStart(sSymbol, ' ');
1286 sSymbol = comphelper::string::stripEnd(sSymbol, ' ');
1288 OUStringBuffer sTemp("[$");
1289 sTemp.append(sSymbol);
1290 sTemp.append("] ");
1291 sTemp.append(sNewFormat);
1293 // for negative values : $ -0.00, not -$ 0.00 ...
1294 // (the real solution would be a possibility to choose a "positive currency format" and a "negative currency format" ...
1295 // But not now ... (and hey, you could take a formatted field for this ....))
1296 // FS - 31.03.00 74642
1297 sTemp.append(";[$");
1298 sTemp.append(sSymbol);
1299 sTemp.append("] -");
1300 sTemp.append(sNewFormat);
1302 sNewFormat = sTemp;
1304 else
1306 OUString sTemp = getCurrencySymbol();
1307 sTemp = comphelper::string::stripStart(sTemp, ' ');
1308 sTemp = comphelper::string::stripEnd(sTemp, ' ');
1310 sNewFormat.append(" [$");
1311 sNewFormat.append(sTemp);
1312 sNewFormat.append(']');
1315 // set this new basic format
1316 m_bChangingFormat = sal_True;
1317 SetFormat(sNewFormat.makeStringAndClear(), eLanguage);
1318 m_bChangingFormat = sal_False;
1321 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */