Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
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 <vcl/settings.hxx>
28 : #include <svl/zformat.hxx>
29 : #include <svtools/fmtfield.hxx>
30 : #include <i18nlangtag/languagetag.hxx>
31 : #include <com/sun/star/lang/Locale.hpp>
32 : #include <com/sun/star/util/SearchOptions.hpp>
33 : #include <com/sun/star/util/SearchAlgorithms.hpp>
34 : #include <com/sun/star/util/SearchResult.hpp>
35 : #include <com/sun/star/util/SearchFlags.hpp>
36 : #include <unotools/syslocale.hxx>
37 : #include <map>
38 : #include <rtl/math.hxx>
39 : #include <rtl/ustrbuf.hxx>
40 :
41 : using namespace ::com::sun::star::lang;
42 : using namespace ::com::sun::star::util;
43 :
44 : // hmm. No support for regular expression. Well, I always (not really :) wanted to write a finite automat
45 : // so here comes a finite automat ...
46 :
47 : namespace validation
48 : {
49 : // the states of our automat.
50 : enum State
51 : {
52 : START, // at the very start of the string
53 : NUM_START, // the very start of the number
54 :
55 : DIGIT_PRE_COMMA, // some pre-comma digits are read, perhaps including some thousand separators
56 :
57 : DIGIT_POST_COMMA, // reading digits after the comma
58 : EXPONENT_START, // at the very start of the exponent value
59 : // (means: not including the "e" which denotes the exponent)
60 : EXPONENT_DIGIT, // currently reading the digits of the exponent
61 :
62 : END // reached the end of the string
63 : };
64 :
65 : // a row in the transition table (means the set of states to be reached from a given state)
66 : typedef ::std::map< sal_Unicode, State > StateTransitions;
67 :
68 : // a single transition
69 : typedef StateTransitions::value_type Transition;
70 :
71 : // the complete transition table
72 : typedef ::std::map< State, StateTransitions > TransitionTable;
73 :
74 : // the validator class
75 0 : class NumberValidator
76 : {
77 : private:
78 : TransitionTable m_aTransitions;
79 : const sal_Unicode m_cThSep;
80 : const sal_Unicode m_cDecSep;
81 :
82 : public:
83 : NumberValidator( const sal_Unicode _cThSep, const sal_Unicode _cDecSep );
84 :
85 : bool isValidNumericFragment( const OUString& _rText );
86 :
87 : private:
88 : bool implValidateNormalized( const OUString& _rText );
89 : };
90 :
91 0 : static void lcl_insertStopTransition( StateTransitions& _rRow )
92 : {
93 0 : _rRow.insert( Transition( '_', END ) );
94 0 : }
95 :
96 0 : static void lcl_insertStartExponentTransition( StateTransitions& _rRow )
97 : {
98 0 : _rRow.insert( Transition( 'e', EXPONENT_START ) );
99 0 : }
100 :
101 0 : static void lcl_insertSignTransitions( StateTransitions& _rRow, const State eNextState )
102 : {
103 0 : _rRow.insert( Transition( '-', eNextState ) );
104 0 : _rRow.insert( Transition( '+', eNextState ) );
105 0 : }
106 :
107 0 : static void lcl_insertDigitTransitions( StateTransitions& _rRow, const State eNextState )
108 : {
109 0 : for ( sal_Unicode aChar = '0'; aChar <= '9'; ++aChar )
110 0 : _rRow.insert( Transition( aChar, eNextState ) );
111 0 : }
112 :
113 0 : static void lcl_insertCommonPreCommaTransitions( StateTransitions& _rRow, const sal_Unicode _cThSep, const sal_Unicode _cDecSep )
114 : {
115 : // digits are allowed
116 0 : lcl_insertDigitTransitions( _rRow, DIGIT_PRE_COMMA );
117 :
118 : // the thousand separator is allowed
119 0 : _rRow.insert( Transition( _cThSep, DIGIT_PRE_COMMA ) );
120 :
121 : // a comma is allowed
122 0 : _rRow.insert( Transition( _cDecSep, DIGIT_POST_COMMA ) );
123 0 : }
124 :
125 0 : NumberValidator::NumberValidator( const sal_Unicode _cThSep, const sal_Unicode _cDecSep )
126 : :m_cThSep( _cThSep )
127 0 : ,m_cDecSep( _cDecSep )
128 : {
129 : // build up our transition table
130 :
131 : // how to procede from START
132 : {
133 0 : StateTransitions& rRow = m_aTransitions[ START ];
134 0 : rRow.insert( Transition( '_', NUM_START ) );
135 : // if we encounter the normalizing character, we want to procede with the number
136 : }
137 :
138 : // how to procede from NUM_START
139 : {
140 0 : StateTransitions& rRow = m_aTransitions[ NUM_START ];
141 :
142 : // a sign is allowed
143 0 : lcl_insertSignTransitions( rRow, DIGIT_PRE_COMMA );
144 :
145 : // common transitions for the two pre-comma states
146 0 : lcl_insertCommonPreCommaTransitions( rRow, m_cThSep, m_cDecSep );
147 :
148 : // the exponent may start here
149 : // (this would mean string like "_+e10_", but this is a valid fragment, though no valid number)
150 0 : lcl_insertStartExponentTransition( rRow );
151 : }
152 :
153 : // how to procede from DIGIT_PRE_COMMA
154 : {
155 0 : StateTransitions& rRow = m_aTransitions[ DIGIT_PRE_COMMA ];
156 :
157 : // common transitions for the two pre-comma states
158 0 : lcl_insertCommonPreCommaTransitions( rRow, m_cThSep, m_cDecSep );
159 :
160 : // the exponent may start here
161 0 : lcl_insertStartExponentTransition( rRow );
162 :
163 : // the final transition indicating the end of the string
164 : // (if there is no comma and no post-comma, then the string may end here)
165 0 : lcl_insertStopTransition( rRow );
166 : }
167 :
168 : // how to procede from DIGIT_POST_COMMA
169 : {
170 0 : StateTransitions& rRow = m_aTransitions[ DIGIT_POST_COMMA ];
171 :
172 : // there might be digits, which would keep the state at DIGIT_POST_COMMA
173 0 : lcl_insertDigitTransitions( rRow, DIGIT_POST_COMMA );
174 :
175 : // the exponent may start here
176 0 : lcl_insertStartExponentTransition( rRow );
177 :
178 : // the string may end here
179 0 : lcl_insertStopTransition( rRow );
180 : }
181 :
182 : // how to procede from EXPONENT_START
183 : {
184 0 : StateTransitions& rRow = m_aTransitions[ EXPONENT_START ];
185 :
186 : // there may be a sign
187 0 : lcl_insertSignTransitions( rRow, EXPONENT_DIGIT );
188 :
189 : // there may be digits
190 0 : lcl_insertDigitTransitions( rRow, EXPONENT_DIGIT );
191 :
192 : // the string may end here
193 0 : lcl_insertStopTransition( rRow );
194 : }
195 :
196 : // how to procede from EXPONENT_DIGIT
197 : {
198 0 : StateTransitions& rRow = m_aTransitions[ EXPONENT_DIGIT ];
199 :
200 : // there may be digits
201 0 : lcl_insertDigitTransitions( rRow, EXPONENT_DIGIT );
202 :
203 : // the string may end here
204 0 : lcl_insertStopTransition( rRow );
205 : }
206 :
207 : // how to procede from END
208 : {
209 0 : /*StateTransitions& rRow =*/ m_aTransitions[ EXPONENT_DIGIT ];
210 : // no valid transition to leave this state
211 : // (note that we, for consistency, nevertheless want to have a row in the table)
212 : }
213 0 : }
214 :
215 0 : bool NumberValidator::implValidateNormalized( const OUString& _rText )
216 : {
217 0 : const sal_Unicode* pCheckPos = _rText.getStr();
218 0 : State eCurrentState = START;
219 :
220 0 : while ( END != eCurrentState )
221 : {
222 : // look up the transition row for the current state
223 0 : TransitionTable::const_iterator aRow = m_aTransitions.find( eCurrentState );
224 : DBG_ASSERT( m_aTransitions.end() != aRow,
225 : "NumberValidator::implValidateNormalized: invalid transition table (row not found)!" );
226 :
227 0 : if ( m_aTransitions.end() != aRow )
228 : {
229 : // look up the current character in this row
230 0 : StateTransitions::const_iterator aTransition = aRow->second.find( *pCheckPos );
231 0 : if ( aRow->second.end() != aTransition )
232 : {
233 : // there is a valid transition for this character
234 0 : eCurrentState = aTransition->second;
235 0 : ++pCheckPos;
236 0 : continue;
237 : }
238 : }
239 :
240 : // if we're here, there is no valid transition
241 0 : break;
242 : }
243 :
244 : DBG_ASSERT( ( END != eCurrentState ) || ( 0 == *pCheckPos ),
245 : "NumberValidator::implValidateNormalized: inconsistency!" );
246 : // if we're at END, then the string should be done, too - the string should be normalized, means ending
247 : // a "_" and not containing any other "_" (except at the start), and "_" is the only possibility
248 : // to reach the END state
249 :
250 : // the string is valid if and only if we reached the final state
251 0 : return ( END == eCurrentState );
252 : }
253 :
254 0 : bool NumberValidator::isValidNumericFragment( const OUString& _rText )
255 : {
256 0 : if ( _rText.isEmpty() )
257 : // empty strings are always allowed
258 0 : return true;
259 :
260 : // normalize the string
261 0 : OUString sNormalized( "_" );
262 0 : sNormalized += _rText;
263 0 : sNormalized += "_";
264 :
265 0 : return implValidateNormalized( sNormalized );
266 : }
267 : }
268 :
269 : SvNumberFormatter* FormattedField::StaticFormatter::s_cFormatter = NULL;
270 : sal_uLong FormattedField::StaticFormatter::s_nReferences = 0;
271 :
272 0 : SvNumberFormatter* FormattedField::StaticFormatter::GetFormatter()
273 : {
274 0 : if (!s_cFormatter)
275 : {
276 : // get the Office's locale and translate
277 0 : LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
278 : s_cFormatter = new SvNumberFormatter(
279 : ::comphelper::getProcessComponentContext(),
280 0 : eSysLanguage);
281 : }
282 0 : return s_cFormatter;
283 : }
284 :
285 0 : FormattedField::StaticFormatter::StaticFormatter()
286 : {
287 0 : ++s_nReferences;
288 0 : }
289 :
290 0 : FormattedField::StaticFormatter::~StaticFormatter()
291 : {
292 0 : if (--s_nReferences == 0)
293 : {
294 0 : delete s_cFormatter;
295 0 : s_cFormatter = NULL;
296 : }
297 0 : }
298 :
299 :
300 0 : FormattedField::FormattedField(Window* pParent, WinBits nStyle, SvNumberFormatter* pInitialFormatter, sal_Int32 nFormatKey)
301 : :SpinField(pParent, nStyle)
302 : ,m_aLastSelection(0,0)
303 : ,m_dMinValue(0)
304 : ,m_dMaxValue(0)
305 : ,m_bHasMin(false)
306 : ,m_bHasMax(false)
307 : ,m_bStrictFormat(true)
308 : ,m_bValueDirty(true)
309 : ,m_bEnableEmptyField(true)
310 : ,m_bAutoColor(false)
311 : ,m_bEnableNaN(false)
312 : ,m_dCurrentValue(0)
313 : ,m_dDefaultValue(0)
314 : ,m_nFormatKey(0)
315 : ,m_pFormatter(NULL)
316 : ,m_dSpinSize(1)
317 : ,m_dSpinFirst(-1000000)
318 : ,m_dSpinLast(1000000)
319 : ,m_bTreatAsNumber(true)
320 : ,m_pLastOutputColor(NULL)
321 0 : ,m_bUseInputStringForFormatting(false)
322 : {
323 :
324 0 : if (pInitialFormatter)
325 : {
326 0 : m_pFormatter = pInitialFormatter;
327 0 : m_nFormatKey = nFormatKey;
328 : }
329 0 : }
330 :
331 0 : extern "C" SAL_DLLPUBLIC_EXPORT Window* SAL_CALL makeFormattedField(Window *pParent, VclBuilder::stringmap &)
332 : {
333 0 : WinBits nWinBits = WB_BORDER;
334 0 : return new FormattedField(pParent, nWinBits);
335 : }
336 :
337 0 : FormattedField::~FormattedField()
338 : {
339 0 : }
340 :
341 0 : void FormattedField::SetText(const OUString& rStr)
342 : {
343 :
344 0 : SpinField::SetText(rStr);
345 0 : m_bValueDirty = true;
346 0 : }
347 :
348 0 : void FormattedField::SetText( const OUString& rStr, const Selection& rNewSelection )
349 : {
350 :
351 0 : SpinField::SetText( rStr, rNewSelection );
352 0 : m_bValueDirty = true;
353 0 : }
354 :
355 0 : void FormattedField::SetTextFormatted(const OUString& rStr)
356 : {
357 :
358 : #if defined DBG_UTIL
359 : if (ImplGetFormatter()->IsTextFormat(m_nFormatKey))
360 : DBG_WARNING("FormattedField::SetTextFormatted : valid only with text formats !");
361 : #endif
362 :
363 0 : m_sCurrentTextValue = rStr;
364 :
365 0 : OUString sFormatted;
366 0 : double dNumber = 0.0;
367 : // IsNumberFormat changes the format key parameter
368 0 : sal_uInt32 nTempFormatKey = static_cast< sal_uInt32 >( m_nFormatKey );
369 0 : if( IsUsingInputStringForFormatting() &&
370 0 : ImplGetFormatter()->IsNumberFormat(m_sCurrentTextValue, nTempFormatKey, dNumber) )
371 : {
372 0 : ImplGetFormatter()->GetInputLineString(dNumber, m_nFormatKey, sFormatted);
373 : }
374 : else
375 : {
376 : ImplGetFormatter()->GetOutputString(m_sCurrentTextValue,
377 : m_nFormatKey,
378 : sFormatted,
379 0 : &m_pLastOutputColor);
380 : }
381 :
382 : // calculate the new selection
383 0 : Selection aSel(GetSelection());
384 0 : Selection aNewSel(aSel);
385 0 : aNewSel.Justify();
386 0 : sal_Int32 nNewLen = sFormatted.getLength();
387 0 : sal_Int32 nCurrentLen = GetText().getLength();
388 0 : if ((nNewLen > nCurrentLen) && (aNewSel.Max() == nCurrentLen))
389 : { // the new text is longer and the cursor was behind the last char (of the old text)
390 0 : if (aNewSel.Min() == 0)
391 : { // the whole text was selected -> select the new text on the whole, too
392 0 : aNewSel.Max() = nNewLen;
393 0 : if (!nCurrentLen)
394 : { // there wasn't really a previous selection (as there was no previous text), we're setting a new one -> check the selection options
395 0 : sal_uLong nSelOptions = GetSettings().GetStyleSettings().GetSelectionOptions();
396 0 : if (nSelOptions & SELECTION_OPTION_SHOWFIRST)
397 : { // selection should be from right to left -> swap min and max
398 0 : aNewSel.Min() = aNewSel.Max();
399 0 : aNewSel.Max() = 0;
400 : }
401 : }
402 : }
403 0 : else if (aNewSel.Max() == aNewSel.Min())
404 : { // there was no selection -> set the cursor behind the new last char
405 0 : aNewSel.Max() = nNewLen;
406 0 : aNewSel.Min() = nNewLen;
407 : }
408 : }
409 0 : else if (aNewSel.Max() > nNewLen)
410 0 : aNewSel.Max() = nNewLen;
411 : else
412 0 : aNewSel = aSel; // don't use the justified version
413 0 : SpinField::SetText(sFormatted, aNewSel);
414 0 : m_bValueDirty = false;
415 0 : }
416 :
417 0 : OUString FormattedField::GetTextValue() const
418 : {
419 0 : if (m_bValueDirty)
420 : {
421 0 : ((FormattedField*)this)->m_sCurrentTextValue = GetText();
422 0 : ((FormattedField*)this)->m_bValueDirty = false;
423 : }
424 0 : return m_sCurrentTextValue;
425 : }
426 :
427 0 : void FormattedField::EnableNotANumber( bool _bEnable )
428 : {
429 0 : if ( m_bEnableNaN == _bEnable )
430 0 : return;
431 :
432 0 : m_bEnableNaN = _bEnable;
433 : }
434 :
435 0 : void FormattedField::SetAutoColor(bool _bAutomatic)
436 : {
437 0 : if (_bAutomatic == m_bAutoColor)
438 0 : return;
439 :
440 0 : m_bAutoColor = _bAutomatic;
441 0 : if (m_bAutoColor)
442 : { // if auto color is switched on, adjust the current text color, too
443 0 : if (m_pLastOutputColor)
444 0 : SetControlForeground(*m_pLastOutputColor);
445 : else
446 0 : SetControlForeground();
447 : }
448 : }
449 :
450 0 : void FormattedField::impl_Modify(bool makeValueDirty)
451 : {
452 :
453 0 : if (!IsStrictFormat())
454 : {
455 0 : if(makeValueDirty)
456 0 : m_bValueDirty = true;
457 0 : SpinField::Modify();
458 0 : return;
459 : }
460 :
461 0 : OUString sCheck = GetText();
462 0 : if (CheckText(sCheck))
463 : {
464 0 : m_sLastValidText = sCheck;
465 0 : m_aLastSelection = GetSelection();
466 0 : if(makeValueDirty)
467 0 : m_bValueDirty = true;
468 : }
469 : else
470 : {
471 0 : ImplSetTextImpl(m_sLastValidText, &m_aLastSelection);
472 : }
473 :
474 0 : SpinField::Modify();
475 : }
476 :
477 0 : void FormattedField::Modify()
478 : {
479 :
480 0 : impl_Modify();
481 0 : }
482 :
483 0 : void FormattedField::ImplSetTextImpl(const OUString& rNew, Selection* pNewSel)
484 : {
485 :
486 0 : if (m_bAutoColor)
487 : {
488 0 : if (m_pLastOutputColor)
489 0 : SetControlForeground(*m_pLastOutputColor);
490 : else
491 0 : SetControlForeground();
492 : }
493 :
494 0 : if (pNewSel)
495 0 : SpinField::SetText(rNew, *pNewSel);
496 : else
497 : {
498 0 : Selection aSel(GetSelection());
499 0 : aSel.Justify();
500 :
501 0 : sal_Int32 nNewLen = rNew.getLength();
502 0 : sal_Int32 nCurrentLen = GetText().getLength();
503 :
504 0 : if ((nNewLen > nCurrentLen) && (aSel.Max() == nCurrentLen))
505 : { // new new text is longer and the cursor is behind the last char
506 0 : if (aSel.Min() == 0)
507 : { // the whole text was selected -> select the new text on the whole, too
508 0 : aSel.Max() = nNewLen;
509 0 : if (!nCurrentLen)
510 : { // there wasn't really a previous selection (as there was no previous text), we're setting a new one -> check the selection options
511 0 : sal_uLong nSelOptions = GetSettings().GetStyleSettings().GetSelectionOptions();
512 0 : if (nSelOptions & SELECTION_OPTION_SHOWFIRST)
513 : { // selection should be from right to left -> swap min and max
514 0 : aSel.Min() = aSel.Max();
515 0 : aSel.Max() = 0;
516 : }
517 : }
518 : }
519 0 : else if (aSel.Max() == aSel.Min())
520 : { // there was no selection -> set the cursor behind the new last char
521 0 : aSel.Max() = nNewLen;
522 0 : aSel.Min() = nNewLen;
523 : }
524 : }
525 0 : else if (aSel.Max() > nNewLen)
526 0 : aSel.Max() = nNewLen;
527 0 : SpinField::SetText(rNew, aSel);
528 : }
529 :
530 0 : m_bValueDirty = true; // not always necessary, but better re-evaluate for safety reasons
531 0 : }
532 :
533 0 : bool FormattedField::PreNotify(NotifyEvent& rNEvt)
534 : {
535 0 : if (rNEvt.GetType() == EVENT_KEYINPUT)
536 0 : m_aLastSelection = GetSelection();
537 0 : return SpinField::PreNotify(rNEvt);
538 : }
539 :
540 0 : void FormattedField::ImplSetFormatKey(sal_uLong nFormatKey)
541 : {
542 :
543 0 : m_nFormatKey = nFormatKey;
544 0 : bool bNeedFormatter = (m_pFormatter == NULL) && (nFormatKey != 0);
545 0 : if (bNeedFormatter)
546 : {
547 0 : ImplGetFormatter(); // this creates a standard formatter
548 :
549 : // It might happen that the standard formatter makes no sense here, but it takes a default
550 : // format. Thus, it is possible to set one of the other standard keys (which are spanning
551 : // across multiple formatters).
552 0 : m_nFormatKey = nFormatKey;
553 : // When calling SetFormatKey without a formatter, the key must be one of the standard values
554 : // that is available for all formatters (and, thus, also in this new one).
555 : DBG_ASSERT(m_pFormatter->GetEntry(nFormatKey) != NULL, "FormattedField::ImplSetFormatKey : invalid format key !");
556 : }
557 0 : }
558 :
559 0 : void FormattedField::SetFormatKey(sal_uLong nFormatKey)
560 : {
561 0 : bool bNoFormatter = (m_pFormatter == NULL);
562 0 : ImplSetFormatKey(nFormatKey);
563 0 : FormatChanged((bNoFormatter && (m_pFormatter != NULL)) ? FCT_FORMATTER : FCT_KEYONLY);
564 0 : }
565 :
566 0 : void FormattedField::SetFormatter(SvNumberFormatter* pFormatter, bool bResetFormat)
567 : {
568 :
569 0 : if (bResetFormat)
570 : {
571 0 : m_pFormatter = pFormatter;
572 :
573 : // calc the default format key from the Office's UI locale
574 0 : if ( m_pFormatter )
575 : {
576 : // get the Office's locale and translate
577 0 : LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
578 : // get the standard numeric format for this language
579 0 : m_nFormatKey = m_pFormatter->GetStandardFormat( NUMBERFORMAT_NUMBER, eSysLanguage );
580 : }
581 : else
582 0 : m_nFormatKey = 0;
583 : }
584 : else
585 : {
586 : LanguageType aOldLang;
587 0 : OUString sOldFormat = GetFormat(aOldLang);
588 :
589 0 : sal_uInt32 nDestKey = pFormatter->TestNewString(sOldFormat);
590 0 : if (nDestKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
591 : {
592 : // language of the new formatter
593 0 : const SvNumberformat* pDefaultEntry = pFormatter->GetEntry(0);
594 0 : LanguageType aNewLang = pDefaultEntry ? pDefaultEntry->GetLanguage() : LANGUAGE_DONTKNOW;
595 :
596 : // convert the old format string into the new language
597 : sal_Int32 nCheckPos;
598 : short nType;
599 0 : pFormatter->PutandConvertEntry(sOldFormat, nCheckPos, nType, nDestKey, aOldLang, aNewLang);
600 0 : m_nFormatKey = nDestKey;
601 : }
602 0 : m_pFormatter = pFormatter;
603 : }
604 :
605 0 : FormatChanged(FCT_FORMATTER);
606 0 : }
607 :
608 0 : OUString FormattedField::GetFormat(LanguageType& eLang) const
609 : {
610 0 : const SvNumberformat* pFormatEntry = ImplGetFormatter()->GetEntry(m_nFormatKey);
611 : DBG_ASSERT(pFormatEntry != NULL, "FormattedField::GetFormat: no number format for the given format key.");
612 0 : OUString sFormatString = pFormatEntry ? pFormatEntry->GetFormatstring() : OUString();
613 0 : eLang = pFormatEntry ? pFormatEntry->GetLanguage() : LANGUAGE_DONTKNOW;
614 :
615 0 : return sFormatString;
616 : }
617 :
618 0 : bool FormattedField::SetFormat(const OUString& rFormatString, LanguageType eLang)
619 : {
620 0 : sal_uInt32 nNewKey = ImplGetFormatter()->TestNewString(rFormatString, eLang);
621 0 : if (nNewKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
622 : {
623 : sal_Int32 nCheckPos;
624 : short nType;
625 0 : OUString rFormat(rFormatString);
626 0 : if (!ImplGetFormatter()->PutEntry(rFormat, nCheckPos, nType, nNewKey, eLang))
627 0 : return false;
628 0 : DBG_ASSERT(nNewKey != NUMBERFORMAT_ENTRY_NOT_FOUND, "FormattedField::SetFormatString : PutEntry returned an invalid key !");
629 : }
630 :
631 0 : if (nNewKey != m_nFormatKey)
632 0 : SetFormatKey(nNewKey);
633 0 : return true;
634 : }
635 :
636 0 : bool FormattedField::GetThousandsSep() const
637 : {
638 : DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
639 : "FormattedField::GetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
640 :
641 : bool bThousand, IsRed;
642 : sal_uInt16 nPrecision, nAnzLeading;
643 0 : ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nAnzLeading);
644 :
645 0 : return bThousand;
646 : }
647 :
648 0 : void FormattedField::SetThousandsSep(bool _bUseSeparator)
649 : {
650 : DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
651 : "FormattedField::SetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
652 :
653 : // get the current settings
654 : bool bThousand, IsRed;
655 : sal_uInt16 nPrecision, nAnzLeading;
656 0 : ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nAnzLeading);
657 0 : if (bThousand == (bool)_bUseSeparator)
658 0 : return;
659 :
660 : // we need the language for the following
661 : LanguageType eLang;
662 0 : GetFormat(eLang);
663 :
664 : // generate a new format ...
665 0 : OUString sFmtDescription = ImplGetFormatter()->GenerateFormat(m_nFormatKey, eLang, _bUseSeparator, IsRed, nPrecision, nAnzLeading);
666 : // ... and introduce it to the formatter
667 0 : sal_Int32 nCheckPos = 0;
668 : sal_uInt32 nNewKey;
669 : short nType;
670 0 : ImplGetFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
671 :
672 : // set the new key
673 0 : ImplSetFormatKey(nNewKey);
674 0 : FormatChanged(FCT_THOUSANDSSEP);
675 : }
676 :
677 0 : sal_uInt16 FormattedField::GetDecimalDigits() const
678 : {
679 : DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
680 : "FormattedField::GetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
681 :
682 : bool bThousand, IsRed;
683 : sal_uInt16 nPrecision, nAnzLeading;
684 0 : ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nAnzLeading);
685 :
686 0 : return nPrecision;
687 : }
688 :
689 0 : void FormattedField::SetDecimalDigits(sal_uInt16 _nPrecision)
690 : {
691 : DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
692 : "FormattedField::SetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
693 :
694 : // get the current settings
695 : bool bThousand, IsRed;
696 : sal_uInt16 nPrecision, nAnzLeading;
697 0 : ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nAnzLeading);
698 0 : if (nPrecision == _nPrecision)
699 0 : return;
700 :
701 : // we need the language for the following
702 : LanguageType eLang;
703 0 : GetFormat(eLang);
704 :
705 : // generate a new format ...
706 0 : OUString sFmtDescription = ImplGetFormatter()->GenerateFormat(m_nFormatKey, eLang, bThousand, IsRed, _nPrecision, nAnzLeading);
707 : // ... and introduce it to the formatter
708 0 : sal_Int32 nCheckPos = 0;
709 : sal_uInt32 nNewKey;
710 : short nType;
711 0 : ImplGetFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
712 :
713 : // set the new key
714 0 : ImplSetFormatKey(nNewKey);
715 0 : FormatChanged(FCT_PRECISION);
716 : }
717 :
718 0 : void FormattedField::FormatChanged( FORMAT_CHANGE_TYPE _nWhat )
719 : {
720 0 : m_pLastOutputColor = NULL;
721 :
722 0 : if ( ( 0 != ( _nWhat & FCT_FORMATTER ) ) && m_pFormatter )
723 0 : m_pFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_INTL_FORMAT );
724 :
725 0 : ReFormat();
726 0 : }
727 :
728 0 : void FormattedField::Commit()
729 : {
730 : // remember the old text
731 0 : OUString sOld( GetText() );
732 :
733 : // do the reformat
734 0 : ReFormat();
735 :
736 : // did the text change?
737 0 : if ( GetText() != sOld )
738 : { // consider the field as modified,
739 : // but we already have the most recent value;
740 : // don't reparse it from the text
741 : // (can lead to data loss when the format is lossy,
742 : // as is e.g. our default date format: 2-digit year!)
743 0 : impl_Modify(false);
744 0 : }
745 0 : }
746 :
747 0 : void FormattedField::ReFormat()
748 : {
749 0 : if (!IsEmptyFieldEnabled() || !GetText().isEmpty())
750 : {
751 0 : if (TreatingAsNumber())
752 : {
753 0 : double dValue = GetValue();
754 0 : if ( m_bEnableNaN && ::rtl::math::isNan( dValue ) )
755 0 : return;
756 0 : ImplSetValue( dValue, true );
757 : }
758 : else
759 0 : SetTextFormatted(GetTextValue());
760 : }
761 : }
762 :
763 0 : bool FormattedField::Notify(NotifyEvent& rNEvt)
764 : {
765 :
766 0 : if ((rNEvt.GetType() == EVENT_KEYINPUT) && !IsReadOnly())
767 : {
768 0 : const KeyEvent& rKEvt = *rNEvt.GetKeyEvent();
769 0 : sal_uInt16 nMod = rKEvt.GetKeyCode().GetModifier();
770 0 : switch ( rKEvt.GetKeyCode().GetCode() )
771 : {
772 : case KEY_UP:
773 : case KEY_DOWN:
774 : case KEY_PAGEUP:
775 : case KEY_PAGEDOWN:
776 0 : if (!nMod && ImplGetFormatter()->IsTextFormat(m_nFormatKey))
777 : {
778 : // the base class would translate this into calls to Up/Down/First/Last,
779 : // but we don't want this if we are text-formatted
780 0 : return true;
781 : }
782 : }
783 : }
784 :
785 0 : if ((rNEvt.GetType() == EVENT_COMMAND) && !IsReadOnly())
786 : {
787 0 : const CommandEvent* pCommand = rNEvt.GetCommandEvent();
788 0 : if (pCommand->GetCommand() == COMMAND_WHEEL)
789 : {
790 0 : const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData();
791 0 : if ((pData->GetMode() == COMMAND_WHEEL_SCROLL) && ImplGetFormatter()->IsTextFormat(m_nFormatKey))
792 : {
793 : // same as above : prevent the base class from doing Up/Down-calls
794 : // (normally I should put this test into the Up/Down methods itself, shouldn't I ?)
795 : // FS - 71553 - 19.01.00
796 0 : return true;
797 : }
798 : }
799 : }
800 :
801 0 : if (rNEvt.GetType() == EVENT_LOSEFOCUS)
802 : {
803 : // Sonderbehandlung fuer leere Texte
804 0 : if (GetText().isEmpty())
805 : {
806 0 : if (!IsEmptyFieldEnabled())
807 : {
808 0 : if (TreatingAsNumber())
809 : {
810 0 : ImplSetValue(m_dCurrentValue, true);
811 0 : Modify();
812 : }
813 : else
814 : {
815 0 : OUString sNew = GetTextValue();
816 0 : if (!sNew.isEmpty())
817 0 : SetTextFormatted(sNew);
818 : else
819 0 : SetTextFormatted(m_sDefaultText);
820 : }
821 0 : m_bValueDirty = false;
822 : }
823 : }
824 : else
825 : {
826 0 : Commit();
827 : }
828 : }
829 :
830 0 : return SpinField::Notify( rNEvt );
831 : }
832 :
833 0 : void FormattedField::SetMinValue(double dMin)
834 : {
835 : DBG_ASSERT(m_bTreatAsNumber, "FormattedField::SetMinValue : only to be used in numeric mode !");
836 :
837 0 : m_dMinValue = dMin;
838 0 : m_bHasMin = true;
839 : // for checking the current value at the new border -> ImplSetValue
840 0 : ReFormat();
841 0 : }
842 :
843 0 : void FormattedField::SetMaxValue(double dMax)
844 : {
845 : DBG_ASSERT(m_bTreatAsNumber, "FormattedField::SetMaxValue : only to be used in numeric mode !");
846 :
847 0 : m_dMaxValue = dMax;
848 0 : m_bHasMax = true;
849 : // for checking the current value at the new border -> ImplSetValue
850 0 : ReFormat();
851 0 : }
852 :
853 0 : void FormattedField::SetTextValue(const OUString& rText)
854 : {
855 0 : SetText(rText);
856 0 : ReFormat();
857 0 : }
858 :
859 0 : void FormattedField::EnableEmptyField(bool bEnable)
860 : {
861 0 : if (bEnable == m_bEnableEmptyField)
862 0 : return;
863 :
864 0 : m_bEnableEmptyField = bEnable;
865 0 : if (!m_bEnableEmptyField && GetText().isEmpty())
866 0 : ImplSetValue(m_dCurrentValue, true);
867 : }
868 :
869 0 : void FormattedField::ImplSetValue(double dVal, bool bForce)
870 : {
871 :
872 0 : if (m_bHasMin && (dVal<m_dMinValue))
873 0 : dVal = m_dMinValue;
874 0 : if (m_bHasMax && (dVal>m_dMaxValue))
875 0 : dVal = m_dMaxValue;
876 0 : if (!bForce && (dVal == GetValue()))
877 0 : return;
878 :
879 : DBG_ASSERT(ImplGetFormatter() != NULL, "FormattedField::ImplSetValue : can't set a value without a formatter !");
880 :
881 0 : m_bValueDirty = false;
882 0 : m_dCurrentValue = dVal;
883 :
884 0 : OUString sNewText;
885 0 : if (ImplGetFormatter()->IsTextFormat(m_nFormatKey))
886 : {
887 : // first convert the number as string in standard format
888 0 : OUString sTemp;
889 0 : ImplGetFormatter()->GetOutputString(dVal, 0, sTemp, &m_pLastOutputColor);
890 : // then encode the string in the corresponding text format
891 0 : ImplGetFormatter()->GetOutputString(sTemp, m_nFormatKey, sNewText, &m_pLastOutputColor);
892 : }
893 : else
894 : {
895 0 : if( IsUsingInputStringForFormatting())
896 : {
897 0 : ImplGetFormatter()->GetInputLineString(dVal, m_nFormatKey, sNewText);
898 : }
899 : else
900 : {
901 0 : ImplGetFormatter()->GetOutputString(dVal, m_nFormatKey, sNewText, &m_pLastOutputColor);
902 : }
903 : }
904 :
905 0 : ImplSetTextImpl(sNewText, NULL);
906 0 : m_bValueDirty = false;
907 0 : DBG_ASSERT(CheckText(sNewText), "FormattedField::ImplSetValue : formatted string doesn't match the criteria !");
908 : }
909 :
910 0 : bool FormattedField::ImplGetValue(double& dNewVal)
911 : {
912 :
913 0 : dNewVal = m_dCurrentValue;
914 0 : if (!m_bValueDirty)
915 0 : return true;
916 :
917 0 : dNewVal = m_dDefaultValue;
918 0 : OUString sText(GetText());
919 0 : if (sText.isEmpty())
920 0 : return true;
921 :
922 : DBG_ASSERT(ImplGetFormatter() != NULL, "FormattedField::ImplGetValue : can't give you a current value without a formatter !");
923 :
924 0 : sal_uInt32 nFormatKey = m_nFormatKey; // IsNumberFormat changes the FormatKey!
925 :
926 0 : if (ImplGetFormatter()->IsTextFormat(nFormatKey) && m_bTreatAsNumber)
927 : // for detection of values like "1,1" in fields that are formated as text
928 0 : nFormatKey = 0;
929 :
930 : // special treatment for percentage formatting
931 0 : if (ImplGetFormatter()->GetType(m_nFormatKey) == NUMBERFORMAT_PERCENT)
932 : {
933 : // the language of our format
934 0 : LanguageType eLanguage = m_pFormatter->GetEntry(m_nFormatKey)->GetLanguage();
935 : // the default number format for this language
936 0 : sal_uLong nStandardNumericFormat = m_pFormatter->GetStandardFormat(NUMBERFORMAT_NUMBER, eLanguage);
937 :
938 0 : sal_uInt32 nTempFormat = nStandardNumericFormat;
939 : double dTemp;
940 0 : if (m_pFormatter->IsNumberFormat(sText, nTempFormat, dTemp) &&
941 0 : NUMBERFORMAT_NUMBER == m_pFormatter->GetType(nTempFormat))
942 : // the string is equivalent to a number formatted one (has no % sign) -> append it
943 0 : sText += "%";
944 : // (with this, a input of '3' becomes '3%', which then by the formatter is translated
945 : // into 0.03. Without this, the formatter would give us the double 3 for an input '3',
946 : // which equals 300 percent.
947 : }
948 0 : if (!ImplGetFormatter()->IsNumberFormat(sText, nFormatKey, dNewVal))
949 0 : return false;
950 :
951 0 : if (m_bHasMin && (dNewVal<m_dMinValue))
952 0 : dNewVal = m_dMinValue;
953 0 : if (m_bHasMax && (dNewVal>m_dMaxValue))
954 0 : dNewVal = m_dMaxValue;
955 0 : return true;
956 : }
957 :
958 0 : void FormattedField::SetValue(double dVal)
959 : {
960 0 : ImplSetValue(dVal, m_bValueDirty);
961 0 : }
962 :
963 0 : double FormattedField::GetValue()
964 : {
965 :
966 0 : if ( !ImplGetValue( m_dCurrentValue ) )
967 : {
968 0 : if ( m_bEnableNaN )
969 0 : ::rtl::math::setNan( &m_dCurrentValue );
970 : else
971 0 : m_dCurrentValue = m_dDefaultValue;
972 : }
973 :
974 0 : m_bValueDirty = false;
975 0 : return m_dCurrentValue;
976 : }
977 :
978 0 : void FormattedField::Up()
979 : {
980 : // setValue handles under- and overflows (min/max) automatically
981 0 : SetValue(GetValue() + m_dSpinSize);
982 0 : SetModifyFlag();
983 0 : Modify();
984 :
985 0 : SpinField::Up();
986 0 : }
987 :
988 0 : void FormattedField::Down()
989 : {
990 0 : SetValue(GetValue() - m_dSpinSize);
991 0 : SetModifyFlag();
992 0 : Modify();
993 :
994 0 : SpinField::Down();
995 0 : }
996 :
997 0 : void FormattedField::First()
998 : {
999 0 : if (m_bHasMin)
1000 : {
1001 0 : SetValue(m_dMinValue);
1002 0 : SetModifyFlag();
1003 0 : Modify();
1004 : }
1005 :
1006 0 : SpinField::First();
1007 0 : }
1008 :
1009 0 : void FormattedField::Last()
1010 : {
1011 0 : if (m_bHasMax)
1012 : {
1013 0 : SetValue(m_dMaxValue);
1014 0 : SetModifyFlag();
1015 0 : Modify();
1016 : }
1017 :
1018 0 : SpinField::Last();
1019 0 : }
1020 :
1021 0 : void FormattedField::UseInputStringForFormatting( bool bUseInputStr /* = true */ )
1022 : {
1023 0 : m_bUseInputStringForFormatting = bUseInputStr;
1024 0 : }
1025 :
1026 0 : bool FormattedField::IsUsingInputStringForFormatting() const
1027 : {
1028 0 : return m_bUseInputStringForFormatting;
1029 : }
1030 :
1031 0 : DoubleNumericField::~DoubleNumericField()
1032 : {
1033 0 : delete m_pNumberValidator;
1034 0 : }
1035 :
1036 0 : void DoubleNumericField::FormatChanged(FORMAT_CHANGE_TYPE nWhat)
1037 : {
1038 0 : ResetConformanceTester();
1039 0 : FormattedField::FormatChanged(nWhat);
1040 0 : }
1041 :
1042 0 : bool DoubleNumericField::CheckText(const OUString& sText) const
1043 : {
1044 : // We'd like to implement this using the NumberFormatter::IsNumberFormat, but unfortunately, this doesn't
1045 : // recognize fragments of numbers (like, for instance "1e", which happens during entering e.g. "1e10")
1046 : // Thus, the roundabout way via a regular expression
1047 0 : return m_pNumberValidator->isValidNumericFragment( sText );
1048 : }
1049 :
1050 0 : void DoubleNumericField::ResetConformanceTester()
1051 : {
1052 : // the thousands and the decimal separator are language dependent
1053 0 : const SvNumberformat* pFormatEntry = ImplGetFormatter()->GetEntry(m_nFormatKey);
1054 :
1055 0 : sal_Unicode cSeparatorThousand = ',';
1056 0 : sal_Unicode cSeparatorDecimal = '.';
1057 0 : if (pFormatEntry)
1058 : {
1059 0 : LocaleDataWrapper aLocaleInfo( LanguageTag( pFormatEntry->GetLanguage()) );
1060 :
1061 0 : OUString sSeparator = aLocaleInfo.getNumThousandSep();
1062 0 : if (!sSeparator.isEmpty())
1063 0 : cSeparatorThousand = sSeparator[0];
1064 :
1065 0 : sSeparator = aLocaleInfo.getNumDecimalSep();
1066 0 : if (!sSeparator.isEmpty())
1067 0 : cSeparatorDecimal = sSeparator[0];
1068 : }
1069 :
1070 0 : delete m_pNumberValidator;
1071 0 : m_pNumberValidator = new validation::NumberValidator( cSeparatorThousand, cSeparatorDecimal );
1072 0 : }
1073 :
1074 0 : DoubleCurrencyField::DoubleCurrencyField(Window* pParent, WinBits nStyle)
1075 : :FormattedField(pParent, nStyle)
1076 0 : ,m_bChangingFormat(false)
1077 : {
1078 0 : m_bPrependCurrSym = false;
1079 :
1080 : // initialize with a system currency format
1081 0 : m_sCurrencySymbol = SvtSysLocale().GetLocaleData().getCurrSymbol();
1082 0 : UpdateCurrencyFormat();
1083 0 : }
1084 :
1085 0 : void DoubleCurrencyField::FormatChanged(FORMAT_CHANGE_TYPE nWhat)
1086 : {
1087 0 : if (m_bChangingFormat)
1088 : {
1089 0 : FormattedField::FormatChanged(nWhat);
1090 0 : return;
1091 : }
1092 :
1093 0 : switch (nWhat)
1094 : {
1095 : case FCT_FORMATTER:
1096 : case FCT_PRECISION:
1097 : case FCT_THOUSANDSSEP:
1098 : // the aspects which changed don't take our currency settings into account (in fact, they most probably
1099 : // destroyed them)
1100 0 : UpdateCurrencyFormat();
1101 0 : break;
1102 : case FCT_KEYONLY:
1103 : OSL_FAIL("DoubleCurrencyField::FormatChanged : somebody modified my key !");
1104 : // We always build our own format from the settings we get via special methods (setCurrencySymbol etc.).
1105 : // Nobody but ourself should modifiy the format key directly !
1106 0 : break;
1107 : }
1108 :
1109 0 : FormattedField::FormatChanged(nWhat);
1110 : }
1111 :
1112 0 : void DoubleCurrencyField::setCurrencySymbol(const OUString& rSymbol)
1113 : {
1114 0 : if (m_sCurrencySymbol == rSymbol)
1115 0 : return;
1116 :
1117 0 : m_sCurrencySymbol = rSymbol;
1118 0 : UpdateCurrencyFormat();
1119 0 : FormatChanged(FCT_CURRENCY_SYMBOL);
1120 : }
1121 :
1122 0 : void DoubleCurrencyField::setPrependCurrSym(bool _bPrepend)
1123 : {
1124 0 : if (m_bPrependCurrSym == _bPrepend)
1125 0 : return;
1126 :
1127 0 : m_bPrependCurrSym = _bPrepend;
1128 0 : UpdateCurrencyFormat();
1129 0 : FormatChanged(FCT_CURRSYM_POSITION);
1130 : }
1131 :
1132 0 : void DoubleCurrencyField::UpdateCurrencyFormat()
1133 : {
1134 : // the old settings
1135 : LanguageType eLanguage;
1136 0 : GetFormat(eLanguage);
1137 0 : bool bThSep = GetThousandsSep();
1138 0 : sal_uInt16 nDigits = GetDecimalDigits();
1139 :
1140 : // build a new format string with the base class' and my own settings
1141 :
1142 : /* Strangely with gcc 4.6.3 this needs a temporary LanguageTag, otherwise
1143 : * there's
1144 : * error: request for member 'getNumThousandSep' in 'aLocaleInfo', which is
1145 : * of non-class type 'LocaleDataWrapper(LanguageTag)' */
1146 0 : LanguageTag aLanguageTag( eLanguage);
1147 0 : LocaleDataWrapper aLocaleInfo( aLanguageTag );
1148 :
1149 0 : OUStringBuffer sNewFormat;
1150 0 : if (bThSep)
1151 : {
1152 0 : sNewFormat.append('#');
1153 0 : sNewFormat.append(aLocaleInfo.getNumThousandSep());
1154 0 : sNewFormat.append("##0");
1155 : }
1156 : else
1157 0 : sNewFormat.append('0');
1158 :
1159 0 : if (nDigits)
1160 : {
1161 0 : sNewFormat.append(aLocaleInfo.getNumDecimalSep());
1162 :
1163 0 : OUStringBuffer sTemp;
1164 0 : comphelper::string::padToLength(sTemp, nDigits, '0');
1165 0 : sNewFormat.append(sTemp);
1166 : }
1167 :
1168 0 : if (getPrependCurrSym())
1169 : {
1170 0 : OUString sSymbol = getCurrencySymbol();
1171 0 : sSymbol = comphelper::string::stripStart(sSymbol, ' ');
1172 0 : sSymbol = comphelper::string::stripEnd(sSymbol, ' ');
1173 :
1174 0 : OUStringBuffer sTemp("[$");
1175 0 : sTemp.append(sSymbol);
1176 0 : sTemp.append("] ");
1177 0 : sTemp.append(sNewFormat);
1178 :
1179 : // for negative values : $ -0.00, not -$ 0.00 ...
1180 : // (the real solution would be a possibility to choose a "positive currency format" and a "negative currency format" ...
1181 : // But not now ... (and hey, you could take a formatted field for this ....))
1182 : // FS - 31.03.00 74642
1183 0 : sTemp.append(";[$");
1184 0 : sTemp.append(sSymbol);
1185 0 : sTemp.append("] -");
1186 0 : sTemp.append(sNewFormat);
1187 :
1188 0 : sNewFormat = sTemp;
1189 : }
1190 : else
1191 : {
1192 0 : OUString sTemp = getCurrencySymbol();
1193 0 : sTemp = comphelper::string::stripStart(sTemp, ' ');
1194 0 : sTemp = comphelper::string::stripEnd(sTemp, ' ');
1195 :
1196 0 : sNewFormat.append(" [$");
1197 0 : sNewFormat.append(sTemp);
1198 0 : sNewFormat.append(']');
1199 : }
1200 :
1201 : // set this new basic format
1202 0 : m_bChangingFormat = true;
1203 0 : SetFormat(sNewFormat.makeStringAndClear(), eLanguage);
1204 0 : m_bChangingFormat = false;
1205 0 : }
1206 :
1207 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|