GPU-Calc: remove Alloc_Host_Ptr for clmem of NAN vector
[LibreOffice.git] / svtools / source / control / fmtfield.cxx
blob551da379cb6070b53e785495e60938854e86892e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <stdio.h>
21 #include <tools/debug.hxx>
22 #include <comphelper/processfactory.hxx>
23 #include <comphelper/string.hxx>
24 #include <unotools/localedatawrapper.hxx>
25 #include <vcl/svapp.hxx>
26 #include <vcl/builder.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 OUString& _rText );
86 private:
87 sal_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 )
125 :m_cThSep( _cThSep )
126 ,m_cDecSep( _cDecSep )
128 // build up our transition table
130 // how to procede from START
132 StateTransitions& rRow = m_aTransitions[ START ];
133 rRow.insert( Transition( '_', NUM_START ) );
134 // if we encounter the normalizing character, we want to procede with the number
137 // how to procede from NUM_START
139 StateTransitions& rRow = m_aTransitions[ NUM_START ];
141 // a sign is allowed
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 procede 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 procede 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 procede 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 procede 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 procede 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 sal_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;
234 ++pCheckPos;
235 continue;
239 // if we're here, there is no valid transition
240 break;
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 sal_Bool NumberValidator::isValidNumericFragment( const OUString& _rText )
255 if ( _rText.isEmpty() )
256 // empty strings are always allowed
257 return sal_True;
259 // normalize the string
260 OUString sNormalized( "_" );
261 sNormalized += _rText;
262 sNormalized += "_";
264 return implValidateNormalized( sNormalized );
268 SvNumberFormatter* FormattedField::StaticFormatter::s_cFormatter = NULL;
269 sal_uLong FormattedField::StaticFormatter::s_nReferences = 0;
271 SvNumberFormatter* FormattedField::StaticFormatter::GetFormatter()
273 if (!s_cFormatter)
275 // get the Office's locale and translate
276 LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
277 s_cFormatter = new SvNumberFormatter(
278 ::comphelper::getProcessComponentContext(),
279 eSysLanguage);
281 return s_cFormatter;
284 FormattedField::StaticFormatter::StaticFormatter()
286 ++s_nReferences;
289 FormattedField::StaticFormatter::~StaticFormatter()
291 if (--s_nReferences == 0)
293 delete s_cFormatter;
294 s_cFormatter = NULL;
298 DBG_NAME(FormattedField);
300 #define INIT_MEMBERS() \
301 m_aLastSelection(0,0) \
302 ,m_dMinValue(0) \
303 ,m_dMaxValue(0) \
304 ,m_bHasMin(sal_False) \
305 ,m_bHasMax(sal_False) \
306 ,m_bStrictFormat(sal_True) \
307 ,m_bValueDirty(sal_True) \
308 ,m_bEnableEmptyField(sal_True) \
309 ,m_bAutoColor(sal_False) \
310 ,m_bEnableNaN(sal_False) \
311 ,m_dCurrentValue(0) \
312 ,m_dDefaultValue(0) \
313 ,m_nFormatKey(0) \
314 ,m_pFormatter(NULL) \
315 ,m_dSpinSize(1) \
316 ,m_dSpinFirst(-1000000) \
317 ,m_dSpinLast(1000000) \
318 ,m_bTreatAsNumber(sal_True) \
319 ,m_pLastOutputColor(NULL) \
320 ,m_bUseInputStringForFormatting(false)
322 FormattedField::FormattedField(Window* pParent, WinBits nStyle, SvNumberFormatter* pInitialFormatter, sal_Int32 nFormatKey)
323 :SpinField(pParent, nStyle)
324 ,INIT_MEMBERS()
326 DBG_CTOR(FormattedField, NULL);
328 if (pInitialFormatter)
330 m_pFormatter = pInitialFormatter;
331 m_nFormatKey = nFormatKey;
335 FormattedField::FormattedField(Window* pParent, const ResId& rResId, SvNumberFormatter* pInitialFormatter, sal_Int32 nFormatKey)
336 :SpinField(pParent, rResId)
337 ,INIT_MEMBERS()
339 DBG_CTOR(FormattedField, NULL);
341 if (pInitialFormatter)
343 m_pFormatter = pInitialFormatter;
344 m_nFormatKey = nFormatKey;
347 extern "C" SAL_DLLPUBLIC_EXPORT Window* SAL_CALL makeFormattedField(Window *pParent, VclBuilder::stringmap &)
349 WinBits nWinBits = WB_BORDER;
350 return new FormattedField(pParent, nWinBits);
353 FormattedField::~FormattedField()
355 DBG_DTOR(FormattedField, NULL);
358 void FormattedField::SetText(const OUString& rStr)
360 DBG_CHKTHIS(FormattedField, NULL);
362 SpinField::SetText(rStr);
363 m_bValueDirty = sal_True;
366 void FormattedField::SetText( const OUString& rStr, const Selection& rNewSelection )
368 DBG_CHKTHIS(FormattedField, NULL);
370 SpinField::SetText( rStr, rNewSelection );
371 m_bValueDirty = sal_True;
374 void FormattedField::SetTextFormatted(const OUString& rStr)
376 DBG_CHKTHIS(FormattedField, NULL);
378 #if defined DBG_UTIL
379 if (ImplGetFormatter()->IsTextFormat(m_nFormatKey))
380 DBG_WARNING("FormattedField::SetTextFormatted : valid only with text formats !");
381 #endif
383 m_sCurrentTextValue = rStr;
385 OUString sFormatted;
386 double dNumber = 0.0;
387 // IsNumberFormat changes the format key parameter
388 sal_uInt32 nTempFormatKey = static_cast< sal_uInt32 >( m_nFormatKey );
389 if( IsUsingInputStringForFormatting() &&
390 ImplGetFormatter()->IsNumberFormat(m_sCurrentTextValue, nTempFormatKey, dNumber) )
392 ImplGetFormatter()->GetInputLineString(dNumber, m_nFormatKey, sFormatted);
394 else
396 OUString sTempIn(m_sCurrentTextValue);
397 OUString sTempOut(sFormatted);
398 ImplGetFormatter()->GetOutputString(sTempIn, m_nFormatKey, sTempOut, &m_pLastOutputColor);
399 m_sCurrentTextValue = sTempIn;
400 sFormatted = sTempOut;
403 // calculate the new selection
404 Selection aSel(GetSelection());
405 Selection aNewSel(aSel);
406 aNewSel.Justify();
407 sal_Int32 nNewLen = sFormatted.getLength();
408 sal_Int32 nCurrentLen = GetText().getLength();
409 if ((nNewLen > nCurrentLen) && (aNewSel.Max() == nCurrentLen))
410 { // the new text is longer and the cursor was behind the last char (of the old text)
411 if (aNewSel.Min() == 0)
412 { // the whole text was selected -> select the new text on the whole, too
413 aNewSel.Max() = nNewLen;
414 if (!nCurrentLen)
415 { // there wasn't really a previous selection (as there was no previous text), we're setting a new one -> check the selection options
416 sal_uLong nSelOptions = GetSettings().GetStyleSettings().GetSelectionOptions();
417 if (nSelOptions & SELECTION_OPTION_SHOWFIRST)
418 { // selection should be from right to left -> swap min and max
419 aNewSel.Min() = aNewSel.Max();
420 aNewSel.Max() = 0;
424 else if (aNewSel.Max() == aNewSel.Min())
425 { // there was no selection -> set the cursor behind the new last char
426 aNewSel.Max() = nNewLen;
427 aNewSel.Min() = nNewLen;
430 else if (aNewSel.Max() > nNewLen)
431 aNewSel.Max() = nNewLen;
432 else
433 aNewSel = aSel; // don't use the justified version
434 SpinField::SetText(sFormatted, aNewSel);
435 m_bValueDirty = sal_False;
438 OUString FormattedField::GetTextValue() const
440 if (m_bValueDirty)
442 ((FormattedField*)this)->m_sCurrentTextValue = GetText();
443 ((FormattedField*)this)->m_bValueDirty = sal_False;
445 return m_sCurrentTextValue;
448 void FormattedField::EnableNotANumber( sal_Bool _bEnable )
450 if ( m_bEnableNaN == _bEnable )
451 return;
453 m_bEnableNaN = _bEnable;
456 void FormattedField::SetAutoColor(sal_Bool _bAutomatic)
458 if (_bAutomatic == m_bAutoColor)
459 return;
461 m_bAutoColor = _bAutomatic;
462 if (m_bAutoColor)
463 { // if auto color is switched on, adjust the current text color, too
464 if (m_pLastOutputColor)
465 SetControlForeground(*m_pLastOutputColor);
466 else
467 SetControlForeground();
471 void FormattedField::impl_Modify(bool makeValueDirty)
473 DBG_CHKTHIS(FormattedField, NULL);
475 if (!IsStrictFormat())
477 if(makeValueDirty)
478 m_bValueDirty = sal_True;
479 SpinField::Modify();
480 return;
483 OUString sCheck = GetText();
484 if (CheckText(sCheck))
486 m_sLastValidText = sCheck;
487 m_aLastSelection = GetSelection();
488 if(makeValueDirty)
489 m_bValueDirty = sal_True;
491 else
493 ImplSetTextImpl(m_sLastValidText, &m_aLastSelection);
496 SpinField::Modify();
499 void FormattedField::Modify()
501 DBG_CHKTHIS(FormattedField, NULL);
503 impl_Modify();
506 void FormattedField::ImplSetTextImpl(const OUString& rNew, Selection* pNewSel)
508 DBG_CHKTHIS(FormattedField, NULL);
510 if (m_bAutoColor)
512 if (m_pLastOutputColor)
513 SetControlForeground(*m_pLastOutputColor);
514 else
515 SetControlForeground();
518 if (pNewSel)
519 SpinField::SetText(rNew, *pNewSel);
520 else
522 Selection aSel(GetSelection());
523 aSel.Justify();
525 sal_Int32 nNewLen = rNew.getLength();
526 sal_Int32 nCurrentLen = GetText().getLength();
528 if ((nNewLen > nCurrentLen) && (aSel.Max() == nCurrentLen))
529 { // new new text is longer and the cursor is behind the last char
530 if (aSel.Min() == 0)
531 { // the whole text was selected -> select the new text on the whole, too
532 aSel.Max() = nNewLen;
533 if (!nCurrentLen)
534 { // there wasn't really a previous selection (as there was no previous text), we're setting a new one -> check the selection options
535 sal_uLong nSelOptions = GetSettings().GetStyleSettings().GetSelectionOptions();
536 if (nSelOptions & SELECTION_OPTION_SHOWFIRST)
537 { // selection should be from right to left -> swap min and max
538 aSel.Min() = aSel.Max();
539 aSel.Max() = 0;
543 else if (aSel.Max() == aSel.Min())
544 { // there was no selection -> set the cursor behind the new last char
545 aSel.Max() = nNewLen;
546 aSel.Min() = nNewLen;
549 else if (aSel.Max() > nNewLen)
550 aSel.Max() = nNewLen;
551 SpinField::SetText(rNew, aSel);
554 m_bValueDirty = sal_True; // not always necessary, but better re-evaluate for safety reasons
557 long FormattedField::PreNotify(NotifyEvent& rNEvt)
559 DBG_CHKTHIS(FormattedField, NULL);
560 if (rNEvt.GetType() == EVENT_KEYINPUT)
561 m_aLastSelection = GetSelection();
562 return SpinField::PreNotify(rNEvt);
565 void FormattedField::ImplSetFormatKey(sal_uLong nFormatKey)
567 DBG_CHKTHIS(FormattedField, NULL);
569 m_nFormatKey = nFormatKey;
570 sal_Bool bNeedFormatter = (m_pFormatter == NULL) && (nFormatKey != 0);
571 if (bNeedFormatter)
573 ImplGetFormatter(); // this creates a standard formatter
575 // It might happen that the standard formatter makes no sense here, but it takes a default
576 // format. Thus, it is possible to set one of the other standard keys (which are spanning
577 // across multiple formatters).
578 m_nFormatKey = nFormatKey;
579 // When calling SetFormatKey without a formatter, the key must be one of the standard values
580 // that is available for all formatters (and, thus, also in this new one).
581 DBG_ASSERT(m_pFormatter->GetEntry(nFormatKey) != NULL, "FormattedField::ImplSetFormatKey : invalid format key !");
585 void FormattedField::SetFormatKey(sal_uLong nFormatKey)
587 DBG_CHKTHIS(FormattedField, NULL);
588 sal_Bool bNoFormatter = (m_pFormatter == NULL);
589 ImplSetFormatKey(nFormatKey);
590 FormatChanged((bNoFormatter && (m_pFormatter != NULL)) ? FCT_FORMATTER : FCT_KEYONLY);
593 void FormattedField::SetFormatter(SvNumberFormatter* pFormatter, sal_Bool bResetFormat)
595 DBG_CHKTHIS(FormattedField, NULL);
597 if (bResetFormat)
599 m_pFormatter = pFormatter;
601 // calc the default format key from the Office's UI locale
602 if ( m_pFormatter )
604 // get the Office's locale and translate
605 LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
606 // get the standard numeric format for this language
607 m_nFormatKey = m_pFormatter->GetStandardFormat( NUMBERFORMAT_NUMBER, eSysLanguage );
609 else
610 m_nFormatKey = 0;
612 else
614 LanguageType aOldLang;
615 OUString sOldFormat = GetFormat(aOldLang);
617 sal_uInt32 nDestKey = pFormatter->TestNewString(sOldFormat);
618 if (nDestKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
620 // language of the new formatter
621 const SvNumberformat* pDefaultEntry = pFormatter->GetEntry(0);
622 LanguageType aNewLang = pDefaultEntry ? pDefaultEntry->GetLanguage() : LANGUAGE_DONTKNOW;
624 // convert the old format string into the new language
625 sal_Int32 nCheckPos;
626 short nType;
627 pFormatter->PutandConvertEntry(sOldFormat, nCheckPos, nType, nDestKey, aOldLang, aNewLang);
628 m_nFormatKey = nDestKey;
630 m_pFormatter = pFormatter;
633 FormatChanged(FCT_FORMATTER);
636 OUString FormattedField::GetFormat(LanguageType& eLang) const
638 DBG_CHKTHIS(FormattedField, NULL);
639 const SvNumberformat* pFormatEntry = ImplGetFormatter()->GetEntry(m_nFormatKey);
640 DBG_ASSERT(pFormatEntry != NULL, "FormattedField::GetFormat: no number format for the given format key.");
641 OUString sFormatString = pFormatEntry ? pFormatEntry->GetFormatstring() : OUString();
642 eLang = pFormatEntry ? pFormatEntry->GetLanguage() : LANGUAGE_DONTKNOW;
644 return sFormatString;
647 sal_Bool FormattedField::SetFormat(const OUString& rFormatString, LanguageType eLang)
649 DBG_CHKTHIS(FormattedField, NULL);
650 sal_uInt32 nNewKey = ImplGetFormatter()->TestNewString(rFormatString, eLang);
651 if (nNewKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
653 sal_Int32 nCheckPos;
654 short nType;
655 OUString rFormat(rFormatString);
656 if (!ImplGetFormatter()->PutEntry(rFormat, nCheckPos, nType, nNewKey, eLang))
657 return sal_False;
658 DBG_ASSERT(nNewKey != NUMBERFORMAT_ENTRY_NOT_FOUND, "FormattedField::SetFormatString : PutEntry returned an invalid key !");
661 if (nNewKey != m_nFormatKey)
662 SetFormatKey(nNewKey);
663 return sal_True;
666 sal_Bool FormattedField::GetThousandsSep() const
668 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
669 "FormattedField::GetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
671 bool bThousand, IsRed;
672 sal_uInt16 nPrecision, nAnzLeading;
673 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nAnzLeading);
675 return bThousand;
678 void FormattedField::SetThousandsSep(sal_Bool _bUseSeparator)
680 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
681 "FormattedField::SetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
683 // get the current settings
684 bool bThousand, IsRed;
685 sal_uInt16 nPrecision, nAnzLeading;
686 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nAnzLeading);
687 if (bThousand == (bool)_bUseSeparator)
688 return;
690 // we need the language for the following
691 LanguageType eLang;
692 GetFormat(eLang);
694 // generate a new format ...
695 OUString sFmtDescription = ImplGetFormatter()->GenerateFormat(m_nFormatKey, eLang, _bUseSeparator, IsRed, nPrecision, nAnzLeading);
696 // ... and introduce it to the formatter
697 sal_Int32 nCheckPos = 0;
698 sal_uInt32 nNewKey;
699 short nType;
700 ImplGetFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
702 // set the new key
703 ImplSetFormatKey(nNewKey);
704 FormatChanged(FCT_THOUSANDSSEP);
707 sal_uInt16 FormattedField::GetDecimalDigits() const
709 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
710 "FormattedField::GetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
712 bool bThousand, IsRed;
713 sal_uInt16 nPrecision, nAnzLeading;
714 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nAnzLeading);
716 return nPrecision;
719 void FormattedField::SetDecimalDigits(sal_uInt16 _nPrecision)
721 DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
722 "FormattedField::SetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
724 // get the current settings
725 bool bThousand, IsRed;
726 sal_uInt16 nPrecision, nAnzLeading;
727 ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nAnzLeading);
728 if (nPrecision == _nPrecision)
729 return;
731 // we need the language for the following
732 LanguageType eLang;
733 GetFormat(eLang);
735 // generate a new format ...
736 OUString sFmtDescription = ImplGetFormatter()->GenerateFormat(m_nFormatKey, eLang, bThousand, IsRed, _nPrecision, nAnzLeading);
737 // ... and introduce it to the formatter
738 sal_Int32 nCheckPos = 0;
739 sal_uInt32 nNewKey;
740 short nType;
741 ImplGetFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
743 // set the new key
744 ImplSetFormatKey(nNewKey);
745 FormatChanged(FCT_PRECISION);
748 void FormattedField::FormatChanged( FORMAT_CHANGE_TYPE _nWhat )
750 DBG_CHKTHIS(FormattedField, NULL);
751 m_pLastOutputColor = NULL;
753 if ( ( 0 != ( _nWhat & FCT_FORMATTER ) ) && m_pFormatter )
754 m_pFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_INTL_FORMAT );
756 ReFormat();
759 void FormattedField::Commit()
761 // remember the old text
762 OUString sOld( GetText() );
764 // do the reformat
765 ReFormat();
767 // did the text change?
768 if ( GetText() != sOld )
769 { // consider the field as modified,
770 // but we already have the most recent value;
771 // don't reparse it from the text
772 // (can lead to data loss when the format is lossy,
773 // as is e.g. our default date format: 2-digit year!)
774 impl_Modify(false);
778 void FormattedField::ReFormat()
780 if (!IsEmptyFieldEnabled() || !GetText().isEmpty())
782 if (TreatingAsNumber())
784 double dValue = GetValue();
785 if ( m_bEnableNaN && ::rtl::math::isNan( dValue ) )
786 return;
787 ImplSetValue( dValue, sal_True );
789 else
790 SetTextFormatted(GetTextValue());
794 long FormattedField::Notify(NotifyEvent& rNEvt)
796 DBG_CHKTHIS(FormattedField, NULL);
798 if ((rNEvt.GetType() == EVENT_KEYINPUT) && !IsReadOnly())
800 const KeyEvent& rKEvt = *rNEvt.GetKeyEvent();
801 sal_uInt16 nMod = rKEvt.GetKeyCode().GetModifier();
802 switch ( rKEvt.GetKeyCode().GetCode() )
804 case KEY_UP:
805 case KEY_DOWN:
806 case KEY_PAGEUP:
807 case KEY_PAGEDOWN:
808 if (!nMod && ImplGetFormatter()->IsTextFormat(m_nFormatKey))
810 // the base class would translate this into calls to Up/Down/First/Last,
811 // but we don't want this if we are text-formatted
812 return 1;
817 if ((rNEvt.GetType() == EVENT_COMMAND) && !IsReadOnly())
819 const CommandEvent* pCommand = rNEvt.GetCommandEvent();
820 if (pCommand->GetCommand() == COMMAND_WHEEL)
822 const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData();
823 if ((pData->GetMode() == COMMAND_WHEEL_SCROLL) && ImplGetFormatter()->IsTextFormat(m_nFormatKey))
825 // same as above : prevent the base class from doing Up/Down-calls
826 // (normally I should put this test into the Up/Down methods itself, shouldn't I ?)
827 // FS - 71553 - 19.01.00
828 return 1;
833 if (rNEvt.GetType() == EVENT_LOSEFOCUS)
835 // Sonderbehandlung fuer leere Texte
836 if (GetText().isEmpty())
838 if (!IsEmptyFieldEnabled())
840 if (TreatingAsNumber())
842 ImplSetValue(m_dCurrentValue, sal_True);
843 Modify();
845 else
847 OUString sNew = GetTextValue();
848 if (!sNew.isEmpty())
849 SetTextFormatted(sNew);
850 else
851 SetTextFormatted(m_sDefaultText);
853 m_bValueDirty = sal_False;
856 else
858 Commit();
862 return SpinField::Notify( rNEvt );
865 void FormattedField::SetMinValue(double dMin)
867 DBG_CHKTHIS(FormattedField, NULL);
868 DBG_ASSERT(m_bTreatAsNumber, "FormattedField::SetMinValue : only to be used in numeric mode !");
870 m_dMinValue = dMin;
871 m_bHasMin = sal_True;
872 // for checking the current value at the new border -> ImplSetValue
873 ReFormat();
876 void FormattedField::SetMaxValue(double dMax)
878 DBG_CHKTHIS(FormattedField, NULL);
879 DBG_ASSERT(m_bTreatAsNumber, "FormattedField::SetMaxValue : only to be used in numeric mode !");
881 m_dMaxValue = dMax;
882 m_bHasMax = sal_True;
883 // for checking the current value at the new border -> ImplSetValue
884 ReFormat();
887 void FormattedField::SetTextValue(const OUString& rText)
889 DBG_CHKTHIS(FormattedField, NULL);
890 SetText(rText);
891 ReFormat();
894 void FormattedField::EnableEmptyField(sal_Bool bEnable)
896 DBG_CHKTHIS(FormattedField, NULL);
897 if (bEnable == m_bEnableEmptyField)
898 return;
900 m_bEnableEmptyField = bEnable;
901 if (!m_bEnableEmptyField && GetText().isEmpty())
902 ImplSetValue(m_dCurrentValue, sal_True);
905 void FormattedField::ImplSetValue(double dVal, sal_Bool bForce)
907 DBG_CHKTHIS(FormattedField, NULL);
909 if (m_bHasMin && (dVal<m_dMinValue))
910 dVal = m_dMinValue;
911 if (m_bHasMax && (dVal>m_dMaxValue))
912 dVal = m_dMaxValue;
913 if (!bForce && (dVal == GetValue()))
914 return;
916 DBG_ASSERT(ImplGetFormatter() != NULL, "FormattedField::ImplSetValue : can't set a value without a formatter !");
918 m_bValueDirty = sal_False;
919 m_dCurrentValue = dVal;
921 OUString sNewText;
922 if (ImplGetFormatter()->IsTextFormat(m_nFormatKey))
924 // first convert the number as string in standard format
925 OUString sTemp;
926 ImplGetFormatter()->GetOutputString(dVal, 0, sTemp, &m_pLastOutputColor);
927 // than encode the string in the corresponding text format
929 ImplGetFormatter()->GetOutputString(sTemp, m_nFormatKey, sNewText, &m_pLastOutputColor);
932 else
934 if( IsUsingInputStringForFormatting())
936 ImplGetFormatter()->GetInputLineString(dVal, m_nFormatKey, sNewText);
938 else
940 ImplGetFormatter()->GetOutputString(dVal, m_nFormatKey, sNewText, &m_pLastOutputColor);
944 ImplSetTextImpl(sNewText, NULL);
945 m_bValueDirty = sal_False;
946 DBG_ASSERT(CheckText(sNewText), "FormattedField::ImplSetValue : formatted string doesn't match the criteria !");
949 sal_Bool FormattedField::ImplGetValue(double& dNewVal)
951 DBG_CHKTHIS(FormattedField, NULL);
953 dNewVal = m_dCurrentValue;
954 if (!m_bValueDirty)
955 return sal_True;
957 dNewVal = m_dDefaultValue;
958 OUString sText(GetText());
959 if (sText.isEmpty())
960 return sal_True;
962 DBG_ASSERT(ImplGetFormatter() != NULL, "FormattedField::ImplGetValue : can't give you a current value without a formatter !");
964 sal_uInt32 nFormatKey = m_nFormatKey; // IsNumberFormat changes the FormatKey!
966 if (ImplGetFormatter()->IsTextFormat(nFormatKey) && m_bTreatAsNumber)
967 // for detection of values like "1,1" in fields that are formated as text
968 nFormatKey = 0;
970 // special treatment for percentage formatting
971 if (ImplGetFormatter()->GetType(m_nFormatKey) == NUMBERFORMAT_PERCENT)
973 // the language of our format
974 LanguageType eLanguage = m_pFormatter->GetEntry(m_nFormatKey)->GetLanguage();
975 // the default number format for this language
976 sal_uLong nStandardNumericFormat = m_pFormatter->GetStandardFormat(NUMBERFORMAT_NUMBER, eLanguage);
978 sal_uInt32 nTempFormat = nStandardNumericFormat;
979 double dTemp;
980 if (m_pFormatter->IsNumberFormat(sText, nTempFormat, dTemp) &&
981 NUMBERFORMAT_NUMBER == m_pFormatter->GetType(nTempFormat))
982 // the string is equivalent to a number formatted one (has no % sign) -> append it
983 sText += "%";
984 // (with this, a input of '3' becomes '3%', which then by the formatter is translated
985 // into 0.03. Without this, the formatter would give us the double 3 for an input '3',
986 // which equals 300 percent.
988 if (!ImplGetFormatter()->IsNumberFormat(sText, nFormatKey, dNewVal))
989 return sal_False;
991 if (m_bHasMin && (dNewVal<m_dMinValue))
992 dNewVal = m_dMinValue;
993 if (m_bHasMax && (dNewVal>m_dMaxValue))
994 dNewVal = m_dMaxValue;
995 return sal_True;
998 void FormattedField::SetValue(double dVal)
1000 DBG_CHKTHIS(FormattedField, NULL);
1001 ImplSetValue(dVal, m_bValueDirty);
1004 double FormattedField::GetValue()
1006 DBG_CHKTHIS(FormattedField, NULL);
1008 if ( !ImplGetValue( m_dCurrentValue ) )
1010 if ( m_bEnableNaN )
1011 ::rtl::math::setNan( &m_dCurrentValue );
1012 else
1013 m_dCurrentValue = m_dDefaultValue;
1016 m_bValueDirty = sal_False;
1017 return m_dCurrentValue;
1020 void FormattedField::Up()
1022 DBG_CHKTHIS(FormattedField, NULL);
1023 // setValue handles under- and overflows (min/max) automatically
1024 SetValue(GetValue() + m_dSpinSize);
1025 SetModifyFlag();
1026 Modify();
1028 SpinField::Up();
1031 void FormattedField::Down()
1033 DBG_CHKTHIS(FormattedField, NULL);
1034 SetValue(GetValue() - m_dSpinSize);
1035 SetModifyFlag();
1036 Modify();
1038 SpinField::Down();
1041 void FormattedField::First()
1043 DBG_CHKTHIS(FormattedField, NULL);
1044 if (m_bHasMin)
1046 SetValue(m_dMinValue);
1047 SetModifyFlag();
1048 Modify();
1051 SpinField::First();
1054 void FormattedField::Last()
1056 DBG_CHKTHIS(FormattedField, NULL);
1057 if (m_bHasMax)
1059 SetValue(m_dMaxValue);
1060 SetModifyFlag();
1061 Modify();
1064 SpinField::Last();
1067 void FormattedField::UseInputStringForFormatting( bool bUseInputStr /* = true */ )
1069 m_bUseInputStringForFormatting = bUseInputStr;
1072 bool FormattedField::IsUsingInputStringForFormatting() const
1074 return m_bUseInputStringForFormatting;
1077 DoubleNumericField::~DoubleNumericField()
1079 delete m_pNumberValidator;
1082 void DoubleNumericField::FormatChanged(FORMAT_CHANGE_TYPE nWhat)
1084 ResetConformanceTester();
1085 FormattedField::FormatChanged(nWhat);
1088 sal_Bool DoubleNumericField::CheckText(const OUString& sText) const
1090 // We'd like to implement this using the NumberFormatter::IsNumberFormat, but unfortunately, this doesn't
1091 // recognize fragments of numbers (like, for instance "1e", which happens during entering e.g. "1e10")
1092 // Thus, the roundabout way via a regular expression
1093 return m_pNumberValidator->isValidNumericFragment( sText );
1096 void DoubleNumericField::ResetConformanceTester()
1098 // the thousands and the decimal separator are language dependent
1099 const SvNumberformat* pFormatEntry = ImplGetFormatter()->GetEntry(m_nFormatKey);
1101 sal_Unicode cSeparatorThousand = ',';
1102 sal_Unicode cSeparatorDecimal = '.';
1103 if (pFormatEntry)
1105 LocaleDataWrapper aLocaleInfo( LanguageTag( pFormatEntry->GetLanguage()) );
1107 OUString sSeparator = aLocaleInfo.getNumThousandSep();
1108 if (!sSeparator.isEmpty())
1109 cSeparatorThousand = sSeparator[0];
1111 sSeparator = aLocaleInfo.getNumDecimalSep();
1112 if (!sSeparator.isEmpty())
1113 cSeparatorDecimal = sSeparator[0];
1116 delete m_pNumberValidator;
1117 m_pNumberValidator = new validation::NumberValidator( cSeparatorThousand, cSeparatorDecimal );
1120 DoubleCurrencyField::DoubleCurrencyField(Window* pParent, WinBits nStyle)
1121 :FormattedField(pParent, nStyle)
1122 ,m_bChangingFormat(sal_False)
1124 m_bPrependCurrSym = sal_False;
1126 // initialize with a system currency format
1127 m_sCurrencySymbol = SvtSysLocale().GetLocaleData().getCurrSymbol();
1128 UpdateCurrencyFormat();
1131 void DoubleCurrencyField::FormatChanged(FORMAT_CHANGE_TYPE nWhat)
1133 if (m_bChangingFormat)
1135 FormattedField::FormatChanged(nWhat);
1136 return;
1139 switch (nWhat)
1141 case FCT_FORMATTER:
1142 case FCT_PRECISION:
1143 case FCT_THOUSANDSSEP:
1144 // the aspects which changed don't take our currency settings into account (in fact, they most probably
1145 // destroyed them)
1146 UpdateCurrencyFormat();
1147 break;
1148 case FCT_KEYONLY:
1149 OSL_FAIL("DoubleCurrencyField::FormatChanged : somebody modified my key !");
1150 // We always build our own format from the settings we get via special methods (setCurrencySymbol etc.).
1151 // Nobody but ourself should modifiy the format key directly !
1152 break;
1155 FormattedField::FormatChanged(nWhat);
1158 void DoubleCurrencyField::setCurrencySymbol(const OUString& rSymbol)
1160 if (m_sCurrencySymbol == rSymbol)
1161 return;
1163 m_sCurrencySymbol = rSymbol;
1164 UpdateCurrencyFormat();
1165 FormatChanged(FCT_CURRENCY_SYMBOL);
1168 void DoubleCurrencyField::setPrependCurrSym(sal_Bool _bPrepend)
1170 if (m_bPrependCurrSym == _bPrepend)
1171 return;
1173 m_bPrependCurrSym = _bPrepend;
1174 UpdateCurrencyFormat();
1175 FormatChanged(FCT_CURRSYM_POSITION);
1178 void DoubleCurrencyField::UpdateCurrencyFormat()
1180 // the old settings
1181 LanguageType eLanguage;
1182 GetFormat(eLanguage);
1183 sal_Bool bThSep = GetThousandsSep();
1184 sal_uInt16 nDigits = GetDecimalDigits();
1186 // build a new format string with the base class' and my own settings
1188 /* Strangely with gcc 4.6.3 this needs a temporary LanguageTag, otherwise
1189 * there's
1190 * error: request for member 'getNumThousandSep' in 'aLocaleInfo', which is
1191 * of non-class type 'LocaleDataWrapper(LanguageTag)' */
1192 LanguageTag aLanguageTag( eLanguage);
1193 LocaleDataWrapper aLocaleInfo( aLanguageTag );
1195 OUStringBuffer sNewFormat;
1196 if (bThSep)
1198 sNewFormat.append('#');
1199 sNewFormat.append(aLocaleInfo.getNumThousandSep());
1200 sNewFormat.append("##0");
1202 else
1203 sNewFormat.append('0');
1205 if (nDigits)
1207 sNewFormat.append(aLocaleInfo.getNumDecimalSep());
1209 OUStringBuffer sTemp;
1210 comphelper::string::padToLength(sTemp, nDigits, '0');
1211 sNewFormat.append(sTemp);
1214 if (getPrependCurrSym())
1216 OUString sSymbol = getCurrencySymbol();
1217 sSymbol = comphelper::string::stripStart(sSymbol, ' ');
1218 sSymbol = comphelper::string::stripEnd(sSymbol, ' ');
1220 OUStringBuffer sTemp("[$");
1221 sTemp.append(sSymbol);
1222 sTemp.append("] ");
1223 sTemp.append(sNewFormat);
1225 // for negative values : $ -0.00, not -$ 0.00 ...
1226 // (the real solution would be a possibility to choose a "positive currency format" and a "negative currency format" ...
1227 // But not now ... (and hey, you could take a formatted field for this ....))
1228 // FS - 31.03.00 74642
1229 sTemp.append(";[$");
1230 sTemp.append(sSymbol);
1231 sTemp.append("] -");
1232 sTemp.append(sNewFormat);
1234 sNewFormat = sTemp;
1236 else
1238 OUString sTemp = getCurrencySymbol();
1239 sTemp = comphelper::string::stripStart(sTemp, ' ');
1240 sTemp = comphelper::string::stripEnd(sTemp, ' ');
1242 sNewFormat.append(" [$");
1243 sNewFormat.append(sTemp);
1244 sNewFormat.append(']');
1247 // set this new basic format
1248 m_bChangingFormat = sal_True;
1249 SetFormat(sNewFormat.makeStringAndClear(), eLanguage);
1250 m_bChangingFormat = sal_False;
1253 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */