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 148 : 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 592 : static void lcl_insertStopTransition( StateTransitions& _rRow )
92 : {
93 592 : _rRow.insert( Transition( '_', END ) );
94 592 : }
95 :
96 444 : static void lcl_insertStartExponentTransition( StateTransitions& _rRow )
97 : {
98 444 : _rRow.insert( Transition( 'e', EXPONENT_START ) );
99 444 : }
100 :
101 296 : static void lcl_insertSignTransitions( StateTransitions& _rRow, const State eNextState )
102 : {
103 296 : _rRow.insert( Transition( '-', eNextState ) );
104 296 : _rRow.insert( Transition( '+', eNextState ) );
105 296 : }
106 :
107 740 : static void lcl_insertDigitTransitions( StateTransitions& _rRow, const State eNextState )
108 : {
109 8140 : for ( sal_Unicode aChar = '0'; aChar <= '9'; ++aChar )
110 7400 : _rRow.insert( Transition( aChar, eNextState ) );
111 740 : }
112 :
113 296 : static void lcl_insertCommonPreCommaTransitions( StateTransitions& _rRow, const sal_Unicode _cThSep, const sal_Unicode _cDecSep )
114 : {
115 : // digits are allowed
116 296 : lcl_insertDigitTransitions( _rRow, DIGIT_PRE_COMMA );
117 :
118 : // the thousand separator is allowed
119 296 : _rRow.insert( Transition( _cThSep, DIGIT_PRE_COMMA ) );
120 :
121 : // a comma is allowed
122 296 : _rRow.insert( Transition( _cDecSep, DIGIT_POST_COMMA ) );
123 296 : }
124 :
125 148 : NumberValidator::NumberValidator( const sal_Unicode _cThSep, const sal_Unicode _cDecSep )
126 : :m_cThSep( _cThSep )
127 148 : ,m_cDecSep( _cDecSep )
128 : {
129 : // build up our transition table
130 :
131 : // how to procede from START
132 : {
133 148 : StateTransitions& rRow = m_aTransitions[ START ];
134 148 : 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 148 : StateTransitions& rRow = m_aTransitions[ NUM_START ];
141 :
142 : // a sign is allowed
143 148 : lcl_insertSignTransitions( rRow, DIGIT_PRE_COMMA );
144 :
145 : // common transitions for the two pre-comma states
146 148 : 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 148 : lcl_insertStartExponentTransition( rRow );
151 : }
152 :
153 : // how to procede from DIGIT_PRE_COMMA
154 : {
155 148 : StateTransitions& rRow = m_aTransitions[ DIGIT_PRE_COMMA ];
156 :
157 : // common transitions for the two pre-comma states
158 148 : lcl_insertCommonPreCommaTransitions( rRow, m_cThSep, m_cDecSep );
159 :
160 : // the exponent may start here
161 148 : 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 148 : lcl_insertStopTransition( rRow );
166 : }
167 :
168 : // how to procede from DIGIT_POST_COMMA
169 : {
170 148 : StateTransitions& rRow = m_aTransitions[ DIGIT_POST_COMMA ];
171 :
172 : // there might be digits, which would keep the state at DIGIT_POST_COMMA
173 148 : lcl_insertDigitTransitions( rRow, DIGIT_POST_COMMA );
174 :
175 : // the exponent may start here
176 148 : lcl_insertStartExponentTransition( rRow );
177 :
178 : // the string may end here
179 148 : lcl_insertStopTransition( rRow );
180 : }
181 :
182 : // how to procede from EXPONENT_START
183 : {
184 148 : StateTransitions& rRow = m_aTransitions[ EXPONENT_START ];
185 :
186 : // there may be a sign
187 148 : lcl_insertSignTransitions( rRow, EXPONENT_DIGIT );
188 :
189 : // there may be digits
190 148 : lcl_insertDigitTransitions( rRow, EXPONENT_DIGIT );
191 :
192 : // the string may end here
193 148 : lcl_insertStopTransition( rRow );
194 : }
195 :
196 : // how to procede from EXPONENT_DIGIT
197 : {
198 148 : StateTransitions& rRow = m_aTransitions[ EXPONENT_DIGIT ];
199 :
200 : // there may be digits
201 148 : lcl_insertDigitTransitions( rRow, EXPONENT_DIGIT );
202 :
203 : // the string may end here
204 148 : lcl_insertStopTransition( rRow );
205 : }
206 :
207 : // how to procede from END
208 : {
209 148 : /*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 148 : }
214 :
215 56 : bool NumberValidator::implValidateNormalized( const OUString& _rText )
216 : {
217 56 : const sal_Unicode* pCheckPos = _rText.getStr();
218 56 : State eCurrentState = START;
219 :
220 424 : while ( END != eCurrentState )
221 : {
222 : // look up the transition row for the current state
223 328 : 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 328 : if ( m_aTransitions.end() != aRow )
228 : {
229 : // look up the current character in this row
230 328 : StateTransitions::const_iterator aTransition = aRow->second.find( *pCheckPos );
231 328 : if ( aRow->second.end() != aTransition )
232 : {
233 : // there is a valid transition for this character
234 312 : eCurrentState = aTransition->second;
235 312 : ++pCheckPos;
236 312 : continue;
237 : }
238 : }
239 :
240 : // if we're here, there is no valid transition
241 16 : 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 56 : return ( END == eCurrentState );
252 : }
253 :
254 56 : bool NumberValidator::isValidNumericFragment( const OUString& _rText )
255 : {
256 56 : if ( _rText.isEmpty() )
257 : // empty strings are always allowed
258 0 : return true;
259 :
260 : // normalize the string
261 56 : OUString sNormalized( "_" );
262 56 : sNormalized += _rText;
263 56 : sNormalized += "_";
264 :
265 56 : return implValidateNormalized( sNormalized );
266 : }
267 : }
268 :
269 : SvNumberFormatter* FormattedField::StaticFormatter::s_cFormatter = NULL;
270 : sal_uLong FormattedField::StaticFormatter::s_nReferences = 0;
271 :
272 70 : SvNumberFormatter* FormattedField::StaticFormatter::GetFormatter()
273 : {
274 70 : if (!s_cFormatter)
275 : {
276 : // get the Office's locale and translate
277 70 : LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
278 : s_cFormatter = new SvNumberFormatter(
279 : ::comphelper::getProcessComponentContext(),
280 70 : eSysLanguage);
281 : }
282 70 : return s_cFormatter;
283 : }
284 :
285 126 : FormattedField::StaticFormatter::StaticFormatter()
286 : {
287 126 : ++s_nReferences;
288 126 : }
289 :
290 126 : FormattedField::StaticFormatter::~StaticFormatter()
291 : {
292 126 : if (--s_nReferences == 0)
293 : {
294 72 : delete s_cFormatter;
295 72 : s_cFormatter = NULL;
296 : }
297 126 : }
298 :
299 :
300 126 : FormattedField::FormattedField(vcl::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 126 : ,m_bUseInputStringForFormatting(false)
322 : {
323 :
324 126 : if (pInitialFormatter)
325 : {
326 0 : m_pFormatter = pInitialFormatter;
327 0 : m_nFormatKey = nFormatKey;
328 : }
329 126 : }
330 :
331 0 : extern "C" SAL_DLLPUBLIC_EXPORT vcl::Window* SAL_CALL makeFormattedField(vcl::Window *pParent, VclBuilder::stringmap &)
332 : {
333 0 : WinBits nWinBits = WB_BORDER | WB_SPIN;
334 0 : return new FormattedField(pParent, nWinBits);
335 : }
336 :
337 184 : FormattedField::~FormattedField()
338 : {
339 184 : }
340 :
341 156 : void FormattedField::SetText(const OUString& rStr)
342 : {
343 :
344 156 : SpinField::SetText(rStr);
345 156 : m_bValueDirty = true;
346 156 : }
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 34 : 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 34 : m_sCurrentTextValue = rStr;
364 :
365 34 : OUString sFormatted;
366 34 : double dNumber = 0.0;
367 : // IsNumberFormat changes the format key parameter
368 34 : sal_uInt32 nTempFormatKey = static_cast< sal_uInt32 >( m_nFormatKey );
369 34 : 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 34 : &m_pLastOutputColor);
380 : }
381 :
382 : // calculate the new selection
383 34 : Selection aSel(GetSelection());
384 34 : Selection aNewSel(aSel);
385 34 : aNewSel.Justify();
386 34 : sal_Int32 nNewLen = sFormatted.getLength();
387 34 : sal_Int32 nCurrentLen = GetText().getLength();
388 34 : 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 6 : if (aNewSel.Min() == 0)
391 : { // the whole text was selected -> select the new text on the whole, too
392 6 : aNewSel.Max() = nNewLen;
393 6 : 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 6 : sal_uLong nSelOptions = GetSettings().GetStyleSettings().GetSelectionOptions();
396 6 : if (nSelOptions & SELECTION_OPTION_SHOWFIRST)
397 : { // selection should be from right to left -> swap min and max
398 2 : aNewSel.Min() = aNewSel.Max();
399 2 : 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 28 : else if (aNewSel.Max() > nNewLen)
410 0 : aNewSel.Max() = nNewLen;
411 : else
412 28 : aNewSel = aSel; // don't use the justified version
413 34 : SpinField::SetText(sFormatted, aNewSel);
414 34 : m_bValueDirty = false;
415 34 : }
416 :
417 2 : OUString FormattedField::GetTextValue() const
418 : {
419 2 : if (m_bValueDirty)
420 : {
421 0 : ((FormattedField*)this)->m_sCurrentTextValue = GetText();
422 0 : ((FormattedField*)this)->m_bValueDirty = false;
423 : }
424 2 : return m_sCurrentTextValue;
425 : }
426 :
427 106 : void FormattedField::EnableNotANumber( bool _bEnable )
428 : {
429 106 : if ( m_bEnableNaN == _bEnable )
430 160 : return;
431 :
432 52 : m_bEnableNaN = _bEnable;
433 : }
434 :
435 140 : void FormattedField::SetAutoColor(bool _bAutomatic)
436 : {
437 140 : if (_bAutomatic == m_bAutoColor)
438 210 : return;
439 :
440 70 : m_bAutoColor = _bAutomatic;
441 70 : if (m_bAutoColor)
442 : { // if auto color is switched on, adjust the current text color, too
443 70 : if (m_pLastOutputColor)
444 0 : SetControlForeground(*m_pLastOutputColor);
445 : else
446 70 : SetControlForeground();
447 : }
448 : }
449 :
450 138 : void FormattedField::impl_Modify(bool makeValueDirty)
451 : {
452 :
453 138 : if (!IsStrictFormat())
454 : {
455 0 : if(makeValueDirty)
456 0 : m_bValueDirty = true;
457 0 : SpinField::Modify();
458 138 : return;
459 : }
460 :
461 138 : OUString sCheck = GetText();
462 138 : if (CheckText(sCheck))
463 : {
464 122 : m_sLastValidText = sCheck;
465 122 : m_aLastSelection = GetSelection();
466 122 : if(makeValueDirty)
467 122 : m_bValueDirty = true;
468 : }
469 : else
470 : {
471 16 : ImplSetTextImpl(m_sLastValidText, &m_aLastSelection);
472 : }
473 :
474 138 : SpinField::Modify();
475 : }
476 :
477 138 : void FormattedField::Modify()
478 : {
479 :
480 138 : impl_Modify();
481 138 : }
482 :
483 164 : void FormattedField::ImplSetTextImpl(const OUString& rNew, Selection* pNewSel)
484 : {
485 :
486 164 : if (m_bAutoColor)
487 : {
488 164 : if (m_pLastOutputColor)
489 0 : SetControlForeground(*m_pLastOutputColor);
490 : else
491 164 : SetControlForeground();
492 : }
493 :
494 164 : if (pNewSel)
495 16 : SpinField::SetText(rNew, *pNewSel);
496 : else
497 : {
498 148 : Selection aSel(GetSelection());
499 148 : aSel.Justify();
500 :
501 148 : sal_Int32 nNewLen = rNew.getLength();
502 148 : sal_Int32 nCurrentLen = GetText().getLength();
503 :
504 148 : if ((nNewLen > nCurrentLen) && (aSel.Max() == nCurrentLen))
505 : { // new new text is longer and the cursor is behind the last char
506 50 : if (aSel.Min() == 0)
507 : { // the whole text was selected -> select the new text on the whole, too
508 46 : aSel.Max() = nNewLen;
509 46 : 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 22 : sal_uLong nSelOptions = GetSettings().GetStyleSettings().GetSelectionOptions();
512 22 : 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 4 : 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 98 : else if (aSel.Max() > nNewLen)
526 8 : aSel.Max() = nNewLen;
527 148 : SpinField::SetText(rNew, aSel);
528 : }
529 :
530 164 : m_bValueDirty = true; // not always necessary, but better re-evaluate for safety reasons
531 164 : }
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 410 : void FormattedField::ImplSetFormatKey(sal_uLong nFormatKey)
541 : {
542 :
543 410 : m_nFormatKey = nFormatKey;
544 410 : bool bNeedFormatter = (m_pFormatter == NULL) && (nFormatKey != 0);
545 410 : 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 410 : }
558 :
559 248 : void FormattedField::SetFormatKey(sal_uLong nFormatKey)
560 : {
561 248 : bool bNoFormatter = (m_pFormatter == NULL);
562 248 : ImplSetFormatKey(nFormatKey);
563 248 : FormatChanged((bNoFormatter && (m_pFormatter != NULL)) ? FCT_FORMATTER : FCT_KEYONLY);
564 248 : }
565 :
566 128 : void FormattedField::SetFormatter(SvNumberFormatter* pFormatter, bool bResetFormat)
567 : {
568 :
569 128 : if (bResetFormat)
570 : {
571 126 : m_pFormatter = pFormatter;
572 :
573 : // calc the default format key from the Office's UI locale
574 126 : if ( m_pFormatter )
575 : {
576 : // get the Office's locale and translate
577 126 : LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
578 : // get the standard numeric format for this language
579 126 : m_nFormatKey = m_pFormatter->GetStandardFormat( NUMBERFORMAT_NUMBER, eSysLanguage );
580 : }
581 : else
582 0 : m_nFormatKey = 0;
583 : }
584 : else
585 : {
586 : LanguageType aOldLang;
587 2 : OUString sOldFormat = GetFormat(aOldLang);
588 :
589 2 : sal_uInt32 nDestKey = pFormatter->TestNewString(sOldFormat);
590 2 : 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 2 : m_pFormatter = pFormatter;
603 : }
604 :
605 128 : FormatChanged(FCT_FORMATTER);
606 128 : }
607 :
608 388 : OUString FormattedField::GetFormat(LanguageType& eLang) const
609 : {
610 388 : const SvNumberformat* pFormatEntry = ImplGetFormatter()->GetEntry(m_nFormatKey);
611 : DBG_ASSERT(pFormatEntry != NULL, "FormattedField::GetFormat: no number format for the given format key.");
612 388 : OUString sFormatString = pFormatEntry ? pFormatEntry->GetFormatstring() : OUString();
613 388 : eLang = pFormatEntry ? pFormatEntry->GetLanguage() : LANGUAGE_DONTKNOW;
614 :
615 388 : return sFormatString;
616 : }
617 :
618 224 : bool FormattedField::SetFormat(const OUString& rFormatString, LanguageType eLang)
619 : {
620 224 : sal_uInt32 nNewKey = ImplGetFormatter()->TestNewString(rFormatString, eLang);
621 224 : if (nNewKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
622 : {
623 : sal_Int32 nCheckPos;
624 : short nType;
625 189 : OUString rFormat(rFormatString);
626 189 : if (!ImplGetFormatter()->PutEntry(rFormat, nCheckPos, nType, nNewKey, eLang))
627 0 : return false;
628 189 : DBG_ASSERT(nNewKey != NUMBERFORMAT_ENTRY_NOT_FOUND, "FormattedField::SetFormatString : PutEntry returned an invalid key !");
629 : }
630 :
631 224 : if (nNewKey != m_nFormatKey)
632 190 : SetFormatKey(nNewKey);
633 224 : return true;
634 : }
635 :
636 224 : 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 224 : ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nAnzLeading);
644 :
645 224 : return bThousand;
646 : }
647 :
648 101 : 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 101 : ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nAnzLeading);
657 101 : if (bThousand == (bool)_bUseSeparator)
658 148 : return;
659 :
660 : // we need the language for the following
661 : LanguageType eLang;
662 54 : GetFormat(eLang);
663 :
664 : // generate a new format ...
665 54 : OUString sFmtDescription = ImplGetFormatter()->GenerateFormat(m_nFormatKey, eLang, _bUseSeparator, IsRed, nPrecision, nAnzLeading);
666 : // ... and introduce it to the formatter
667 54 : sal_Int32 nCheckPos = 0;
668 : sal_uInt32 nNewKey;
669 : short nType;
670 54 : ImplGetFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
671 :
672 : // set the new key
673 54 : ImplSetFormatKey(nNewKey);
674 54 : FormatChanged(FCT_THOUSANDSSEP);
675 : }
676 :
677 224 : 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 224 : ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nAnzLeading);
685 :
686 224 : return nPrecision;
687 : }
688 :
689 108 : 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 108 : ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nAnzLeading);
698 108 : if (nPrecision == _nPrecision)
699 108 : return;
700 :
701 : // we need the language for the following
702 : LanguageType eLang;
703 108 : GetFormat(eLang);
704 :
705 : // generate a new format ...
706 108 : OUString sFmtDescription = ImplGetFormatter()->GenerateFormat(m_nFormatKey, eLang, bThousand, IsRed, _nPrecision, nAnzLeading);
707 : // ... and introduce it to the formatter
708 108 : sal_Int32 nCheckPos = 0;
709 : sal_uInt32 nNewKey;
710 : short nType;
711 108 : ImplGetFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
712 :
713 : // set the new key
714 108 : ImplSetFormatKey(nNewKey);
715 108 : FormatChanged(FCT_PRECISION);
716 : }
717 :
718 612 : void FormattedField::FormatChanged( FORMAT_CHANGE_TYPE _nWhat )
719 : {
720 612 : m_pLastOutputColor = NULL;
721 :
722 612 : if ( ( 0 != ( _nWhat & FCT_FORMATTER ) ) && m_pFormatter )
723 182 : m_pFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_INTL_FORMAT );
724 :
725 612 : ReFormat();
726 612 : }
727 :
728 2 : void FormattedField::Commit()
729 : {
730 : // remember the old text
731 2 : OUString sOld( GetText() );
732 :
733 : // do the reformat
734 2 : ReFormat();
735 :
736 : // did the text change?
737 2 : 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 2 : }
745 2 : }
746 :
747 846 : void FormattedField::ReFormat()
748 : {
749 846 : if (!IsEmptyFieldEnabled() || !GetText().isEmpty())
750 : {
751 96 : if (TreatingAsNumber())
752 : {
753 94 : double dValue = GetValue();
754 94 : if ( m_bEnableNaN && ::rtl::math::isNan( dValue ) )
755 846 : return;
756 94 : ImplSetValue( dValue, true );
757 : }
758 : else
759 2 : SetTextFormatted(GetTextValue());
760 : }
761 : }
762 :
763 50 : bool FormattedField::Notify(NotifyEvent& rNEvt)
764 : {
765 :
766 50 : 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 50 : 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() == CommandWheelMode::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 50 : 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 50 : return SpinField::Notify( rNEvt );
831 : }
832 :
833 116 : void FormattedField::SetMinValue(double dMin)
834 : {
835 : DBG_ASSERT(m_bTreatAsNumber, "FormattedField::SetMinValue : only to be used in numeric mode !");
836 :
837 116 : m_dMinValue = dMin;
838 116 : m_bHasMin = true;
839 : // for checking the current value at the new border -> ImplSetValue
840 116 : ReFormat();
841 116 : }
842 :
843 116 : void FormattedField::SetMaxValue(double dMax)
844 : {
845 : DBG_ASSERT(m_bTreatAsNumber, "FormattedField::SetMaxValue : only to be used in numeric mode !");
846 :
847 116 : m_dMaxValue = dMax;
848 116 : m_bHasMax = true;
849 : // for checking the current value at the new border -> ImplSetValue
850 116 : ReFormat();
851 116 : }
852 :
853 0 : void FormattedField::SetTextValue(const OUString& rText)
854 : {
855 0 : SetText(rText);
856 0 : ReFormat();
857 0 : }
858 :
859 2 : void FormattedField::EnableEmptyField(bool bEnable)
860 : {
861 2 : if (bEnable == m_bEnableEmptyField)
862 4 : return;
863 :
864 0 : m_bEnableEmptyField = bEnable;
865 0 : if (!m_bEnableEmptyField && GetText().isEmpty())
866 0 : ImplSetValue(m_dCurrentValue, true);
867 : }
868 :
869 160 : void FormattedField::ImplSetValue(double dVal, bool bForce)
870 : {
871 :
872 160 : if (m_bHasMin && (dVal<m_dMinValue))
873 0 : dVal = m_dMinValue;
874 160 : if (m_bHasMax && (dVal>m_dMaxValue))
875 12 : dVal = m_dMaxValue;
876 160 : if (!bForce && (dVal == GetValue()))
877 172 : return;
878 :
879 : DBG_ASSERT(ImplGetFormatter() != NULL, "FormattedField::ImplSetValue : can't set a value without a formatter !");
880 :
881 148 : m_bValueDirty = false;
882 148 : m_dCurrentValue = dVal;
883 :
884 148 : OUString sNewText;
885 148 : 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 148 : if( IsUsingInputStringForFormatting())
896 : {
897 0 : ImplGetFormatter()->GetInputLineString(dVal, m_nFormatKey, sNewText);
898 : }
899 : else
900 : {
901 148 : ImplGetFormatter()->GetOutputString(dVal, m_nFormatKey, sNewText, &m_pLastOutputColor);
902 : }
903 : }
904 :
905 148 : ImplSetTextImpl(sNewText, NULL);
906 148 : m_bValueDirty = false;
907 148 : DBG_ASSERT(CheckText(sNewText), "FormattedField::ImplSetValue : formatted string doesn't match the criteria !");
908 : }
909 :
910 582 : bool FormattedField::ImplGetValue(double& dNewVal)
911 : {
912 :
913 582 : dNewVal = m_dCurrentValue;
914 582 : if (!m_bValueDirty)
915 438 : return true;
916 :
917 144 : dNewVal = m_dDefaultValue;
918 144 : OUString sText(GetText());
919 144 : if (sText.isEmpty())
920 10 : return true;
921 :
922 : DBG_ASSERT(ImplGetFormatter() != NULL, "FormattedField::ImplGetValue : can't give you a current value without a formatter !");
923 :
924 134 : sal_uInt32 nFormatKey = m_nFormatKey; // IsNumberFormat changes the FormatKey!
925 :
926 134 : 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 134 : 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 134 : if (!ImplGetFormatter()->IsNumberFormat(sText, nFormatKey, dNewVal))
949 26 : return false;
950 :
951 108 : if (m_bHasMin && (dNewVal<m_dMinValue))
952 0 : dNewVal = m_dMinValue;
953 108 : if (m_bHasMax && (dNewVal>m_dMaxValue))
954 0 : dNewVal = m_dMaxValue;
955 108 : return true;
956 : }
957 :
958 66 : void FormattedField::SetValue(double dVal)
959 : {
960 66 : ImplSetValue(dVal, m_bValueDirty);
961 66 : }
962 :
963 582 : double FormattedField::GetValue()
964 : {
965 :
966 582 : if ( !ImplGetValue( m_dCurrentValue ) )
967 : {
968 26 : if ( m_bEnableNaN )
969 0 : ::rtl::math::setNan( &m_dCurrentValue );
970 : else
971 26 : m_dCurrentValue = m_dDefaultValue;
972 : }
973 :
974 582 : m_bValueDirty = false;
975 582 : return m_dCurrentValue;
976 : }
977 :
978 20 : void FormattedField::Up()
979 : {
980 : // setValue handles under- and overflows (min/max) automatically
981 20 : SetValue(GetValue() + m_dSpinSize);
982 20 : SetModifyFlag();
983 20 : Modify();
984 :
985 20 : SpinField::Up();
986 20 : }
987 :
988 10 : void FormattedField::Down()
989 : {
990 10 : SetValue(GetValue() - m_dSpinSize);
991 10 : SetModifyFlag();
992 10 : Modify();
993 :
994 10 : SpinField::Down();
995 10 : }
996 :
997 10 : void FormattedField::First()
998 : {
999 10 : if (m_bHasMin)
1000 : {
1001 8 : SetValue(m_dMinValue);
1002 8 : SetModifyFlag();
1003 8 : Modify();
1004 : }
1005 :
1006 10 : SpinField::First();
1007 10 : }
1008 :
1009 10 : void FormattedField::Last()
1010 : {
1011 10 : if (m_bHasMax)
1012 : {
1013 8 : SetValue(m_dMaxValue);
1014 8 : SetModifyFlag();
1015 8 : Modify();
1016 : }
1017 :
1018 10 : SpinField::Last();
1019 10 : }
1020 :
1021 0 : void FormattedField::UseInputStringForFormatting( bool bUseInputStr /* = true */ )
1022 : {
1023 0 : m_bUseInputStringForFormatting = bUseInputStr;
1024 0 : }
1025 :
1026 :
1027 102 : DoubleNumericField::~DoubleNumericField()
1028 : {
1029 34 : delete m_pNumberValidator;
1030 68 : }
1031 :
1032 114 : void DoubleNumericField::FormatChanged(FORMAT_CHANGE_TYPE nWhat)
1033 : {
1034 114 : ResetConformanceTester();
1035 114 : FormattedField::FormatChanged(nWhat);
1036 114 : }
1037 :
1038 56 : bool DoubleNumericField::CheckText(const OUString& sText) const
1039 : {
1040 : // We'd like to implement this using the NumberFormatter::IsNumberFormat, but unfortunately, this doesn't
1041 : // recognize fragments of numbers (like, for instance "1e", which happens during entering e.g. "1e10")
1042 : // Thus, the roundabout way via a regular expression
1043 56 : return m_pNumberValidator->isValidNumericFragment( sText );
1044 : }
1045 :
1046 148 : void DoubleNumericField::ResetConformanceTester()
1047 : {
1048 : // the thousands and the decimal separator are language dependent
1049 148 : const SvNumberformat* pFormatEntry = ImplGetFormatter()->GetEntry(m_nFormatKey);
1050 :
1051 148 : sal_Unicode cSeparatorThousand = ',';
1052 148 : sal_Unicode cSeparatorDecimal = '.';
1053 148 : if (pFormatEntry)
1054 : {
1055 148 : LocaleDataWrapper aLocaleInfo( LanguageTag( pFormatEntry->GetLanguage()) );
1056 :
1057 296 : OUString sSeparator = aLocaleInfo.getNumThousandSep();
1058 148 : if (!sSeparator.isEmpty())
1059 148 : cSeparatorThousand = sSeparator[0];
1060 :
1061 148 : sSeparator = aLocaleInfo.getNumDecimalSep();
1062 148 : if (!sSeparator.isEmpty())
1063 296 : cSeparatorDecimal = sSeparator[0];
1064 : }
1065 :
1066 148 : delete m_pNumberValidator;
1067 148 : m_pNumberValidator = new validation::NumberValidator( cSeparatorThousand, cSeparatorDecimal );
1068 148 : }
1069 :
1070 34 : DoubleCurrencyField::DoubleCurrencyField(vcl::Window* pParent, WinBits nStyle)
1071 : :FormattedField(pParent, nStyle)
1072 34 : ,m_bChangingFormat(false)
1073 : {
1074 34 : m_bPrependCurrSym = false;
1075 :
1076 : // initialize with a system currency format
1077 34 : m_sCurrencySymbol = SvtSysLocale().GetLocaleData().getCurrSymbol();
1078 34 : UpdateCurrencyFormat();
1079 34 : }
1080 :
1081 380 : void DoubleCurrencyField::FormatChanged(FORMAT_CHANGE_TYPE nWhat)
1082 : {
1083 380 : if (m_bChangingFormat)
1084 : {
1085 190 : FormattedField::FormatChanged(nWhat);
1086 570 : return;
1087 : }
1088 :
1089 190 : switch (nWhat)
1090 : {
1091 : case FCT_FORMATTER:
1092 : case FCT_PRECISION:
1093 : case FCT_THOUSANDSSEP:
1094 : // the aspects which changed don't take our currency settings into account (in fact, they most probably
1095 : // destroyed them)
1096 116 : UpdateCurrencyFormat();
1097 116 : break;
1098 : case FCT_KEYONLY:
1099 : OSL_FAIL("DoubleCurrencyField::FormatChanged : somebody modified my key !");
1100 : // We always build our own format from the settings we get via special methods (setCurrencySymbol etc.).
1101 : // Nobody but ourself should modifiy the format key directly !
1102 0 : break;
1103 : }
1104 :
1105 190 : FormattedField::FormatChanged(nWhat);
1106 : }
1107 :
1108 50 : void DoubleCurrencyField::setCurrencySymbol(const OUString& rSymbol)
1109 : {
1110 50 : if (m_sCurrencySymbol == rSymbol)
1111 66 : return;
1112 :
1113 34 : m_sCurrencySymbol = rSymbol;
1114 34 : UpdateCurrencyFormat();
1115 34 : FormatChanged(FCT_CURRENCY_SYMBOL);
1116 : }
1117 :
1118 50 : void DoubleCurrencyField::setPrependCurrSym(bool _bPrepend)
1119 : {
1120 50 : if (m_bPrependCurrSym == _bPrepend)
1121 60 : return;
1122 :
1123 40 : m_bPrependCurrSym = _bPrepend;
1124 40 : UpdateCurrencyFormat();
1125 40 : FormatChanged(FCT_CURRSYM_POSITION);
1126 : }
1127 :
1128 224 : void DoubleCurrencyField::UpdateCurrencyFormat()
1129 : {
1130 : // the old settings
1131 : LanguageType eLanguage;
1132 224 : GetFormat(eLanguage);
1133 224 : bool bThSep = GetThousandsSep();
1134 224 : sal_uInt16 nDigits = GetDecimalDigits();
1135 :
1136 : // build a new format string with the base class' and my own settings
1137 :
1138 : /* Strangely with gcc 4.6.3 this needs a temporary LanguageTag, otherwise
1139 : * there's
1140 : * error: request for member 'getNumThousandSep' in 'aLocaleInfo', which is
1141 : * of non-class type 'LocaleDataWrapper(LanguageTag)' */
1142 224 : LanguageTag aLanguageTag( eLanguage);
1143 448 : LocaleDataWrapper aLocaleInfo( aLanguageTag );
1144 :
1145 448 : OUStringBuffer sNewFormat;
1146 224 : if (bThSep)
1147 : {
1148 37 : sNewFormat.append('#');
1149 37 : sNewFormat.append(aLocaleInfo.getNumThousandSep());
1150 37 : sNewFormat.append("##0");
1151 : }
1152 : else
1153 187 : sNewFormat.append('0');
1154 :
1155 224 : if (nDigits)
1156 : {
1157 138 : sNewFormat.append(aLocaleInfo.getNumDecimalSep());
1158 :
1159 138 : OUStringBuffer sTemp;
1160 138 : comphelper::string::padToLength(sTemp, nDigits, '0');
1161 138 : sNewFormat.append(sTemp);
1162 : }
1163 :
1164 224 : if (getPrependCurrSym())
1165 : {
1166 61 : OUString sSymbol = getCurrencySymbol();
1167 61 : sSymbol = comphelper::string::stripStart(sSymbol, ' ');
1168 61 : sSymbol = comphelper::string::stripEnd(sSymbol, ' ');
1169 :
1170 122 : OUStringBuffer sTemp("[$");
1171 61 : sTemp.append(sSymbol);
1172 61 : sTemp.append("] ");
1173 61 : sTemp.append(sNewFormat);
1174 :
1175 : // for negative values : $ -0.00, not -$ 0.00 ...
1176 : // (the real solution would be a possibility to choose a "positive currency format" and a "negative currency format" ...
1177 : // But not now ... (and hey, you could take a formatted field for this ....))
1178 : // FS - 31.03.00 74642
1179 61 : sTemp.append(";[$");
1180 61 : sTemp.append(sSymbol);
1181 61 : sTemp.append("] -");
1182 61 : sTemp.append(sNewFormat);
1183 :
1184 122 : sNewFormat = sTemp;
1185 : }
1186 : else
1187 : {
1188 163 : OUString sTemp = getCurrencySymbol();
1189 163 : sTemp = comphelper::string::stripStart(sTemp, ' ');
1190 163 : sTemp = comphelper::string::stripEnd(sTemp, ' ');
1191 :
1192 163 : sNewFormat.append(" [$");
1193 163 : sNewFormat.append(sTemp);
1194 163 : sNewFormat.append(']');
1195 : }
1196 :
1197 : // set this new basic format
1198 224 : m_bChangingFormat = true;
1199 224 : SetFormat(sNewFormat.makeStringAndClear(), eLanguage);
1200 448 : m_bChangingFormat = false;
1201 1451 : }
1202 :
1203 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|