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 "sal/config.h"
21 :
22 : #include <comphelper/processfactory.hxx>
23 : #include <comphelper/string.hxx>
24 :
25 : #include "tools/debug.hxx"
26 :
27 : #include "tools/rc.h"
28 :
29 : #include "vcl/dialog.hxx"
30 : #include "vcl/field.hxx"
31 : #include "vcl/event.hxx"
32 : #include "vcl/svapp.hxx"
33 :
34 : #include "svids.hrc"
35 : #include "svdata.hxx"
36 :
37 : #include "i18nutil/unicode.hxx"
38 :
39 : #include "rtl/math.hxx"
40 :
41 : #include <unotools/localedatawrapper.hxx>
42 :
43 : using namespace ::com::sun::star;
44 : using namespace ::comphelper;
45 :
46 : // -----------------------------------------------------------------------
47 :
48 : #define FORMAT_NUMERIC 1
49 : #define FORMAT_METRIC 2
50 : #define FORMAT_CURRENCY 3
51 :
52 : // -----------------------------------------------------------------------
53 :
54 0 : static sal_Int64 ImplPower10( sal_uInt16 n )
55 : {
56 : sal_uInt16 i;
57 0 : sal_Int64 nValue = 1;
58 :
59 0 : for ( i=0; i < n; i++ )
60 0 : nValue *= 10;
61 :
62 0 : return nValue;
63 : }
64 :
65 : // -----------------------------------------------------------------------
66 :
67 0 : static sal_Bool ImplNumericProcessKeyInput( Edit*, const KeyEvent& rKEvt,
68 : sal_Bool bStrictFormat, sal_Bool bThousandSep,
69 : const LocaleDataWrapper& rLocaleDataWrappper )
70 : {
71 0 : if ( !bStrictFormat )
72 0 : return sal_False;
73 : else
74 : {
75 0 : sal_Unicode cChar = rKEvt.GetCharCode();
76 0 : sal_uInt16 nGroup = rKEvt.GetKeyCode().GetGroup();
77 :
78 0 : if ( (nGroup == KEYGROUP_FKEYS) || (nGroup == KEYGROUP_CURSOR) ||
79 : (nGroup == KEYGROUP_MISC) ||
80 : ((cChar >= '0') && (cChar <= '9')) ||
81 0 : string::equals(rLocaleDataWrappper.getNumDecimalSep(), cChar) ||
82 0 : (bThousandSep && string::equals(rLocaleDataWrappper.getNumThousandSep(), cChar)) ||
83 : (cChar == '-') )
84 0 : return sal_False;
85 : else
86 0 : return sal_True;
87 : }
88 : }
89 :
90 : // -----------------------------------------------------------------------
91 :
92 0 : static sal_Bool ImplNumericGetValue( const XubString& rStr, double& rValue,
93 : sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrappper,
94 : sal_Bool bCurrency = sal_False )
95 : {
96 0 : XubString aStr = rStr;
97 0 : XubString aStr1;
98 0 : rtl::OUStringBuffer aStr2;
99 0 : sal_Bool bNegative = sal_False;
100 : xub_StrLen nDecPos;
101 :
102 : // react on empty string
103 0 : if ( !rStr.Len() )
104 0 : return sal_False;
105 :
106 : // remove leading and trailing spaces
107 0 : aStr = string::strip(aStr, ' ');
108 :
109 : // find position of decimal point
110 0 : nDecPos = aStr.Search( rLocaleDataWrappper.getNumDecimalSep() );
111 0 : if ( nDecPos != STRING_NOTFOUND )
112 : {
113 0 : aStr1 = aStr.Copy( 0, nDecPos );
114 0 : aStr2.append(aStr.Copy(nDecPos+1));
115 : }
116 : else
117 0 : aStr1 = aStr;
118 :
119 : // negative?
120 0 : if ( bCurrency )
121 : {
122 0 : if ( (aStr.GetChar( 0 ) == '(') && (aStr.GetChar( aStr.Len()-1 ) == ')') )
123 0 : bNegative = sal_True;
124 0 : if ( !bNegative )
125 : {
126 0 : for (xub_StrLen i=0; i < aStr.Len(); i++ )
127 : {
128 0 : if ( (aStr.GetChar( i ) >= '0') && (aStr.GetChar( i ) <= '9') )
129 0 : break;
130 0 : else if ( aStr.GetChar( i ) == '-' )
131 : {
132 0 : bNegative = sal_True;
133 0 : break;
134 : }
135 : }
136 : }
137 0 : if ( !bNegative && bCurrency && aStr.Len() )
138 : {
139 0 : sal_uInt16 nFormat = rLocaleDataWrappper.getCurrNegativeFormat();
140 0 : if ( (nFormat == 3) || (nFormat == 6) ||
141 : (nFormat == 7) || (nFormat == 10) )
142 : {
143 0 : for (xub_StrLen i = (xub_StrLen)(aStr.Len()-1); i > 0; i++ )
144 : {
145 0 : if ( (aStr.GetChar( i ) >= '0') && (aStr.GetChar( i ) <= '9') )
146 0 : break;
147 0 : else if ( aStr.GetChar( i ) == '-' )
148 : {
149 0 : bNegative = sal_True;
150 0 : break;
151 : }
152 : }
153 : }
154 : }
155 : }
156 : else
157 : {
158 0 : if ( aStr1.GetChar( 0 ) == '-' )
159 0 : bNegative = sal_True;
160 : }
161 :
162 : // remove all unwanted charaters
163 0 : for (xub_StrLen i=0; i < aStr1.Len(); )
164 : {
165 0 : if ( (aStr1.GetChar( i ) >= '0') && (aStr1.GetChar( i ) <= '9') )
166 0 : i++;
167 : else
168 0 : aStr1.Erase( i, 1 );
169 : }
170 0 : for (sal_Int32 i=0; i < aStr2.getLength(); )
171 : {
172 0 : if ((aStr2[i] >= '0') && (aStr2[i] <= '9'))
173 0 : ++i;
174 : else
175 0 : aStr2.remove(i, 1);
176 : }
177 :
178 0 : if ( !aStr1.Len() && !aStr2.getLength() )
179 0 : return sal_False;
180 :
181 0 : if ( !aStr1.Len() )
182 0 : aStr1.Insert( '0' );
183 0 : if ( bNegative )
184 0 : aStr1.Insert( '-', 0 );
185 :
186 : // prune and round fraction
187 0 : bool bRound = false;
188 0 : if (aStr2.getLength() > nDecDigits)
189 : {
190 0 : if (aStr2[nDecDigits] >= '5')
191 0 : bRound = true;
192 0 : string::truncateToLength(aStr2, nDecDigits);
193 : }
194 0 : if (aStr2.getLength() < nDecDigits)
195 0 : string::padToLength(aStr2, nDecDigits, '0');
196 :
197 0 : aStr = aStr1;
198 0 : aStr += aStr2.makeStringAndClear();
199 :
200 : // check range
201 0 : double nValue = rtl::OUString(aStr).toDouble();
202 0 : if (bRound)
203 : {
204 0 : if ( !bNegative )
205 0 : nValue++;
206 : else
207 0 : nValue--;
208 : }
209 :
210 0 : rValue = nValue;
211 :
212 0 : return sal_True;
213 : }
214 :
215 0 : static void ImplUpdateSeparatorString( String& io_rText,
216 : const String& rOldDecSep, const String& rNewDecSep,
217 : const String& rOldThSep, const String& rNewThSep )
218 : {
219 0 : rtl::OUStringBuffer aBuf( io_rText.Len() );
220 0 : xub_StrLen nIndexDec = 0, nIndexTh = 0, nIndex = 0;
221 :
222 0 : const sal_Unicode* pBuffer = io_rText.GetBuffer();
223 0 : while( nIndex != STRING_NOTFOUND )
224 : {
225 0 : nIndexDec = io_rText.Search( rOldDecSep, nIndex );
226 0 : nIndexTh = io_rText.Search( rOldThSep, nIndex );
227 0 : if( (nIndexTh != STRING_NOTFOUND && nIndexDec != STRING_NOTFOUND && nIndexTh < nIndexDec )
228 : || (nIndexTh != STRING_NOTFOUND && nIndexDec == STRING_NOTFOUND)
229 : )
230 : {
231 0 : aBuf.append( pBuffer + nIndex, nIndexTh - nIndex );
232 0 : aBuf.append( rNewThSep );
233 0 : nIndex = nIndexTh + rOldThSep.Len();
234 : }
235 0 : else if( nIndexDec != STRING_NOTFOUND )
236 : {
237 0 : aBuf.append( pBuffer + nIndex, nIndexDec - nIndex );
238 0 : aBuf.append( rNewDecSep );
239 0 : nIndex = nIndexDec + rOldDecSep.Len();
240 : }
241 : else
242 : {
243 0 : aBuf.append( pBuffer + nIndex );
244 0 : nIndex = STRING_NOTFOUND;
245 : }
246 : }
247 :
248 0 : io_rText = aBuf.makeStringAndClear();
249 0 : }
250 :
251 0 : static void ImplUpdateSeparators( const String& rOldDecSep, const String& rNewDecSep,
252 : const String& rOldThSep, const String& rNewThSep,
253 : Edit* pEdit )
254 : {
255 0 : bool bChangeDec = (rOldDecSep != rNewDecSep);
256 0 : bool bChangeTh = (rOldThSep != rNewThSep );
257 :
258 0 : if( bChangeDec || bChangeTh )
259 : {
260 0 : sal_Bool bUpdateMode = pEdit->IsUpdateMode();
261 0 : pEdit->SetUpdateMode( sal_False );
262 0 : String aText = pEdit->GetText();
263 0 : ImplUpdateSeparatorString( aText, rOldDecSep, rNewDecSep, rOldThSep, rNewThSep );
264 0 : pEdit->SetText( aText );
265 :
266 0 : ComboBox* pCombo = dynamic_cast<ComboBox*>(pEdit);
267 0 : if( pCombo )
268 : {
269 : // update box entries
270 0 : sal_uInt16 nEntryCount = pCombo->GetEntryCount();
271 0 : for ( sal_uInt16 i=0; i < nEntryCount; i++ )
272 : {
273 0 : aText = pCombo->GetEntry( i );
274 0 : void* pEntryData = pCombo->GetEntryData( i );
275 0 : ImplUpdateSeparatorString( aText, rOldDecSep, rNewDecSep, rOldThSep, rNewThSep );
276 0 : pCombo->RemoveEntry( i );
277 0 : pCombo->InsertEntry( aText, i );
278 0 : pCombo->SetEntryData( i, pEntryData );
279 : }
280 : }
281 0 : if( bUpdateMode )
282 0 : pEdit->SetUpdateMode( bUpdateMode );
283 : }
284 0 : }
285 :
286 : // -----------------------------------------------------------------------
287 :
288 0 : FormatterBase::FormatterBase( Edit* pField )
289 : {
290 0 : mpField = pField;
291 0 : mpLocaleDataWrapper = NULL;
292 0 : mbReformat = sal_False;
293 0 : mbStrictFormat = sal_False;
294 0 : mbEmptyFieldValue = sal_False;
295 0 : mbEmptyFieldValueEnabled = sal_False;
296 0 : mbDefaultLocale = sal_True;
297 0 : }
298 :
299 : // -----------------------------------------------------------------------
300 :
301 0 : FormatterBase::~FormatterBase()
302 : {
303 0 : delete mpLocaleDataWrapper;
304 0 : }
305 :
306 : // -----------------------------------------------------------------------
307 :
308 0 : LocaleDataWrapper& FormatterBase::ImplGetLocaleDataWrapper() const
309 : {
310 0 : if ( !mpLocaleDataWrapper )
311 : {
312 0 : ((FormatterBase*)this)->mpLocaleDataWrapper = new LocaleDataWrapper( GetLanguageTag() );
313 : }
314 0 : return *mpLocaleDataWrapper;
315 : }
316 :
317 0 : const LocaleDataWrapper& FormatterBase::GetLocaleDataWrapper() const
318 : {
319 0 : return ImplGetLocaleDataWrapper();
320 : }
321 :
322 : // -----------------------------------------------------------------------
323 :
324 0 : void FormatterBase::Reformat()
325 : {
326 0 : }
327 :
328 : // -----------------------------------------------------------------------
329 :
330 0 : void FormatterBase::ReformatAll()
331 : {
332 0 : Reformat();
333 0 : };
334 :
335 : // -----------------------------------------------------------------------
336 :
337 0 : void FormatterBase::SetStrictFormat( sal_Bool bStrict )
338 : {
339 0 : if ( bStrict != mbStrictFormat )
340 : {
341 0 : mbStrictFormat = bStrict;
342 0 : if ( mbStrictFormat )
343 0 : ReformatAll();
344 : }
345 0 : }
346 :
347 : // -----------------------------------------------------------------------
348 :
349 0 : void FormatterBase::SetLocale( const lang::Locale& rLocale )
350 : {
351 0 : ImplGetLocaleDataWrapper().setLanguageTag( LanguageTag( rLocale) );
352 0 : mbDefaultLocale = sal_False;
353 0 : ReformatAll();
354 0 : }
355 :
356 : // -----------------------------------------------------------------------
357 :
358 0 : const lang::Locale& FormatterBase::GetLocale() const
359 : {
360 0 : if ( !mpLocaleDataWrapper || mbDefaultLocale )
361 : {
362 0 : if ( mpField )
363 0 : return mpField->GetSettings().GetLanguageTag().getLocale();
364 : else
365 0 : return Application::GetSettings().GetLanguageTag().getLocale();
366 : }
367 :
368 0 : return mpLocaleDataWrapper->getLanguageTag().getLocale();
369 : }
370 :
371 : // -----------------------------------------------------------------------
372 :
373 0 : const LanguageTag& FormatterBase::GetLanguageTag() const
374 : {
375 0 : if ( !mpLocaleDataWrapper || mbDefaultLocale )
376 : {
377 0 : if ( mpField )
378 0 : return mpField->GetSettings().GetLanguageTag();
379 : else
380 0 : return Application::GetSettings().GetLanguageTag();
381 : }
382 :
383 0 : return mpLocaleDataWrapper->getLanguageTag();
384 : }
385 :
386 : // -----------------------------------------------------------------------
387 :
388 0 : const AllSettings& FormatterBase::GetFieldSettings() const
389 : {
390 0 : if ( mpField )
391 0 : return mpField->GetSettings();
392 : else
393 0 : return Application::GetSettings();
394 : }
395 :
396 : // -----------------------------------------------------------------------
397 :
398 0 : void FormatterBase::ImplSetText( const XubString& rText, Selection* pNewSelection )
399 : {
400 0 : if ( mpField )
401 : {
402 0 : if ( pNewSelection )
403 0 : mpField->SetText( rText, *pNewSelection );
404 : else
405 : {
406 0 : Selection aSel = mpField->GetSelection();
407 0 : aSel.Min() = aSel.Max();
408 0 : mpField->SetText( rText, aSel );
409 : }
410 :
411 0 : MarkToBeReformatted( sal_False );
412 : }
413 0 : }
414 :
415 : // -----------------------------------------------------------------------
416 :
417 0 : void FormatterBase::SetEmptyFieldValue()
418 : {
419 0 : if ( mpField )
420 0 : mpField->SetText( ImplGetSVEmptyStr() );
421 0 : mbEmptyFieldValue = sal_True;
422 0 : }
423 :
424 : // -----------------------------------------------------------------------
425 :
426 0 : sal_Bool FormatterBase::IsEmptyFieldValue() const
427 : {
428 0 : return (!mpField || !mpField->GetText().Len());
429 : }
430 :
431 : // -----------------------------------------------------------------------
432 :
433 0 : sal_Bool NumericFormatter::ImplNumericReformat( const XubString& rStr, double& rValue,
434 : XubString& rOutStr )
435 : {
436 0 : if ( !ImplNumericGetValue( rStr, rValue, GetDecimalDigits(), ImplGetLocaleDataWrapper() ) )
437 0 : return sal_True;
438 : else
439 : {
440 0 : double nTempVal = rValue;
441 : // caution: precision loss in double cast
442 0 : if ( nTempVal > mnMax )
443 0 : nTempVal = (double)mnMax;
444 0 : else if ( nTempVal < mnMin )
445 0 : nTempVal = (double)mnMin;
446 :
447 0 : if ( GetErrorHdl().IsSet() && (rValue != nTempVal) )
448 : {
449 0 : mnCorrectedValue = (sal_Int64)nTempVal;
450 0 : if ( !GetErrorHdl().Call( this ) )
451 : {
452 0 : mnCorrectedValue = 0;
453 0 : return sal_False;
454 : }
455 : else
456 0 : mnCorrectedValue = 0;
457 : }
458 :
459 0 : rOutStr = CreateFieldText( (sal_Int64)nTempVal );
460 0 : return sal_True;
461 : }
462 : }
463 :
464 : // -----------------------------------------------------------------------
465 :
466 0 : void NumericFormatter::ImplInit()
467 : {
468 0 : mnFieldValue = 0;
469 0 : mnLastValue = 0;
470 0 : mnMin = 0;
471 0 : mnMax = 0x7FFFFFFFFFFFFFFFLL;
472 0 : mnCorrectedValue = 0;
473 0 : mnDecimalDigits = 2;
474 0 : mnType = FORMAT_NUMERIC;
475 0 : mbThousandSep = sal_True;
476 0 : mbShowTrailingZeros = sal_True;
477 :
478 : // for fields
479 0 : mnSpinSize = 1;
480 0 : mnFirst = mnMin;
481 0 : mnLast = mnMax;
482 :
483 0 : SetDecimalDigits( 0 );
484 0 : }
485 :
486 : // -----------------------------------------------------------------------
487 :
488 0 : NumericFormatter::NumericFormatter()
489 : {
490 0 : ImplInit();
491 0 : }
492 :
493 : // -----------------------------------------------------------------------
494 :
495 0 : void NumericFormatter::ImplLoadRes( const ResId& rResId )
496 : {
497 0 : ResMgr* pMgr = rResId.GetResMgr();
498 :
499 0 : if( pMgr )
500 : {
501 0 : sal_uLong nMask = pMgr->ReadLong();
502 :
503 0 : if ( NUMERICFORMATTER_MIN & nMask )
504 0 : mnMin = pMgr->ReadLong();
505 :
506 0 : if ( NUMERICFORMATTER_MAX & nMask )
507 0 : mnMax = pMgr->ReadLong();
508 :
509 0 : if ( NUMERICFORMATTER_STRICTFORMAT & nMask )
510 0 : SetStrictFormat( (sal_Bool)pMgr->ReadShort() );
511 :
512 0 : if ( NUMERICFORMATTER_DECIMALDIGITS & nMask )
513 0 : SetDecimalDigits( pMgr->ReadShort() );
514 :
515 0 : if ( NUMERICFORMATTER_VALUE & nMask )
516 : {
517 0 : mnFieldValue = pMgr->ReadLong();
518 0 : if ( mnFieldValue > mnMax )
519 0 : mnFieldValue = mnMax;
520 0 : else if ( mnFieldValue < mnMin )
521 0 : mnFieldValue = mnMin;
522 0 : mnLastValue = mnFieldValue;
523 : }
524 :
525 0 : if ( NUMERICFORMATTER_NOTHOUSANDSEP & nMask )
526 0 : SetUseThousandSep( !(sal_Bool)pMgr->ReadShort() );
527 : }
528 0 : }
529 :
530 : // -----------------------------------------------------------------------
531 :
532 0 : NumericFormatter::~NumericFormatter()
533 : {
534 0 : }
535 :
536 : // -----------------------------------------------------------------------
537 :
538 0 : void NumericFormatter::SetMin( sal_Int64 nNewMin )
539 : {
540 0 : mnMin = nNewMin;
541 0 : if ( !IsEmptyFieldValue() )
542 0 : ReformatAll();
543 0 : }
544 :
545 : // -----------------------------------------------------------------------
546 :
547 0 : void NumericFormatter::SetMax( sal_Int64 nNewMax )
548 : {
549 0 : mnMax = nNewMax;
550 0 : if ( !IsEmptyFieldValue() )
551 0 : ReformatAll();
552 0 : }
553 :
554 : // -----------------------------------------------------------------------
555 :
556 0 : void NumericFormatter::SetUseThousandSep( sal_Bool b )
557 : {
558 0 : mbThousandSep = b;
559 0 : ReformatAll();
560 0 : }
561 :
562 : // -----------------------------------------------------------------------
563 :
564 0 : void NumericFormatter::SetDecimalDigits( sal_uInt16 nDigits )
565 : {
566 0 : mnDecimalDigits = nDigits;
567 0 : ReformatAll();
568 0 : }
569 :
570 : // -----------------------------------------------------------------------
571 :
572 0 : void NumericFormatter::SetShowTrailingZeros( sal_Bool bShowTrailingZeros )
573 : {
574 0 : if ( mbShowTrailingZeros != bShowTrailingZeros )
575 : {
576 0 : mbShowTrailingZeros = bShowTrailingZeros;
577 0 : ReformatAll();
578 : }
579 0 : }
580 :
581 : // -----------------------------------------------------------------------
582 :
583 0 : sal_uInt16 NumericFormatter::GetDecimalDigits() const
584 : {
585 0 : return mnDecimalDigits;
586 : }
587 :
588 : // -----------------------------------------------------------------------
589 :
590 0 : void NumericFormatter::SetValue( sal_Int64 nNewValue )
591 : {
592 0 : SetUserValue( nNewValue );
593 0 : mnFieldValue = mnLastValue;
594 0 : SetEmptyFieldValueData( sal_False );
595 0 : }
596 :
597 : // -----------------------------------------------------------------------
598 :
599 0 : XubString NumericFormatter::CreateFieldText( sal_Int64 nValue ) const
600 : {
601 0 : return ImplGetLocaleDataWrapper().getNum( nValue, GetDecimalDigits(), IsUseThousandSep(), IsShowTrailingZeros() );
602 : }
603 :
604 : // -----------------------------------------------------------------------
605 :
606 0 : void NumericFormatter::ImplSetUserValue( sal_Int64 nNewValue, Selection* pNewSelection )
607 : {
608 0 : if ( nNewValue > mnMax )
609 0 : nNewValue = mnMax;
610 0 : else if ( nNewValue < mnMin )
611 0 : nNewValue = mnMin;
612 0 : mnLastValue = nNewValue;
613 :
614 0 : if ( GetField() )
615 0 : ImplSetText( CreateFieldText( nNewValue ), pNewSelection );
616 0 : }
617 :
618 : // -----------------------------------------------------------------------
619 :
620 0 : void NumericFormatter::SetUserValue( sal_Int64 nNewValue )
621 : {
622 0 : ImplSetUserValue( nNewValue );
623 0 : }
624 :
625 : // -----------------------------------------------------------------------
626 :
627 0 : sal_Int64 NumericFormatter::GetValue() const
628 : {
629 0 : if ( !GetField() )
630 0 : return 0;
631 :
632 : double nTempValue;
633 :
634 0 : if ( ImplNumericGetValue( GetField()->GetText(), nTempValue,
635 0 : GetDecimalDigits(), ImplGetLocaleDataWrapper() ) )
636 : {
637 : // caution: precision loss in double cast
638 0 : if ( nTempValue > mnMax )
639 0 : nTempValue = (double)mnMax;
640 0 : else if ( nTempValue < mnMin )
641 0 : nTempValue = (double)mnMin;
642 0 : return (sal_Int64)nTempValue;
643 : }
644 : else
645 0 : return mnLastValue;
646 : }
647 :
648 : // -----------------------------------------------------------------------
649 :
650 0 : sal_Bool NumericFormatter::IsValueModified() const
651 : {
652 0 : if ( ImplGetEmptyFieldValue() )
653 0 : return !IsEmptyFieldValue();
654 0 : else if ( GetValue() != mnFieldValue )
655 0 : return sal_True;
656 : else
657 0 : return sal_False;
658 : }
659 :
660 : // -----------------------------------------------------------------------
661 :
662 0 : sal_Int64 NumericFormatter::Normalize( sal_Int64 nValue ) const
663 : {
664 0 : return (nValue * ImplPower10( GetDecimalDigits() ) );
665 : }
666 :
667 : // -----------------------------------------------------------------------
668 :
669 0 : sal_Int64 NumericFormatter::Denormalize( sal_Int64 nValue ) const
670 : {
671 0 : sal_Int64 nFactor = ImplPower10( GetDecimalDigits() );
672 0 : if( nValue < 0 )
673 : {
674 0 : sal_Int64 nHalf = nValue < ( SAL_MIN_INT64 + nFactor )? 0 : nFactor/2;
675 0 : return ((nValue-nHalf) / nFactor );
676 : }
677 : else
678 : {
679 0 : sal_Int64 nHalf = nValue > ( SAL_MAX_INT64 - nFactor )? 0 : nFactor/2;
680 0 : return ((nValue+nHalf) / nFactor );
681 : }
682 : }
683 :
684 : // -----------------------------------------------------------------------
685 :
686 0 : void NumericFormatter::Reformat()
687 : {
688 0 : if ( !GetField() )
689 : return;
690 :
691 0 : if ( !GetField()->GetText().Len() && ImplGetEmptyFieldValue() )
692 : return;
693 :
694 0 : XubString aStr;
695 : // caution: precision loss in double cast
696 0 : double nTemp = (double)mnLastValue;
697 0 : sal_Bool bOK = ImplNumericReformat( GetField()->GetText(), nTemp, aStr );
698 0 : mnLastValue = (sal_Int64)nTemp;
699 0 : if ( !bOK )
700 : return;
701 :
702 0 : if ( aStr.Len() )
703 0 : ImplSetText( aStr );
704 : else
705 0 : SetValue( mnLastValue );
706 : }
707 :
708 : // -----------------------------------------------------------------------
709 :
710 0 : void NumericFormatter::FieldUp()
711 : {
712 0 : sal_Int64 nValue = GetValue();
713 0 : nValue += mnSpinSize;
714 0 : if ( nValue > mnMax )
715 0 : nValue = mnMax;
716 :
717 0 : ImplNewFieldValue( nValue );
718 0 : }
719 :
720 : // -----------------------------------------------------------------------
721 :
722 0 : void NumericFormatter::FieldDown()
723 : {
724 0 : sal_Int64 nValue = GetValue();
725 0 : nValue -= mnSpinSize;
726 0 : if ( nValue < mnMin )
727 0 : nValue = mnMin;
728 :
729 0 : ImplNewFieldValue( nValue );
730 0 : }
731 :
732 : // -----------------------------------------------------------------------
733 :
734 0 : void NumericFormatter::FieldFirst()
735 : {
736 0 : ImplNewFieldValue( mnFirst );
737 0 : }
738 :
739 : // -----------------------------------------------------------------------
740 :
741 0 : void NumericFormatter::FieldLast()
742 : {
743 0 : ImplNewFieldValue( mnLast );
744 0 : }
745 :
746 : // -----------------------------------------------------------------------
747 :
748 0 : void NumericFormatter::ImplNewFieldValue( sal_Int64 nNewValue )
749 : {
750 0 : if ( GetField() )
751 : {
752 : // !!! We should check why we do not validate in ImplSetUserValue() if the value was
753 : // changed. This should be done there as well since otherwise the call to Modify would not
754 : // be allowed. Anyway, the paths from ImplNewFieldValue, ImplSetUserValue, and ImplSetText
755 : // should be checked and clearly traced (with comment) in order to find out what happens.
756 :
757 0 : Selection aSelection = GetField()->GetSelection();
758 0 : aSelection.Justify();
759 0 : XubString aText = GetField()->GetText();
760 : // leave it as is if selected until end
761 0 : if ( (xub_StrLen)aSelection.Max() == aText.Len() )
762 : {
763 0 : if ( !aSelection.Len() )
764 0 : aSelection.Min() = SELECTION_MAX;
765 0 : aSelection.Max() = SELECTION_MAX;
766 : }
767 :
768 0 : sal_Int64 nOldLastValue = mnLastValue;
769 0 : ImplSetUserValue( nNewValue, &aSelection );
770 0 : mnLastValue = nOldLastValue;
771 :
772 : // Modify during Edit is only set during KeyInput
773 0 : if ( GetField()->GetText() != aText )
774 : {
775 0 : GetField()->SetModifyFlag();
776 0 : GetField()->Modify();
777 0 : }
778 : }
779 0 : }
780 :
781 : // -----------------------------------------------------------------------
782 :
783 0 : NumericField::NumericField( Window* pParent, WinBits nWinStyle ) :
784 0 : SpinField( pParent, nWinStyle )
785 : {
786 0 : SetField( this );
787 0 : Reformat();
788 0 : }
789 :
790 : // -----------------------------------------------------------------------
791 :
792 0 : NumericField::NumericField( Window* pParent, const ResId& rResId ) :
793 0 : SpinField( WINDOW_NUMERICFIELD )
794 : {
795 0 : rResId.SetRT( RSC_NUMERICFIELD );
796 0 : WinBits nStyle = ImplInitRes( rResId ) ;
797 0 : SpinField::ImplInit( pParent, nStyle );
798 0 : SetField( this );
799 0 : ImplLoadRes( rResId );
800 0 : Reformat();
801 :
802 0 : if ( !(nStyle & WB_HIDE ) )
803 0 : Show();
804 0 : }
805 :
806 0 : bool NumericField::set_property(const rtl::OString &rKey, const rtl::OString &rValue)
807 : {
808 0 : if (rKey.equalsL(RTL_CONSTASCII_STRINGPARAM("digits")))
809 0 : SetDecimalDigits(rValue.toInt32());
810 : else
811 0 : return SpinField::set_property(rKey, rValue);
812 0 : return true;
813 : }
814 :
815 : // -----------------------------------------------------------------------
816 :
817 0 : void NumericField::ImplLoadRes( const ResId& rResId )
818 : {
819 0 : SpinField::ImplLoadRes( rResId );
820 0 : NumericFormatter::ImplLoadRes( ResId( (RSHEADER_TYPE *)GetClassRes(), *rResId.GetResMgr() ) );
821 :
822 0 : sal_uLong nMask = ReadLongRes();
823 :
824 0 : if ( NUMERICFIELD_FIRST & nMask )
825 0 : mnFirst = ReadLongRes();
826 :
827 0 : if ( NUMERICFIELD_LAST & nMask )
828 0 : mnLast = ReadLongRes();
829 :
830 0 : if ( NUMERICFIELD_SPINSIZE & nMask )
831 0 : mnSpinSize = ReadLongRes();
832 0 : }
833 :
834 : // -----------------------------------------------------------------------
835 :
836 0 : NumericField::~NumericField()
837 : {
838 0 : }
839 :
840 : // -----------------------------------------------------------------------
841 :
842 0 : long NumericField::PreNotify( NotifyEvent& rNEvt )
843 : {
844 0 : if ( (rNEvt.GetType() == EVENT_KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
845 : {
846 0 : if ( ImplNumericProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
847 0 : return 1;
848 : }
849 :
850 0 : return SpinField::PreNotify( rNEvt );
851 : }
852 :
853 : // -----------------------------------------------------------------------
854 :
855 0 : long NumericField::Notify( NotifyEvent& rNEvt )
856 : {
857 0 : if ( rNEvt.GetType() == EVENT_GETFOCUS )
858 0 : MarkToBeReformatted( sal_False );
859 0 : else if ( rNEvt.GetType() == EVENT_LOSEFOCUS )
860 : {
861 0 : if ( MustBeReformatted() && (GetText().Len() || !IsEmptyFieldValueEnabled()) )
862 0 : Reformat();
863 : }
864 :
865 0 : return SpinField::Notify( rNEvt );
866 : }
867 :
868 : // -----------------------------------------------------------------------
869 :
870 0 : void NumericField::DataChanged( const DataChangedEvent& rDCEvt )
871 : {
872 0 : SpinField::DataChanged( rDCEvt );
873 :
874 0 : if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS) && (rDCEvt.GetFlags() & SETTINGS_LOCALE) )
875 : {
876 0 : String sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
877 0 : String sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
878 0 : if ( IsDefaultLocale() )
879 0 : ImplGetLocaleDataWrapper().setLanguageTag( GetSettings().GetLanguageTag() );
880 0 : String sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
881 0 : String sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
882 0 : ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
883 0 : ReformatAll();
884 : }
885 0 : }
886 :
887 : // -----------------------------------------------------------------------
888 :
889 0 : void NumericField::Modify()
890 : {
891 0 : MarkToBeReformatted( sal_True );
892 0 : SpinField::Modify();
893 0 : }
894 :
895 : // -----------------------------------------------------------------------
896 :
897 0 : void NumericField::Up()
898 : {
899 0 : FieldUp();
900 0 : SpinField::Up();
901 0 : }
902 :
903 : // -----------------------------------------------------------------------
904 :
905 0 : void NumericField::Down()
906 : {
907 0 : FieldDown();
908 0 : SpinField::Down();
909 0 : }
910 :
911 : // -----------------------------------------------------------------------
912 :
913 0 : void NumericField::First()
914 : {
915 0 : FieldFirst();
916 0 : SpinField::First();
917 0 : }
918 :
919 : // -----------------------------------------------------------------------
920 :
921 0 : void NumericField::Last()
922 : {
923 0 : FieldLast();
924 0 : SpinField::Last();
925 0 : }
926 :
927 : namespace
928 : {
929 0 : Size calcMinimumSize(const SpinField &rSpinField, const NumericFormatter &rFormatter)
930 : {
931 0 : rtl::OUStringBuffer aBuf;
932 : sal_Int32 nTextLen;
933 :
934 0 : nTextLen = rtl::OUString::valueOf(rFormatter.GetMin()).getLength();
935 0 : string::padToLength(aBuf, nTextLen, '9');
936 : Size aMinTextSize = rSpinField.CalcMinimumSizeForText(
937 0 : rFormatter.CreateFieldText(aBuf.makeStringAndClear().toInt64()));
938 :
939 0 : nTextLen = rtl::OUString::valueOf(rFormatter.GetMax()).getLength();
940 0 : string::padToLength(aBuf, nTextLen, '9');
941 : Size aMaxTextSize = rSpinField.CalcMinimumSizeForText(
942 0 : rFormatter.CreateFieldText(aBuf.makeStringAndClear().toInt64()));
943 :
944 0 : Size aRet(std::max(aMinTextSize.Width(), aMaxTextSize.Width()),
945 0 : std::max(aMinTextSize.Height(), aMaxTextSize.Height()));
946 :
947 0 : OUStringBuffer sBuf("999999999");
948 0 : sal_uInt16 nDigits = rFormatter.GetDecimalDigits();
949 0 : if (nDigits)
950 : {
951 0 : sBuf.append('.');
952 0 : string::padToLength(aBuf, aBuf.getLength() + nDigits, '9');
953 : }
954 0 : aMaxTextSize = rSpinField.CalcMinimumSizeForText(sBuf.makeStringAndClear());
955 0 : aRet.Width() = std::min(aRet.Width(), aMaxTextSize.Width());
956 :
957 0 : return aRet;
958 : }
959 : }
960 :
961 0 : Size NumericField::CalcMinimumSize() const
962 : {
963 0 : return calcMinimumSize(*this, *this);
964 : }
965 :
966 : // -----------------------------------------------------------------------
967 :
968 0 : NumericBox::NumericBox( Window* pParent, WinBits nWinStyle ) :
969 0 : ComboBox( pParent, nWinStyle )
970 : {
971 0 : SetField( this );
972 0 : Reformat();
973 0 : }
974 :
975 : // -----------------------------------------------------------------------
976 :
977 0 : NumericBox::NumericBox( Window* pParent, const ResId& rResId ) :
978 0 : ComboBox( WINDOW_NUMERICBOX )
979 : {
980 0 : rResId.SetRT( RSC_NUMERICBOX );
981 0 : WinBits nStyle = ImplInitRes( rResId );
982 0 : ComboBox::ImplInit( pParent, nStyle );
983 0 : SetField( this );
984 0 : ComboBox::ImplLoadRes( rResId );
985 0 : NumericFormatter::ImplLoadRes( ResId( (RSHEADER_TYPE *)GetClassRes(), *rResId.GetResMgr() ) );
986 0 : Reformat();
987 :
988 0 : if ( !(nStyle & WB_HIDE ) )
989 0 : Show();
990 0 : }
991 :
992 : // -----------------------------------------------------------------------
993 :
994 0 : NumericBox::~NumericBox()
995 : {
996 0 : }
997 :
998 : // -----------------------------------------------------------------------
999 :
1000 0 : long NumericBox::PreNotify( NotifyEvent& rNEvt )
1001 : {
1002 0 : if ( (rNEvt.GetType() == EVENT_KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
1003 : {
1004 0 : if ( ImplNumericProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
1005 0 : return 1;
1006 : }
1007 :
1008 0 : return ComboBox::PreNotify( rNEvt );
1009 : }
1010 :
1011 : // -----------------------------------------------------------------------
1012 :
1013 0 : long NumericBox::Notify( NotifyEvent& rNEvt )
1014 : {
1015 0 : if ( rNEvt.GetType() == EVENT_GETFOCUS )
1016 0 : MarkToBeReformatted( sal_False );
1017 0 : else if ( rNEvt.GetType() == EVENT_LOSEFOCUS )
1018 : {
1019 0 : if ( MustBeReformatted() && (GetText().Len() || !IsEmptyFieldValueEnabled()) )
1020 0 : Reformat();
1021 : }
1022 :
1023 0 : return ComboBox::Notify( rNEvt );
1024 : }
1025 :
1026 : // -----------------------------------------------------------------------
1027 :
1028 0 : void NumericBox::DataChanged( const DataChangedEvent& rDCEvt )
1029 : {
1030 0 : ComboBox::DataChanged( rDCEvt );
1031 :
1032 0 : if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS) && (rDCEvt.GetFlags() & SETTINGS_LOCALE) )
1033 : {
1034 0 : String sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1035 0 : String sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1036 0 : if ( IsDefaultLocale() )
1037 0 : ImplGetLocaleDataWrapper().setLanguageTag( GetSettings().GetLanguageTag() );
1038 0 : String sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1039 0 : String sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1040 0 : ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
1041 0 : ReformatAll();
1042 : }
1043 0 : }
1044 :
1045 : // -----------------------------------------------------------------------
1046 :
1047 0 : void NumericBox::Modify()
1048 : {
1049 0 : MarkToBeReformatted( sal_True );
1050 0 : ComboBox::Modify();
1051 0 : }
1052 :
1053 : // -----------------------------------------------------------------------
1054 :
1055 0 : void NumericBox::ReformatAll()
1056 : {
1057 : double nValue;
1058 0 : XubString aStr;
1059 0 : SetUpdateMode( sal_False );
1060 0 : sal_uInt16 nEntryCount = GetEntryCount();
1061 0 : for ( sal_uInt16 i=0; i < nEntryCount; i++ )
1062 : {
1063 0 : ImplNumericReformat( GetEntry( i ), nValue, aStr );
1064 0 : RemoveEntry( i );
1065 0 : InsertEntry( aStr, i );
1066 : }
1067 0 : NumericFormatter::Reformat();
1068 0 : SetUpdateMode( sal_True );
1069 0 : }
1070 :
1071 : // -----------------------------------------------------------------------
1072 :
1073 0 : void NumericBox::InsertValue( sal_Int64 nValue, sal_uInt16 nPos )
1074 : {
1075 0 : ComboBox::InsertEntry( CreateFieldText( nValue ), nPos );
1076 0 : }
1077 :
1078 : // -----------------------------------------------------------------------
1079 :
1080 0 : static sal_Bool ImplMetricProcessKeyInput( Edit* pEdit, const KeyEvent& rKEvt,
1081 : sal_Bool, sal_Bool bUseThousandSep, const LocaleDataWrapper& rWrapper )
1082 : {
1083 : // no meaningfull strict format; therefore allow all characters
1084 0 : return ImplNumericProcessKeyInput( pEdit, rKEvt, sal_False, bUseThousandSep, rWrapper );
1085 : }
1086 :
1087 : // -----------------------------------------------------------------------
1088 :
1089 0 : static rtl::OUString ImplMetricGetUnitText(const rtl::OUString& rStr)
1090 : {
1091 : // fetch unit text
1092 0 : rtl::OUStringBuffer aStr;
1093 0 : for (sal_Int32 i = rStr.getLength()-1; i >= 0; --i)
1094 : {
1095 0 : sal_Unicode c = rStr[i];
1096 0 : if ( (c == '\'') || (c == '\"') || (c == '%' ) || unicode::isAlpha(c) || unicode::isControl(c) )
1097 0 : aStr.insert(0, c);
1098 : else
1099 : {
1100 0 : if (aStr.getLength())
1101 0 : break;
1102 : }
1103 : }
1104 0 : return aStr.makeStringAndClear();
1105 : }
1106 :
1107 : // -----------------------------------------------------------------------
1108 :
1109 : // #104355# support localized mesaurements
1110 :
1111 0 : static const String& ImplMetricToString( FieldUnit rUnit )
1112 : {
1113 0 : FieldUnitStringList* pList = ImplGetFieldUnits();
1114 0 : if( pList )
1115 : {
1116 : // return unit's default string (ie, the first one )
1117 0 : for( FieldUnitStringList::const_iterator it = pList->begin(); it != pList->end(); ++it )
1118 : {
1119 0 : if ( it->second == rUnit )
1120 0 : return it->first;
1121 : }
1122 : }
1123 :
1124 0 : return String::EmptyString();
1125 : }
1126 :
1127 0 : static FieldUnit ImplStringToMetric(const rtl::OUString &rMetricString)
1128 : {
1129 0 : FieldUnitStringList* pList = ImplGetCleanedFieldUnits();
1130 0 : if( pList )
1131 : {
1132 : // return FieldUnit
1133 0 : rtl::OUString aStr(rMetricString.toAsciiLowerCase());
1134 0 : aStr = string::remove(aStr, ' ');
1135 0 : for( FieldUnitStringList::const_iterator it = pList->begin(); it != pList->end(); ++it )
1136 : {
1137 0 : if ( it->first.Equals( aStr ) )
1138 0 : return it->second;
1139 0 : }
1140 : }
1141 :
1142 0 : return FUNIT_NONE;
1143 : }
1144 :
1145 : // -----------------------------------------------------------------------
1146 :
1147 0 : static FieldUnit ImplMetricGetUnit(const rtl::OUString& rStr)
1148 : {
1149 0 : rtl::OUString aStr = ImplMetricGetUnitText( rStr );
1150 0 : return ImplStringToMetric( aStr );
1151 : }
1152 :
1153 : #define K *1000L
1154 : #define M *1000000LL
1155 : #define X *5280L
1156 :
1157 : // twip in km = 254 / 14 400 000 000
1158 : // expressions too big for default size 32 bit need LL to avoid overflow
1159 :
1160 : static const sal_Int64 aImplFactor[FUNIT_LINE+1][FUNIT_LINE+1] =
1161 : { /*
1162 : mm/100 mm cm m km twip point pica inch foot mile char line */
1163 : { 1, 100, 1 K, 100 K, 100 M, 2540, 2540, 2540, 2540,2540*12,2540*12 X , 53340, 396240},
1164 : { 1, 1, 10, 1 K, 1 M, 2540, 2540, 2540, 2540,2540*12,2540*12 X , 5334, 396240},
1165 : { 1, 1, 1, 100, 100 K, 254, 254, 254, 254, 254*12, 254*12 X , 5334, 39624},
1166 : { 1, 1, 1, 1, 1 K, 254, 254, 254, 254, 254*12, 254*12 X , 533400, 39624},
1167 : { 1, 1, 1, 1, 1, 254, 254, 254, 254, 254*12, 254*12 X ,533400 K, 39624},
1168 : { 1440,144 K,144 K,14400 K,14400LL M, 1, 20, 240, 1440,1440*12,1440*12 X , 210, 3120},
1169 : { 72, 7200, 7200, 720 K, 720 M, 1, 1, 12, 72, 72*12, 72*12 X , 210, 156},
1170 : { 6, 600, 600, 60 K, 60 M, 1, 1, 1, 6, 6*12, 6*12 X , 210, 10},
1171 : { 1, 100, 100, 10 K, 10 M, 1, 1, 1, 1, 12, 12 X , 210, 45},
1172 : { 1, 100, 100, 10 K, 10 M, 1, 1, 1, 1, 1, 1 X , 210, 45},
1173 : { 1, 100, 100, 10 K, 10 M, 1, 1, 1, 1, 1, 1 , 210, 45},
1174 : { 144, 1440,14400, 14400, 14400, 1, 20, 240, 1440,1440*12, 1440*12 X, 1, 156 },
1175 : { 720,72000,72000, 7200 K,7200LL M, 20, 10, 13, 11, 11*12, 11*12 X, 105, 1 }
1176 : };
1177 : #undef X
1178 : #undef M
1179 : #undef K
1180 :
1181 : static FieldUnit eDefaultUnit = FUNIT_NONE;
1182 :
1183 0 : FieldUnit MetricField::GetDefaultUnit() { return eDefaultUnit; }
1184 0 : void MetricField::SetDefaultUnit( FieldUnit meUnit ) { eDefaultUnit = meUnit; }
1185 :
1186 0 : static FieldUnit ImplMap2FieldUnit( MapUnit meUnit, long& nDecDigits )
1187 : {
1188 0 : switch( meUnit )
1189 : {
1190 : case MAP_100TH_MM :
1191 0 : nDecDigits -= 2;
1192 0 : return FUNIT_MM;
1193 : case MAP_10TH_MM :
1194 0 : nDecDigits -= 1;
1195 0 : return FUNIT_MM;
1196 : case MAP_MM :
1197 0 : return FUNIT_MM;
1198 : case MAP_CM :
1199 0 : return FUNIT_CM;
1200 : case MAP_1000TH_INCH :
1201 0 : nDecDigits -= 3;
1202 0 : return FUNIT_INCH;
1203 : case MAP_100TH_INCH :
1204 0 : nDecDigits -= 2;
1205 0 : return FUNIT_INCH;
1206 : case MAP_10TH_INCH :
1207 0 : nDecDigits -= 1;
1208 0 : return FUNIT_INCH;
1209 : case MAP_INCH :
1210 0 : return FUNIT_INCH;
1211 : case MAP_POINT :
1212 0 : return FUNIT_POINT;
1213 : case MAP_TWIP :
1214 0 : return FUNIT_TWIP;
1215 : default:
1216 : OSL_FAIL( "default eInUnit" );
1217 0 : break;
1218 : }
1219 0 : return FUNIT_NONE;
1220 : }
1221 :
1222 : // -----------------------------------------------------------------------
1223 :
1224 0 : static double nonValueDoubleToValueDouble( double nValue )
1225 : {
1226 0 : return rtl::math::isFinite( nValue ) ? nValue : 0.0;
1227 : }
1228 :
1229 0 : sal_Int64 MetricField::ConvertValue( sal_Int64 nValue, sal_Int64 mnBaseValue, sal_uInt16 nDecDigits,
1230 : FieldUnit eInUnit, FieldUnit eOutUnit )
1231 : {
1232 : double nDouble = nonValueDoubleToValueDouble( ConvertDoubleValue(
1233 0 : (double)nValue, mnBaseValue, nDecDigits, eInUnit, eOutUnit ) );
1234 :
1235 : // caution: precision loss in double cast
1236 0 : sal_Int64 nLong = static_cast<sal_Int64>( nDouble );
1237 :
1238 0 : if ( nDouble >= (double)SAL_MAX_INT64 )
1239 0 : nLong = SAL_MAX_INT64;
1240 0 : else if ( nDouble <= (double)SAL_MIN_INT64 )
1241 0 : nLong = SAL_MIN_INT64;
1242 :
1243 0 : return nLong;
1244 : }
1245 :
1246 : // -----------------------------------------------------------------------
1247 :
1248 0 : sal_Int64 MetricField::ConvertValue( sal_Int64 nValue, sal_uInt16 nDigits,
1249 : MapUnit eInUnit, FieldUnit eOutUnit )
1250 : {
1251 : return static_cast<sal_Int64>(
1252 : nonValueDoubleToValueDouble(
1253 0 : ConvertDoubleValue( nValue, nDigits, eInUnit, eOutUnit ) ) );
1254 : }
1255 :
1256 : // -----------------------------------------------------------------------
1257 :
1258 142 : double MetricField::ConvertDoubleValue( double nValue, sal_Int64 mnBaseValue, sal_uInt16 nDecDigits,
1259 : FieldUnit eInUnit, FieldUnit eOutUnit )
1260 : {
1261 142 : if ( eInUnit != eOutUnit )
1262 : {
1263 142 : sal_Int64 nMult = 1, nDiv = 1;
1264 :
1265 142 : if ( eInUnit == FUNIT_PERCENT )
1266 : {
1267 0 : if ( (mnBaseValue <= 0) || (nValue <= 0) )
1268 0 : return nValue;
1269 0 : nDiv = 100;
1270 0 : for ( sal_uInt16 i=0; i < nDecDigits; i++ )
1271 0 : nDiv *= 10;
1272 :
1273 0 : nMult = mnBaseValue;
1274 : }
1275 142 : else if ( eOutUnit == FUNIT_PERCENT ||
1276 : eOutUnit == FUNIT_CUSTOM ||
1277 : eOutUnit == FUNIT_NONE ||
1278 : eInUnit == FUNIT_CUSTOM ||
1279 : eInUnit == FUNIT_NONE )
1280 0 : return nValue;
1281 : else
1282 : {
1283 142 : if ( eOutUnit == FUNIT_100TH_MM )
1284 126 : eOutUnit = FUNIT_NONE;
1285 142 : if ( eInUnit == FUNIT_100TH_MM )
1286 16 : eInUnit = FUNIT_NONE;
1287 :
1288 142 : nDiv = aImplFactor[eInUnit][eOutUnit];
1289 142 : nMult = aImplFactor[eOutUnit][eInUnit];
1290 :
1291 : DBG_ASSERT( nMult > 0, "illegal *" );
1292 : DBG_ASSERT( nDiv > 0, "illegal /" );
1293 : }
1294 :
1295 142 : if ( nMult != 1 && nMult > 0 )
1296 142 : nValue *= nMult;
1297 142 : if ( nDiv != 1 && nDiv > 0 )
1298 : {
1299 142 : nValue += ( nValue < 0 ) ? (-nDiv/2) : (nDiv/2);
1300 142 : nValue /= nDiv;
1301 : }
1302 : }
1303 :
1304 142 : return nValue;
1305 : }
1306 :
1307 : // -----------------------------------------------------------------------
1308 :
1309 0 : double MetricField::ConvertDoubleValue( double nValue, sal_uInt16 nDigits,
1310 : MapUnit eInUnit, FieldUnit eOutUnit )
1311 : {
1312 0 : if ( eOutUnit == FUNIT_PERCENT ||
1313 : eOutUnit == FUNIT_CUSTOM ||
1314 : eOutUnit == FUNIT_NONE ||
1315 : eInUnit == MAP_PIXEL ||
1316 : eInUnit == MAP_SYSFONT ||
1317 : eInUnit == MAP_APPFONT ||
1318 : eInUnit == MAP_RELATIVE )
1319 : {
1320 : OSL_FAIL( "invalid parameters" );
1321 0 : return nValue;
1322 : }
1323 :
1324 0 : long nDecDigits = nDigits;
1325 0 : FieldUnit eFieldUnit = ImplMap2FieldUnit( eInUnit, nDecDigits );
1326 :
1327 0 : if ( nDecDigits < 0 )
1328 : {
1329 0 : while ( nDecDigits )
1330 : {
1331 0 : nValue += 5;
1332 0 : nValue /= 10;
1333 0 : nDecDigits++;
1334 : }
1335 : }
1336 : else
1337 : {
1338 0 : while ( nDecDigits )
1339 : {
1340 0 : nValue *= 10;
1341 0 : nDecDigits--;
1342 : }
1343 : }
1344 :
1345 0 : if ( eFieldUnit != eOutUnit )
1346 : {
1347 0 : sal_Int64 nDiv = aImplFactor[eFieldUnit][eOutUnit];
1348 0 : sal_Int64 nMult = aImplFactor[eOutUnit][eFieldUnit];
1349 :
1350 : DBG_ASSERT( nMult > 0, "illegal *" );
1351 : DBG_ASSERT( nDiv > 0, "illegal /" );
1352 :
1353 0 : if ( nMult != 1 && nMult > 0)
1354 0 : nValue *= nMult;
1355 0 : if ( nDiv != 1 && nDiv > 0 )
1356 : {
1357 0 : nValue += (nValue < 0) ? (-nDiv/2) : (nDiv/2);
1358 0 : nValue /= nDiv;
1359 : }
1360 : }
1361 0 : return nValue;
1362 : }
1363 :
1364 : // -----------------------------------------------------------------------
1365 :
1366 0 : double MetricField::ConvertDoubleValue( double nValue, sal_uInt16 nDigits,
1367 : FieldUnit eInUnit, MapUnit eOutUnit )
1368 : {
1369 0 : if ( eInUnit == FUNIT_PERCENT ||
1370 : eInUnit == FUNIT_CUSTOM ||
1371 : eInUnit == FUNIT_NONE ||
1372 : eOutUnit == MAP_PIXEL ||
1373 : eOutUnit == MAP_SYSFONT ||
1374 : eOutUnit == MAP_APPFONT ||
1375 : eOutUnit == MAP_RELATIVE )
1376 : {
1377 : OSL_FAIL( "invalid parameters" );
1378 0 : return nValue;
1379 : }
1380 :
1381 0 : long nDecDigits = nDigits;
1382 0 : FieldUnit eFieldUnit = ImplMap2FieldUnit( eOutUnit, nDecDigits );
1383 :
1384 0 : if ( nDecDigits < 0 )
1385 : {
1386 0 : while ( nDecDigits )
1387 : {
1388 0 : nValue *= 10;
1389 0 : nDecDigits++;
1390 : }
1391 : }
1392 : else
1393 : {
1394 0 : while ( nDecDigits )
1395 : {
1396 0 : nValue /= 10;
1397 0 : nDecDigits--;
1398 : }
1399 : }
1400 :
1401 0 : if ( eFieldUnit != eInUnit )
1402 : {
1403 0 : sal_Int64 nDiv = aImplFactor[eInUnit][eFieldUnit];
1404 0 : sal_Int64 nMult = aImplFactor[eFieldUnit][eInUnit];
1405 :
1406 : DBG_ASSERT( nMult > 0, "illegal *" );
1407 : DBG_ASSERT( nDiv > 0, "illegal /" );
1408 :
1409 0 : if( nMult != 1 && nMult > 0 )
1410 0 : nValue *= nMult;
1411 0 : if( nDiv != 1 && nDiv > 0 )
1412 : {
1413 0 : nValue += (nValue < 0) ? (-nDiv/2) : (nDiv/2);
1414 0 : nValue /= nDiv;
1415 : }
1416 : }
1417 0 : return nValue;
1418 : }
1419 :
1420 : // -----------------------------------------------------------------------
1421 :
1422 0 : static sal_Bool ImplMetricGetValue( const XubString& rStr, double& rValue, sal_Int64 nBaseValue,
1423 : sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrapper, FieldUnit eUnit )
1424 : {
1425 : // Zahlenwert holen
1426 0 : if ( !ImplNumericGetValue( rStr, rValue, nDecDigits, rLocaleDataWrapper ) )
1427 0 : return sal_False;
1428 :
1429 : // Einheit rausfinden
1430 0 : FieldUnit eEntryUnit = ImplMetricGetUnit( rStr );
1431 :
1432 : // Einheiten umrechnen
1433 0 : rValue = MetricField::ConvertDoubleValue( rValue, nBaseValue, nDecDigits, eEntryUnit, eUnit );
1434 :
1435 0 : return sal_True;
1436 : }
1437 :
1438 : // -----------------------------------------------------------------------
1439 :
1440 0 : sal_Bool MetricFormatter::ImplMetricReformat( const XubString& rStr, double& rValue, XubString& rOutStr )
1441 : {
1442 0 : if ( !ImplMetricGetValue( rStr, rValue, mnBaseValue, GetDecimalDigits(), ImplGetLocaleDataWrapper(), meUnit ) )
1443 0 : return sal_True;
1444 : else
1445 : {
1446 0 : double nTempVal = rValue;
1447 : // caution: precision loss in double cast
1448 0 : if ( nTempVal > GetMax() )
1449 0 : nTempVal = (double)GetMax();
1450 0 : else if ( nTempVal < GetMin())
1451 0 : nTempVal = (double)GetMin();
1452 :
1453 0 : if ( GetErrorHdl().IsSet() && (rValue != nTempVal) )
1454 : {
1455 0 : mnCorrectedValue = (sal_Int64)nTempVal;
1456 0 : if ( !GetErrorHdl().Call( this ) )
1457 : {
1458 0 : mnCorrectedValue = 0;
1459 0 : return sal_False;
1460 : }
1461 : else
1462 0 : mnCorrectedValue = 0;
1463 : }
1464 :
1465 0 : rOutStr = CreateFieldText( (sal_Int64)nTempVal );
1466 0 : return sal_True;
1467 : }
1468 : }
1469 :
1470 : // -----------------------------------------------------------------------
1471 :
1472 0 : inline void MetricFormatter::ImplInit()
1473 : {
1474 0 : mnBaseValue = 0;
1475 0 : meUnit = MetricField::GetDefaultUnit();
1476 0 : mnType = FORMAT_METRIC;
1477 0 : }
1478 :
1479 : // -----------------------------------------------------------------------
1480 :
1481 0 : MetricFormatter::MetricFormatter()
1482 : {
1483 0 : ImplInit();
1484 0 : }
1485 :
1486 : // -----------------------------------------------------------------------
1487 :
1488 0 : void MetricFormatter::ImplLoadRes( const ResId& rResId )
1489 : {
1490 0 : NumericFormatter::ImplLoadRes( rResId );
1491 :
1492 0 : ResMgr* pMgr = rResId.GetResMgr();
1493 0 : if( pMgr )
1494 : {
1495 0 : sal_uLong nMask = pMgr->ReadLong();
1496 :
1497 0 : if ( METRICFORMATTER_UNIT & nMask )
1498 0 : meUnit = (FieldUnit)pMgr->ReadLong();
1499 :
1500 0 : if ( METRICFORMATTER_CUSTOMUNITTEXT & nMask )
1501 0 : maCustomUnitText = pMgr->ReadString();
1502 : }
1503 0 : }
1504 :
1505 : // -----------------------------------------------------------------------
1506 :
1507 0 : MetricFormatter::~MetricFormatter()
1508 : {
1509 0 : }
1510 :
1511 : // -----------------------------------------------------------------------
1512 :
1513 0 : void MetricFormatter::SetUnit( FieldUnit eNewUnit )
1514 : {
1515 0 : if ( eNewUnit == FUNIT_100TH_MM )
1516 : {
1517 0 : SetDecimalDigits( GetDecimalDigits() + 2 );
1518 0 : meUnit = FUNIT_MM;
1519 : }
1520 : else
1521 0 : meUnit = eNewUnit;
1522 0 : ReformatAll();
1523 0 : }
1524 :
1525 : // -----------------------------------------------------------------------
1526 :
1527 0 : void MetricFormatter::SetCustomUnitText( const XubString& rStr )
1528 : {
1529 0 : maCustomUnitText = rStr;
1530 0 : ReformatAll();
1531 0 : }
1532 :
1533 : // -----------------------------------------------------------------------
1534 :
1535 0 : void MetricFormatter::SetValue( sal_Int64 nNewValue, FieldUnit eInUnit )
1536 : {
1537 0 : SetUserValue( nNewValue, eInUnit );
1538 0 : mnFieldValue = mnLastValue;
1539 0 : }
1540 :
1541 : // -----------------------------------------------------------------------
1542 :
1543 0 : XubString MetricFormatter::CreateFieldText( sal_Int64 nValue ) const
1544 : {
1545 0 : XubString aStr = NumericFormatter::CreateFieldText( nValue );
1546 :
1547 0 : if( meUnit == FUNIT_CUSTOM )
1548 0 : aStr += maCustomUnitText;
1549 : else
1550 0 : aStr += ImplMetricToString( meUnit );
1551 :
1552 0 : return aStr;
1553 : }
1554 :
1555 : // -----------------------------------------------------------------------
1556 :
1557 0 : void MetricFormatter::SetUserValue( sal_Int64 nNewValue, FieldUnit eInUnit )
1558 : {
1559 : // convert to previously configured units
1560 0 : nNewValue = MetricField::ConvertValue( nNewValue, mnBaseValue, GetDecimalDigits(), eInUnit, meUnit );
1561 0 : NumericFormatter::SetUserValue( nNewValue );
1562 0 : }
1563 :
1564 : // -----------------------------------------------------------------------
1565 :
1566 0 : sal_Int64 MetricFormatter::GetValue( FieldUnit eOutUnit ) const
1567 : {
1568 0 : if ( !GetField() )
1569 0 : return 0;
1570 :
1571 : double nTempValue;
1572 : // caution: precision loss in double cast
1573 0 : if ( !ImplMetricGetValue( GetField()->GetText(), nTempValue, mnBaseValue, GetDecimalDigits(), ImplGetLocaleDataWrapper(), meUnit ) )
1574 0 : nTempValue = (double)mnLastValue;
1575 :
1576 : // caution: precision loss in double cast
1577 0 : if ( nTempValue > mnMax )
1578 0 : nTempValue = (double)mnMax;
1579 0 : else if ( nTempValue < mnMin )
1580 0 : nTempValue = (double)mnMin;
1581 :
1582 : // convert to requested units
1583 0 : return MetricField::ConvertValue( (sal_Int64)nTempValue, mnBaseValue, GetDecimalDigits(), meUnit, eOutUnit );
1584 : }
1585 :
1586 : // -----------------------------------------------------------------------
1587 :
1588 0 : void MetricFormatter::SetValue( sal_Int64 nValue )
1589 : {
1590 : // Implementation not inline, because it is a virtual Function
1591 0 : SetValue( nValue, FUNIT_NONE );
1592 0 : }
1593 :
1594 : // -----------------------------------------------------------------------
1595 :
1596 0 : sal_Int64 MetricFormatter::GetValue() const
1597 : {
1598 : // Implementation not inline, because it is a virtual Function
1599 0 : return GetValue( FUNIT_NONE );
1600 : }
1601 :
1602 : // -----------------------------------------------------------------------
1603 :
1604 0 : void MetricFormatter::SetMin( sal_Int64 nNewMin, FieldUnit eInUnit )
1605 : {
1606 : // convert to requested units
1607 0 : NumericFormatter::SetMin( MetricField::ConvertValue( nNewMin, mnBaseValue, GetDecimalDigits(),
1608 0 : eInUnit, meUnit ) );
1609 0 : }
1610 :
1611 : // -----------------------------------------------------------------------
1612 :
1613 0 : sal_Int64 MetricFormatter::GetMin( FieldUnit eOutUnit ) const
1614 : {
1615 : // convert to requested units
1616 : return MetricField::ConvertValue( NumericFormatter::GetMin(), mnBaseValue,
1617 0 : GetDecimalDigits(), meUnit, eOutUnit );
1618 : }
1619 :
1620 : // -----------------------------------------------------------------------
1621 :
1622 0 : void MetricFormatter::SetMax( sal_Int64 nNewMax, FieldUnit eInUnit )
1623 : {
1624 : // convert to requested units
1625 0 : NumericFormatter::SetMax( MetricField::ConvertValue( nNewMax, mnBaseValue, GetDecimalDigits(),
1626 0 : eInUnit, meUnit ) );
1627 0 : }
1628 :
1629 : // -----------------------------------------------------------------------
1630 :
1631 0 : sal_Int64 MetricFormatter::GetMax( FieldUnit eOutUnit ) const
1632 : {
1633 : // convert to requested units
1634 : return MetricField::ConvertValue( NumericFormatter::GetMax(), mnBaseValue,
1635 0 : GetDecimalDigits(), meUnit, eOutUnit );
1636 : }
1637 :
1638 : // -----------------------------------------------------------------------
1639 :
1640 0 : void MetricFormatter::SetBaseValue( sal_Int64 nNewBase, FieldUnit eInUnit )
1641 : {
1642 0 : mnBaseValue = MetricField::ConvertValue( nNewBase, mnBaseValue, GetDecimalDigits(),
1643 0 : eInUnit, meUnit );
1644 0 : }
1645 :
1646 : // -----------------------------------------------------------------------
1647 :
1648 0 : sal_Int64 MetricFormatter::GetBaseValue( FieldUnit eOutUnit ) const
1649 : {
1650 : // convert to requested units
1651 0 : return MetricField::ConvertValue( mnBaseValue, mnBaseValue, GetDecimalDigits(),
1652 0 : meUnit, eOutUnit );
1653 : }
1654 :
1655 : // -----------------------------------------------------------------------
1656 :
1657 0 : void MetricFormatter::Reformat()
1658 : {
1659 0 : if ( !GetField() )
1660 : return;
1661 :
1662 0 : XubString aText = GetField()->GetText();
1663 0 : if ( meUnit == FUNIT_CUSTOM )
1664 0 : maCurUnitText = ImplMetricGetUnitText( aText );
1665 :
1666 0 : XubString aStr;
1667 : // caution: precision loss in double cast
1668 0 : double nTemp = (double)mnLastValue;
1669 0 : sal_Bool bOK = ImplMetricReformat( aText, nTemp, aStr );
1670 0 : mnLastValue = (sal_Int64)nTemp;
1671 :
1672 0 : if ( !bOK )
1673 : return;
1674 :
1675 0 : if ( aStr.Len() )
1676 : {
1677 0 : ImplSetText( aStr );
1678 0 : if ( meUnit == FUNIT_CUSTOM )
1679 0 : CustomConvert();
1680 : }
1681 : else
1682 0 : SetValue( mnLastValue );
1683 0 : maCurUnitText.Erase();
1684 : }
1685 :
1686 : // -----------------------------------------------------------------------
1687 :
1688 0 : sal_Int64 MetricFormatter::GetCorrectedValue( FieldUnit eOutUnit ) const
1689 : {
1690 : // convert to requested units
1691 0 : return MetricField::ConvertValue( mnCorrectedValue, mnBaseValue, GetDecimalDigits(),
1692 0 : meUnit, eOutUnit );
1693 : }
1694 :
1695 : // -----------------------------------------------------------------------
1696 :
1697 0 : MetricField::MetricField( Window* pParent, WinBits nWinStyle ) :
1698 0 : SpinField( pParent, nWinStyle )
1699 : {
1700 0 : SetField( this );
1701 0 : Reformat();
1702 0 : }
1703 :
1704 0 : MetricField::MetricField( Window* pParent, const ResId& rResId ) :
1705 0 : SpinField( WINDOW_METRICFIELD )
1706 : {
1707 0 : rResId.SetRT( RSC_METRICFIELD );
1708 0 : WinBits nStyle = ImplInitRes( rResId ) ;
1709 0 : SpinField::ImplInit( pParent, nStyle );
1710 0 : SetField( this );
1711 0 : ImplLoadRes( rResId );
1712 :
1713 0 : if ( !(nStyle & WB_HIDE ) )
1714 0 : Show();
1715 0 : }
1716 :
1717 0 : Size MetricField::CalcMinimumSize() const
1718 : {
1719 0 : return calcMinimumSize(*this, *this);
1720 : }
1721 :
1722 0 : bool MetricField::set_property(const rtl::OString &rKey, const rtl::OString &rValue)
1723 : {
1724 0 : if (rKey.equalsL(RTL_CONSTASCII_STRINGPARAM("format")))
1725 : {
1726 0 : maCustomUnitText = rtl::OStringToOUString(rValue, RTL_TEXTENCODING_UTF8);
1727 0 : meUnit = FUNIT_CUSTOM;
1728 : }
1729 0 : else if (rKey.equalsL(RTL_CONSTASCII_STRINGPARAM("digits")))
1730 0 : SetDecimalDigits(rValue.toInt32());
1731 : else
1732 0 : return SpinField::set_property(rKey, rValue);
1733 0 : return true;
1734 : }
1735 :
1736 0 : void MetricField::ImplLoadRes( const ResId& rResId )
1737 : {
1738 0 : SpinField::ImplLoadRes( rResId );
1739 0 : MetricFormatter::ImplLoadRes( ResId( (RSHEADER_TYPE *)GetClassRes(), *rResId.GetResMgr() ) );
1740 :
1741 0 : sal_uLong nMask = ReadLongRes();
1742 :
1743 0 : if ( METRICFIELD_FIRST & nMask )
1744 0 : mnFirst = ReadLongRes();
1745 :
1746 0 : if ( METRICFIELD_LAST & nMask )
1747 0 : mnLast = ReadLongRes();
1748 :
1749 0 : if ( METRICFIELD_SPINSIZE & nMask )
1750 0 : mnSpinSize = ReadLongRes();
1751 :
1752 0 : Reformat();
1753 0 : }
1754 :
1755 : // -----------------------------------------------------------------------
1756 :
1757 0 : MetricField::~MetricField()
1758 : {
1759 0 : }
1760 :
1761 0 : void MetricField::SetUnit( FieldUnit nNewUnit )
1762 : {
1763 0 : sal_Int64 nRawMax = GetMax( nNewUnit );
1764 0 : sal_Int64 nMax = Denormalize( nRawMax );
1765 0 : sal_Int64 nMin = Denormalize( GetMin( nNewUnit ) );
1766 0 : sal_Int64 nFirst = Denormalize( GetFirst( nNewUnit ) );
1767 0 : sal_Int64 nLast = Denormalize( GetLast( nNewUnit ) );
1768 :
1769 0 : MetricFormatter::SetUnit( nNewUnit );
1770 :
1771 0 : SetMax( Normalize( nMax ), nNewUnit );
1772 0 : SetMin( Normalize( nMin ), nNewUnit );
1773 0 : SetFirst( Normalize( nFirst ), nNewUnit );
1774 0 : SetLast( Normalize( nLast ), nNewUnit );
1775 0 : }
1776 :
1777 : // -----------------------------------------------------------------------
1778 :
1779 0 : void MetricField::SetFirst( sal_Int64 nNewFirst, FieldUnit eInUnit )
1780 : {
1781 : // convert
1782 0 : nNewFirst = MetricField::ConvertValue( nNewFirst, mnBaseValue, GetDecimalDigits(),
1783 0 : eInUnit, meUnit );
1784 0 : mnFirst = nNewFirst;
1785 0 : }
1786 :
1787 : // -----------------------------------------------------------------------
1788 :
1789 0 : sal_Int64 MetricField::GetFirst( FieldUnit eOutUnit ) const
1790 : {
1791 : // convert
1792 0 : return MetricField::ConvertValue( mnFirst, mnBaseValue, GetDecimalDigits(),
1793 0 : meUnit, eOutUnit );
1794 : }
1795 :
1796 : // -----------------------------------------------------------------------
1797 :
1798 0 : void MetricField::SetLast( sal_Int64 nNewLast, FieldUnit eInUnit )
1799 : {
1800 : // convert
1801 0 : nNewLast = MetricField::ConvertValue( nNewLast, mnBaseValue, GetDecimalDigits(),
1802 0 : eInUnit, meUnit );
1803 0 : mnLast = nNewLast;
1804 0 : }
1805 :
1806 : // -----------------------------------------------------------------------
1807 :
1808 0 : sal_Int64 MetricField::GetLast( FieldUnit eOutUnit ) const
1809 : {
1810 : // conver
1811 0 : return MetricField::ConvertValue( mnLast, mnBaseValue, GetDecimalDigits(),
1812 0 : meUnit, eOutUnit );
1813 : }
1814 :
1815 : // -----------------------------------------------------------------------
1816 :
1817 0 : long MetricField::PreNotify( NotifyEvent& rNEvt )
1818 : {
1819 0 : if ( (rNEvt.GetType() == EVENT_KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
1820 : {
1821 0 : if ( ImplMetricProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
1822 0 : return 1;
1823 : }
1824 :
1825 0 : return SpinField::PreNotify( rNEvt );
1826 : }
1827 :
1828 : // -----------------------------------------------------------------------
1829 :
1830 0 : long MetricField::Notify( NotifyEvent& rNEvt )
1831 : {
1832 0 : if ( rNEvt.GetType() == EVENT_GETFOCUS )
1833 0 : MarkToBeReformatted( sal_False );
1834 0 : else if ( rNEvt.GetType() == EVENT_LOSEFOCUS )
1835 : {
1836 0 : if ( MustBeReformatted() && (GetText().Len() || !IsEmptyFieldValueEnabled()) )
1837 0 : Reformat();
1838 : }
1839 :
1840 0 : return SpinField::Notify( rNEvt );
1841 : }
1842 :
1843 : // -----------------------------------------------------------------------
1844 :
1845 0 : void MetricField::DataChanged( const DataChangedEvent& rDCEvt )
1846 : {
1847 0 : SpinField::DataChanged( rDCEvt );
1848 :
1849 0 : if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS) && (rDCEvt.GetFlags() & SETTINGS_LOCALE) )
1850 : {
1851 0 : String sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1852 0 : String sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1853 0 : if ( IsDefaultLocale() )
1854 0 : ImplGetLocaleDataWrapper().setLanguageTag( GetSettings().GetLanguageTag() );
1855 0 : String sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1856 0 : String sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1857 0 : ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
1858 0 : ReformatAll();
1859 : }
1860 0 : }
1861 :
1862 : // -----------------------------------------------------------------------
1863 :
1864 0 : void MetricField::Modify()
1865 : {
1866 0 : MarkToBeReformatted( sal_True );
1867 0 : SpinField::Modify();
1868 0 : }
1869 :
1870 : // -----------------------------------------------------------------------
1871 :
1872 0 : void MetricField::Up()
1873 : {
1874 0 : FieldUp();
1875 0 : SpinField::Up();
1876 0 : }
1877 :
1878 : // -----------------------------------------------------------------------
1879 :
1880 0 : void MetricField::Down()
1881 : {
1882 0 : FieldDown();
1883 0 : SpinField::Down();
1884 0 : }
1885 :
1886 : // -----------------------------------------------------------------------
1887 :
1888 0 : void MetricField::First()
1889 : {
1890 0 : FieldFirst();
1891 0 : SpinField::First();
1892 0 : }
1893 :
1894 : // -----------------------------------------------------------------------
1895 :
1896 0 : void MetricField::Last()
1897 : {
1898 0 : FieldLast();
1899 0 : SpinField::Last();
1900 0 : }
1901 :
1902 : // -----------------------------------------------------------------------
1903 :
1904 0 : void MetricField::CustomConvert()
1905 : {
1906 0 : maCustomConvertLink.Call( this );
1907 0 : }
1908 :
1909 : // -----------------------------------------------------------------------
1910 :
1911 0 : MetricBox::MetricBox( Window* pParent, WinBits nWinStyle ) :
1912 0 : ComboBox( pParent, nWinStyle )
1913 : {
1914 0 : SetField( this );
1915 0 : Reformat();
1916 0 : }
1917 :
1918 : // -----------------------------------------------------------------------
1919 :
1920 0 : MetricBox::MetricBox( Window* pParent, const ResId& rResId ) :
1921 0 : ComboBox( WINDOW_METRICBOX )
1922 : {
1923 0 : rResId.SetRT( RSC_METRICBOX );
1924 0 : WinBits nStyle = ImplInitRes( rResId );
1925 0 : ComboBox::ImplInit( pParent, nStyle );
1926 0 : SetField( this );
1927 0 : Reformat();
1928 0 : ComboBox::ImplLoadRes( rResId );
1929 0 : MetricFormatter::ImplLoadRes( ResId( (RSHEADER_TYPE *)GetClassRes(), *rResId.GetResMgr() ) );
1930 :
1931 0 : if ( !(nStyle & WB_HIDE ) )
1932 0 : Show();
1933 0 : }
1934 :
1935 : // -----------------------------------------------------------------------
1936 :
1937 0 : MetricBox::~MetricBox()
1938 : {
1939 0 : }
1940 :
1941 : // -----------------------------------------------------------------------
1942 :
1943 0 : long MetricBox::PreNotify( NotifyEvent& rNEvt )
1944 : {
1945 0 : if ( (rNEvt.GetType() == EVENT_KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
1946 : {
1947 0 : if ( ImplMetricProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
1948 0 : return 1;
1949 : }
1950 :
1951 0 : return ComboBox::PreNotify( rNEvt );
1952 : }
1953 :
1954 : // -----------------------------------------------------------------------
1955 :
1956 0 : long MetricBox::Notify( NotifyEvent& rNEvt )
1957 : {
1958 0 : if ( rNEvt.GetType() == EVENT_GETFOCUS )
1959 0 : MarkToBeReformatted( sal_False );
1960 0 : else if ( rNEvt.GetType() == EVENT_LOSEFOCUS )
1961 : {
1962 0 : if ( MustBeReformatted() && (GetText().Len() || !IsEmptyFieldValueEnabled()) )
1963 0 : Reformat();
1964 : }
1965 :
1966 0 : return ComboBox::Notify( rNEvt );
1967 : }
1968 :
1969 : // -----------------------------------------------------------------------
1970 :
1971 0 : void MetricBox::DataChanged( const DataChangedEvent& rDCEvt )
1972 : {
1973 0 : ComboBox::DataChanged( rDCEvt );
1974 :
1975 0 : if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS) && (rDCEvt.GetFlags() & SETTINGS_LOCALE) )
1976 : {
1977 0 : String sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1978 0 : String sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1979 0 : if ( IsDefaultLocale() )
1980 0 : ImplGetLocaleDataWrapper().setLanguageTag( GetSettings().GetLanguageTag() );
1981 0 : String sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1982 0 : String sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1983 0 : ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
1984 0 : ReformatAll();
1985 : }
1986 0 : }
1987 :
1988 : // -----------------------------------------------------------------------
1989 :
1990 0 : void MetricBox::Modify()
1991 : {
1992 0 : MarkToBeReformatted( sal_True );
1993 0 : ComboBox::Modify();
1994 0 : }
1995 :
1996 : // -----------------------------------------------------------------------
1997 :
1998 0 : void MetricBox::ReformatAll()
1999 : {
2000 : double nValue;
2001 0 : XubString aStr;
2002 0 : SetUpdateMode( sal_False );
2003 0 : sal_uInt16 nEntryCount = GetEntryCount();
2004 0 : for ( sal_uInt16 i=0; i < nEntryCount; i++ )
2005 : {
2006 0 : ImplMetricReformat( GetEntry( i ), nValue, aStr );
2007 0 : RemoveEntry( i );
2008 0 : InsertEntry( aStr, i );
2009 : }
2010 0 : MetricFormatter::Reformat();
2011 0 : SetUpdateMode( sal_True );
2012 0 : }
2013 :
2014 : // -----------------------------------------------------------------------
2015 :
2016 0 : void MetricBox::CustomConvert()
2017 : {
2018 0 : maCustomConvertLink.Call( this );
2019 0 : }
2020 :
2021 : // -----------------------------------------------------------------------
2022 :
2023 0 : void MetricBox::InsertValue( sal_Int64 nValue, FieldUnit eInUnit, sal_uInt16 nPos )
2024 : {
2025 : // convert to previously configured units
2026 0 : nValue = MetricField::ConvertValue( nValue, mnBaseValue, GetDecimalDigits(),
2027 0 : eInUnit, meUnit );
2028 0 : ComboBox::InsertEntry( CreateFieldText( nValue ), nPos );
2029 0 : }
2030 :
2031 : // -----------------------------------------------------------------------
2032 :
2033 0 : sal_Int64 MetricBox::GetValue( sal_uInt16 nPos, FieldUnit eOutUnit ) const
2034 : {
2035 0 : double nValue = 0;
2036 : ImplMetricGetValue( ComboBox::GetEntry( nPos ), nValue, mnBaseValue,
2037 0 : GetDecimalDigits(), ImplGetLocaleDataWrapper(), meUnit );
2038 :
2039 : // convert to previously configured units
2040 0 : sal_Int64 nRetValue = MetricField::ConvertValue( (sal_Int64)nValue, mnBaseValue, GetDecimalDigits(),
2041 0 : meUnit, eOutUnit );
2042 :
2043 0 : return nRetValue;
2044 : }
2045 :
2046 : // -----------------------------------------------------------------------
2047 :
2048 0 : sal_uInt16 MetricBox::GetValuePos( sal_Int64 nValue, FieldUnit eInUnit ) const
2049 : {
2050 : // convert to previously configured units
2051 0 : nValue = MetricField::ConvertValue( nValue, mnBaseValue, GetDecimalDigits(),
2052 0 : eInUnit, meUnit );
2053 0 : return ComboBox::GetEntryPos( CreateFieldText( nValue ) );
2054 : }
2055 :
2056 : // -----------------------------------------------------------------------
2057 :
2058 0 : sal_Int64 MetricBox::GetValue( FieldUnit eOutUnit ) const
2059 : {
2060 : // Implementation not inline, because it is a virtual Function
2061 0 : return MetricFormatter::GetValue( eOutUnit );
2062 : }
2063 :
2064 : // -----------------------------------------------------------------------
2065 :
2066 0 : sal_Int64 MetricBox::GetValue() const
2067 : {
2068 : // Implementation not inline, because it is a virtual Function
2069 0 : return GetValue( FUNIT_NONE );
2070 : }
2071 :
2072 : // -----------------------------------------------------------------------
2073 :
2074 0 : static sal_Bool ImplCurrencyProcessKeyInput( Edit* pEdit, const KeyEvent& rKEvt,
2075 : sal_Bool, sal_Bool bUseThousandSep, const LocaleDataWrapper& rWrapper )
2076 : {
2077 : // no strict format set; therefore allow all characters
2078 0 : return ImplNumericProcessKeyInput( pEdit, rKEvt, sal_False, bUseThousandSep, rWrapper );
2079 : }
2080 :
2081 : // -----------------------------------------------------------------------
2082 :
2083 0 : inline sal_Bool ImplCurrencyGetValue( const XubString& rStr, double& rValue,
2084 : sal_uInt16 nDecDigits, const LocaleDataWrapper& rWrapper )
2085 : {
2086 : // fetch number
2087 0 : return ImplNumericGetValue( rStr, rValue, nDecDigits, rWrapper, sal_True );
2088 : }
2089 :
2090 : // -----------------------------------------------------------------------
2091 :
2092 0 : sal_Bool CurrencyFormatter::ImplCurrencyReformat( const XubString& rStr,
2093 : XubString& rOutStr )
2094 : {
2095 : double nValue;
2096 0 : if ( !ImplNumericGetValue( rStr, nValue, GetDecimalDigits(), ImplGetLocaleDataWrapper(), sal_True ) )
2097 0 : return sal_True;
2098 : else
2099 : {
2100 0 : double nTempVal = nValue;
2101 : // caution: precision loss in double cast
2102 0 : if ( nTempVal > GetMax() )
2103 0 : nTempVal = (double)GetMax();
2104 0 : else if ( nTempVal < GetMin())
2105 0 : nTempVal = (double)GetMin();
2106 :
2107 0 : if ( GetErrorHdl().IsSet() && (nValue != nTempVal) )
2108 : {
2109 0 : mnCorrectedValue = (sal_Int64)nTempVal;
2110 0 : if ( !GetErrorHdl().Call( this ) )
2111 : {
2112 0 : mnCorrectedValue = 0;
2113 0 : return sal_False;
2114 : }
2115 : else
2116 0 : mnCorrectedValue = 0;
2117 : }
2118 :
2119 0 : rOutStr = CreateFieldText( (long)nTempVal );
2120 0 : return sal_True;
2121 : }
2122 : }
2123 :
2124 : // -----------------------------------------------------------------------
2125 :
2126 0 : inline void CurrencyFormatter::ImplInit()
2127 : {
2128 0 : mnType = FORMAT_CURRENCY;
2129 0 : }
2130 :
2131 : // -----------------------------------------------------------------------
2132 :
2133 0 : CurrencyFormatter::CurrencyFormatter()
2134 : {
2135 0 : ImplInit();
2136 0 : }
2137 :
2138 : // -----------------------------------------------------------------------
2139 :
2140 0 : CurrencyFormatter::~CurrencyFormatter()
2141 : {
2142 0 : }
2143 :
2144 : // -----------------------------------------------------------------------
2145 :
2146 0 : String CurrencyFormatter::GetCurrencySymbol() const
2147 : {
2148 0 : return ImplGetLocaleDataWrapper().getCurrSymbol();
2149 : }
2150 :
2151 : // -----------------------------------------------------------------------
2152 :
2153 0 : void CurrencyFormatter::SetValue( sal_Int64 nNewValue )
2154 : {
2155 0 : SetUserValue( nNewValue );
2156 0 : mnFieldValue = mnLastValue;
2157 0 : SetEmptyFieldValueData( sal_False );
2158 0 : }
2159 :
2160 : // -----------------------------------------------------------------------
2161 :
2162 0 : XubString CurrencyFormatter::CreateFieldText( sal_Int64 nValue ) const
2163 : {
2164 0 : return ImplGetLocaleDataWrapper().getCurr( nValue, GetDecimalDigits(), GetCurrencySymbol(), IsUseThousandSep() );
2165 : }
2166 :
2167 : // -----------------------------------------------------------------------
2168 :
2169 0 : sal_Int64 CurrencyFormatter::GetValue() const
2170 : {
2171 0 : if ( !GetField() )
2172 0 : return 0;
2173 :
2174 : double nTempValue;
2175 0 : if ( ImplCurrencyGetValue( GetField()->GetText(), nTempValue, GetDecimalDigits(), ImplGetLocaleDataWrapper() ) )
2176 : {
2177 : // caution: precision loss in double cast
2178 0 : if ( nTempValue > mnMax )
2179 0 : nTempValue = (double)mnMax;
2180 0 : else if ( nTempValue < mnMin )
2181 0 : nTempValue = (double)mnMin;
2182 0 : return (sal_Int64)nTempValue;
2183 : }
2184 : else
2185 0 : return mnLastValue;
2186 : }
2187 :
2188 : // -----------------------------------------------------------------------
2189 :
2190 0 : void CurrencyFormatter::Reformat()
2191 : {
2192 0 : if ( !GetField() )
2193 : return;
2194 :
2195 0 : XubString aStr;
2196 0 : sal_Bool bOK = ImplCurrencyReformat( GetField()->GetText(), aStr );
2197 0 : if ( !bOK )
2198 : return;
2199 :
2200 0 : if ( aStr.Len() )
2201 : {
2202 0 : ImplSetText( aStr );
2203 : // caution: precision loss in double cast
2204 0 : double nTemp = (double)mnLastValue;
2205 0 : ImplCurrencyGetValue( aStr, nTemp, GetDecimalDigits(), ImplGetLocaleDataWrapper() );
2206 0 : mnLastValue = (sal_Int64)nTemp;
2207 : }
2208 : else
2209 0 : SetValue( mnLastValue );
2210 : }
2211 :
2212 : // -----------------------------------------------------------------------
2213 :
2214 0 : CurrencyField::CurrencyField( Window* pParent, WinBits nWinStyle ) :
2215 0 : SpinField( pParent, nWinStyle )
2216 : {
2217 0 : SetField( this );
2218 0 : Reformat();
2219 0 : }
2220 :
2221 : // -----------------------------------------------------------------------
2222 :
2223 0 : CurrencyField::~CurrencyField()
2224 : {
2225 0 : }
2226 :
2227 : // -----------------------------------------------------------------------
2228 :
2229 0 : long CurrencyField::PreNotify( NotifyEvent& rNEvt )
2230 : {
2231 0 : if ( (rNEvt.GetType() == EVENT_KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
2232 : {
2233 0 : if ( ImplCurrencyProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
2234 0 : return 1;
2235 : }
2236 :
2237 0 : return SpinField::PreNotify( rNEvt );
2238 : }
2239 :
2240 : // -----------------------------------------------------------------------
2241 :
2242 0 : long CurrencyField::Notify( NotifyEvent& rNEvt )
2243 : {
2244 0 : if ( rNEvt.GetType() == EVENT_GETFOCUS )
2245 0 : MarkToBeReformatted( sal_False );
2246 0 : else if ( rNEvt.GetType() == EVENT_LOSEFOCUS )
2247 : {
2248 0 : if ( MustBeReformatted() && (GetText().Len() || !IsEmptyFieldValueEnabled()) )
2249 0 : Reformat();
2250 : }
2251 :
2252 0 : return SpinField::Notify( rNEvt );
2253 : }
2254 :
2255 : // -----------------------------------------------------------------------
2256 :
2257 0 : void CurrencyField::DataChanged( const DataChangedEvent& rDCEvt )
2258 : {
2259 0 : SpinField::DataChanged( rDCEvt );
2260 :
2261 0 : if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS) && (rDCEvt.GetFlags() & SETTINGS_LOCALE) )
2262 : {
2263 0 : String sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
2264 0 : String sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
2265 0 : if ( IsDefaultLocale() )
2266 0 : ImplGetLocaleDataWrapper().setLanguageTag( GetSettings().GetLanguageTag() );
2267 0 : String sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
2268 0 : String sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
2269 0 : ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
2270 0 : ReformatAll();
2271 : }
2272 0 : }
2273 :
2274 : // -----------------------------------------------------------------------
2275 :
2276 0 : void CurrencyField::Modify()
2277 : {
2278 0 : MarkToBeReformatted( sal_True );
2279 0 : SpinField::Modify();
2280 0 : }
2281 :
2282 : // -----------------------------------------------------------------------
2283 :
2284 0 : void CurrencyField::Up()
2285 : {
2286 0 : FieldUp();
2287 0 : SpinField::Up();
2288 0 : }
2289 :
2290 : // -----------------------------------------------------------------------
2291 :
2292 0 : void CurrencyField::Down()
2293 : {
2294 0 : FieldDown();
2295 0 : SpinField::Down();
2296 0 : }
2297 :
2298 : // -----------------------------------------------------------------------
2299 :
2300 0 : void CurrencyField::First()
2301 : {
2302 0 : FieldFirst();
2303 0 : SpinField::First();
2304 0 : }
2305 :
2306 : // -----------------------------------------------------------------------
2307 :
2308 0 : void CurrencyField::Last()
2309 : {
2310 0 : FieldLast();
2311 0 : SpinField::Last();
2312 0 : }
2313 :
2314 : // -----------------------------------------------------------------------
2315 :
2316 0 : CurrencyBox::CurrencyBox( Window* pParent, WinBits nWinStyle ) :
2317 0 : ComboBox( pParent, nWinStyle )
2318 : {
2319 0 : SetField( this );
2320 0 : Reformat();
2321 0 : }
2322 :
2323 : // -----------------------------------------------------------------------
2324 :
2325 0 : CurrencyBox::~CurrencyBox()
2326 : {
2327 0 : }
2328 :
2329 : // -----------------------------------------------------------------------
2330 :
2331 0 : long CurrencyBox::PreNotify( NotifyEvent& rNEvt )
2332 : {
2333 0 : if ( (rNEvt.GetType() == EVENT_KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
2334 : {
2335 0 : if ( ImplCurrencyProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
2336 0 : return 1;
2337 : }
2338 :
2339 0 : return ComboBox::PreNotify( rNEvt );
2340 : }
2341 :
2342 : // -----------------------------------------------------------------------
2343 :
2344 0 : long CurrencyBox::Notify( NotifyEvent& rNEvt )
2345 : {
2346 0 : if ( rNEvt.GetType() == EVENT_GETFOCUS )
2347 0 : MarkToBeReformatted( sal_False );
2348 0 : else if ( rNEvt.GetType() == EVENT_LOSEFOCUS )
2349 : {
2350 0 : if ( MustBeReformatted() && (GetText().Len() || !IsEmptyFieldValueEnabled()) )
2351 0 : Reformat();
2352 : }
2353 :
2354 0 : return ComboBox::Notify( rNEvt );
2355 : }
2356 :
2357 : // -----------------------------------------------------------------------
2358 :
2359 0 : void CurrencyBox::DataChanged( const DataChangedEvent& rDCEvt )
2360 : {
2361 0 : ComboBox::DataChanged( rDCEvt );
2362 :
2363 0 : if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS) && (rDCEvt.GetFlags() & SETTINGS_LOCALE) )
2364 : {
2365 0 : String sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
2366 0 : String sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
2367 0 : if ( IsDefaultLocale() )
2368 0 : ImplGetLocaleDataWrapper().setLanguageTag( GetSettings().GetLanguageTag() );
2369 0 : String sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
2370 0 : String sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
2371 0 : ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
2372 0 : ReformatAll();
2373 : }
2374 0 : }
2375 :
2376 : // -----------------------------------------------------------------------
2377 :
2378 0 : void CurrencyBox::Modify()
2379 : {
2380 0 : MarkToBeReformatted( sal_True );
2381 0 : ComboBox::Modify();
2382 0 : }
2383 :
2384 : // -----------------------------------------------------------------------
2385 :
2386 0 : void CurrencyBox::ReformatAll()
2387 : {
2388 0 : XubString aStr;
2389 0 : SetUpdateMode( sal_False );
2390 0 : sal_uInt16 nEntryCount = GetEntryCount();
2391 0 : for ( sal_uInt16 i=0; i < nEntryCount; i++ )
2392 : {
2393 0 : ImplCurrencyReformat( GetEntry( i ), aStr );
2394 0 : RemoveEntry( i );
2395 0 : InsertEntry( aStr, i );
2396 : }
2397 0 : CurrencyFormatter::Reformat();
2398 0 : SetUpdateMode( sal_True );
2399 0 : }
2400 :
2401 : // -----------------------------------------------------------------------
2402 :
2403 0 : sal_Int64 CurrencyBox::GetValue() const
2404 : {
2405 : // Implementation not inline, because it is a virtual Function
2406 0 : return CurrencyFormatter::GetValue();
2407 : }
2408 :
2409 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|