/src/libreoffice/vcl/source/control/field.cxx
Line | Count | Source |
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 <osl/diagnose.hxx> |
23 | | #include <comphelper/string.hxx> |
24 | | #include <tools/json_writer.hxx> |
25 | | #include <tools/mapunit.hxx> |
26 | | #include <unotools/localedatawrapper.hxx> |
27 | | #include <o3tl/string_view.hxx> |
28 | | #include <i18nutil/unicode.hxx> |
29 | | |
30 | | #include <vcl/event.hxx> |
31 | | #include <vcl/fieldvalues.hxx> |
32 | | #include <vcl/toolkit/field.hxx> |
33 | | #include <vcl/uitest/metricfielduiobject.hxx> |
34 | | |
35 | | #include <svdata.hxx> |
36 | | |
37 | | #include <boost/property_tree/ptree.hpp> |
38 | | |
39 | | #include <cmath> |
40 | | #include <string_view> |
41 | | |
42 | | namespace |
43 | | { |
44 | | sal_Int64 ImplPower10( sal_uInt16 n ) |
45 | 0 | { |
46 | 0 | sal_Int64 nValue = 1; |
47 | |
|
48 | 0 | for (sal_uInt16 i = 0; i < n; i++) |
49 | 0 | { |
50 | 0 | nValue *= 10; |
51 | 0 | } |
52 | |
|
53 | 0 | return nValue; |
54 | 0 | } |
55 | | |
56 | | bool ImplNumericProcessKeyInput( const KeyEvent& rKEvt, |
57 | | bool bStrictFormat, bool bThousandSep, |
58 | | const LocaleDataWrapper& rLocaleDataWrapper ) |
59 | 0 | { |
60 | 0 | if ( !bStrictFormat ) |
61 | 0 | return false; |
62 | 0 | else |
63 | 0 | { |
64 | 0 | sal_Unicode cChar = rKEvt.GetCharCode(); |
65 | 0 | sal_uInt16 nGroup = rKEvt.GetKeyCode().GetGroup(); |
66 | |
|
67 | 0 | return !((nGroup == KEYGROUP_FKEYS) || |
68 | 0 | (nGroup == KEYGROUP_CURSOR) || |
69 | 0 | (nGroup == KEYGROUP_MISC) || |
70 | 0 | ((cChar >= '0') && (cChar <= '9')) || |
71 | 0 | rLocaleDataWrapper.getNumDecimalSep() == OUStringChar(cChar) || |
72 | 0 | (bThousandSep && rLocaleDataWrapper.getNumThousandSep() == OUStringChar(cChar)) || |
73 | 0 | rLocaleDataWrapper.getNumDecimalSepAlt() == OUStringChar(cChar) || |
74 | 0 | (cChar == '-')); |
75 | 0 | } |
76 | 0 | } |
77 | | |
78 | | // Takes a string with a number, which may be an integer, a floating-point with locale-specified |
79 | | // decimal separator, or a fraction (and if allowed, where negatives can be represented in currency |
80 | | // format - in parentheses); pre-processes the string to be a floating-point scaled by nDecDigits; |
81 | | // returns a pair { scaled_whole_part_string, decimal_part_string }. |
82 | | std::pair<OUString, OUString> ToScaledWholeAndDec(std::u16string_view aStr, sal_uInt16 nDecDigits, |
83 | | const LocaleDataWrapper& rLocaleDataWrapper, |
84 | | bool bCurrency) |
85 | 0 | { |
86 | | // remove leading and trailing spaces |
87 | 0 | aStr = o3tl::trim(aStr); |
88 | 0 | OUStringBuffer aStr1, aStr2, aStrNum, aStrDenom; |
89 | 0 | bool bNegative = false; |
90 | 0 | bool bFrac = false; |
91 | | |
92 | | // react on empty string |
93 | 0 | if (aStr.empty()) |
94 | 0 | return {}; |
95 | | |
96 | | // find position of decimal point |
97 | 0 | auto nDecPos = aStr.find(rLocaleDataWrapper.getNumDecimalSep()); |
98 | 0 | if (nDecPos == std::u16string_view::npos && !rLocaleDataWrapper.getNumDecimalSepAlt().isEmpty()) |
99 | 0 | nDecPos = aStr.find( rLocaleDataWrapper.getNumDecimalSepAlt() ); |
100 | | |
101 | | // parse fractional strings |
102 | 0 | if (auto nFracDivPos = aStr.find('/'); |
103 | 0 | nFracDivPos > 0 && nFracDivPos != std::u16string_view::npos) |
104 | 0 | { |
105 | 0 | bFrac = true; |
106 | 0 | auto nFracNumPos = aStr.rfind(' ', nFracDivPos); |
107 | | |
108 | | // If in "a b/c" format. |
109 | 0 | if (nFracNumPos != std::u16string_view::npos) |
110 | 0 | { |
111 | 0 | aStr1.append(aStr.substr(0, nFracNumPos)); |
112 | 0 | aStrNum.append(aStr.substr(nFracNumPos+1, nFracDivPos-nFracNumPos-1)); |
113 | 0 | aStrDenom.append(aStr.substr(nFracDivPos+1)); |
114 | 0 | } |
115 | | // "a/b" format, or not a fraction at all |
116 | 0 | else |
117 | 0 | { |
118 | 0 | aStrNum.append(aStr.substr(0, nFracDivPos)); |
119 | 0 | aStrDenom.append(aStr.substr(nFracDivPos+1)); |
120 | 0 | } |
121 | |
|
122 | 0 | } |
123 | | // parse decimal strings |
124 | 0 | else if (nDecPos != std::u16string_view::npos) |
125 | 0 | { |
126 | 0 | aStr1.append(aStr.substr(0, nDecPos)); |
127 | 0 | aStr2.append(aStr.substr(nDecPos+1)); |
128 | 0 | } |
129 | 0 | else |
130 | 0 | aStr1 = aStr; |
131 | | |
132 | | // negative? |
133 | 0 | if ( bCurrency ) |
134 | 0 | { |
135 | 0 | if ( aStr.starts_with('(') && aStr.ends_with(')') ) |
136 | 0 | bNegative = true; |
137 | 0 | if ( !bNegative ) |
138 | 0 | { |
139 | 0 | for (size_t i = 0; i < aStr.size(); i++) |
140 | 0 | { |
141 | 0 | if ( (aStr[i] >= '0') && (aStr[i] <= '9') ) |
142 | 0 | break; |
143 | 0 | else if ( aStr[i] == '-' ) |
144 | 0 | { |
145 | 0 | bNegative = true; |
146 | 0 | break; |
147 | 0 | } |
148 | 0 | } |
149 | 0 | } |
150 | 0 | if (!bNegative && !aStr.empty()) |
151 | 0 | { |
152 | 0 | sal_uInt16 nFormat = rLocaleDataWrapper.getCurrNegativeFormat(); |
153 | 0 | if ( (nFormat == 3) || (nFormat == 6) || // $1- || 1-$ |
154 | 0 | (nFormat == 7) || (nFormat == 10) ) // 1$- || 1 $- |
155 | 0 | { |
156 | 0 | for (size_t i = aStr.size() - 1; i > 0; --i) |
157 | 0 | { |
158 | 0 | if ( (aStr[i] >= '0') && (aStr[i] <= '9') ) |
159 | 0 | break; |
160 | 0 | else if ( aStr[i] == '-' ) |
161 | 0 | { |
162 | 0 | bNegative = true; |
163 | 0 | break; |
164 | 0 | } |
165 | 0 | } |
166 | 0 | } |
167 | 0 | } |
168 | 0 | } |
169 | 0 | else |
170 | 0 | { |
171 | 0 | if ( !aStr1.isEmpty() && aStr1[0] == '-') |
172 | 0 | bNegative = true; |
173 | 0 | if ( !aStrNum.isEmpty() && aStrNum[0] == '-') // For non-mixed fractions |
174 | 0 | bNegative = true; |
175 | 0 | } |
176 | | |
177 | | // remove all unwanted characters |
178 | | // For whole number |
179 | 0 | for (sal_Int32 i=0; i < aStr1.getLength(); ) |
180 | 0 | { |
181 | 0 | if ( (aStr1[i] >= '0') && (aStr1[i] <= '9') ) |
182 | 0 | i++; |
183 | 0 | else |
184 | 0 | aStr1.remove( i, 1 ); |
185 | 0 | } |
186 | | // For decimal |
187 | 0 | if (!bFrac) { |
188 | 0 | for (sal_Int32 i=0; i < aStr2.getLength(); ) |
189 | 0 | { |
190 | 0 | if ((aStr2[i] >= '0') && (aStr2[i] <= '9')) |
191 | 0 | ++i; |
192 | 0 | else |
193 | 0 | aStr2.remove(i, 1); |
194 | 0 | } |
195 | 0 | } |
196 | 0 | else { |
197 | | // for numerator |
198 | 0 | for (sal_Int32 i=0; i < aStrNum.getLength(); ) |
199 | 0 | { |
200 | 0 | if ((aStrNum[i] >= '0') && (aStrNum[i] <= '9')) |
201 | 0 | ++i; |
202 | 0 | else |
203 | 0 | aStrNum.remove(i, 1); |
204 | 0 | } |
205 | | // for denominator |
206 | 0 | for (sal_Int32 i=0; i < aStrDenom.getLength(); ) |
207 | 0 | { |
208 | 0 | if ((aStrDenom[i] >= '0') && (aStrDenom[i] <= '9')) |
209 | 0 | ++i; |
210 | 0 | else |
211 | 0 | aStrDenom.remove(i, 1); |
212 | 0 | } |
213 | 0 | } |
214 | | |
215 | |
|
216 | 0 | if ( !bFrac && aStr1.isEmpty() && aStr2.isEmpty() ) |
217 | 0 | return {}; |
218 | 0 | else if ( bFrac && aStr1.isEmpty() && (aStrNum.isEmpty() || aStrDenom.isEmpty()) ) |
219 | 0 | return {}; |
220 | | |
221 | 0 | if ( aStr1.isEmpty() ) |
222 | 0 | aStr1 = "0"; |
223 | 0 | if ( bNegative ) |
224 | 0 | aStr1.insert(0, "-"); |
225 | | |
226 | | // Convert fractional strings |
227 | 0 | if (bFrac) { |
228 | | // Convert to fraction |
229 | 0 | sal_Int64 nWholeNum = o3tl::toInt64(aStr1); |
230 | 0 | aStr1.setLength(0); |
231 | 0 | sal_Int64 nNum = o3tl::toInt64(aStrNum); |
232 | 0 | sal_Int64 nDenom = o3tl::toInt64(aStrDenom); |
233 | 0 | if (nDenom == 0) return {}; // Division by zero |
234 | 0 | double nFrac2Dec = nWholeNum + static_cast<double>(nNum)/nDenom; // Convert to double for floating point precision |
235 | 0 | OUStringBuffer aStrFrac(OUString::number(nFrac2Dec)); |
236 | | // Reconvert division result to string and parse |
237 | 0 | nDecPos = aStrFrac.indexOf('.'); |
238 | 0 | if (nDecPos != std::u16string_view::npos) |
239 | 0 | { |
240 | 0 | aStr1.append(aStrFrac.getStr(), nDecPos); |
241 | 0 | aStr2.append(aStrFrac.getStr()+nDecPos+1); |
242 | 0 | } |
243 | 0 | else |
244 | 0 | aStr1 = std::move(aStrFrac); |
245 | 0 | } |
246 | | |
247 | 0 | if (nDecDigits) |
248 | 0 | { |
249 | 0 | const sal_Int32 moveTo1 = std::min(static_cast<sal_Int32>(nDecDigits), aStr2.getLength()); |
250 | 0 | aStr1.append(aStr2.subView(0, moveTo1) + RepeatedUChar('0', nDecDigits - moveTo1)); |
251 | 0 | aStr2.remove(0, moveTo1); |
252 | 0 | } |
253 | |
|
254 | 0 | return { aStr1.makeStringAndClear(), aStr2.makeStringAndClear() }; |
255 | 0 | } |
256 | | |
257 | | bool ImplNumericGetValue(std::u16string_view rStr, sal_Int64& rValue, sal_uInt16 nDecDigits, |
258 | | const LocaleDataWrapper& rLocaleDataWrapper, bool bCurrency = false) |
259 | 0 | { |
260 | 0 | const auto [whole, dec] = ToScaledWholeAndDec(rStr, nDecDigits, rLocaleDataWrapper, bCurrency); |
261 | 0 | if (whole.isEmpty() && dec.isEmpty()) |
262 | 0 | return false; |
263 | | |
264 | | // prune and round fraction |
265 | 0 | const bool bRound = !dec.isEmpty() && dec[0] >= '5'; |
266 | | |
267 | | // check range |
268 | 0 | sal_Int64 nValue = whole.toInt64(); |
269 | 0 | const bool bNegative = whole.startsWith("-"); |
270 | 0 | if( nValue == 0 ) |
271 | 0 | { |
272 | | // check if string is equivalent to zero |
273 | 0 | sal_Int32 nIndex = bNegative ? 1 : 0; |
274 | 0 | while (nIndex < whole.getLength() && whole[nIndex] == '0') |
275 | 0 | ++nIndex; |
276 | 0 | if (nIndex < whole.getLength()) |
277 | 0 | { |
278 | 0 | rValue = bNegative ? SAL_MIN_INT64 : SAL_MAX_INT64; |
279 | 0 | return true; |
280 | 0 | } |
281 | 0 | } |
282 | 0 | if (bRound) |
283 | 0 | { |
284 | 0 | if ( !bNegative ) |
285 | 0 | nValue++; |
286 | 0 | else |
287 | 0 | nValue--; |
288 | 0 | } |
289 | |
|
290 | 0 | rValue = nValue; |
291 | |
|
292 | 0 | return true; |
293 | 0 | } |
294 | | |
295 | | // The returned double is scaled according to nDecDigits, same way as ImplNumericGetValue |
296 | | bool ImplNumericGetDoubleValue(std::u16string_view rStr, double& rValue, sal_uInt16 nDecDigits, |
297 | | const LocaleDataWrapper& rLocaleDataWrapper) |
298 | 0 | { |
299 | 0 | const auto [whole, dec] = ToScaledWholeAndDec(rStr, nDecDigits, rLocaleDataWrapper, false); |
300 | 0 | if (whole.isEmpty() && dec.isEmpty()) |
301 | 0 | return false; |
302 | | |
303 | 0 | rValue = o3tl::toDouble(Concat2View(whole + "." + dec)); |
304 | 0 | return true; |
305 | 0 | } |
306 | | |
307 | | void ImplUpdateSeparatorString( OUString& io_rText, |
308 | | std::u16string_view rOldDecSep, std::u16string_view rNewDecSep, |
309 | | std::u16string_view rOldThSep, std::u16string_view rNewThSep ) |
310 | 0 | { |
311 | 0 | OUStringBuffer aBuf( io_rText.getLength() ); |
312 | 0 | sal_Int32 nIndexDec = 0, nIndexTh = 0, nIndex = 0; |
313 | |
|
314 | 0 | const sal_Unicode* pBuffer = io_rText.getStr(); |
315 | 0 | while( nIndex != -1 ) |
316 | 0 | { |
317 | 0 | nIndexDec = io_rText.indexOf( rOldDecSep, nIndex ); |
318 | 0 | nIndexTh = io_rText.indexOf( rOldThSep, nIndex ); |
319 | 0 | if( (nIndexTh != -1 && nIndexDec != -1 && nIndexTh < nIndexDec ) |
320 | 0 | || (nIndexTh != -1 && nIndexDec == -1) |
321 | 0 | ) |
322 | 0 | { |
323 | 0 | aBuf.append( OUString::Concat(std::u16string_view(pBuffer + nIndex, nIndexTh - nIndex )) + rNewThSep ); |
324 | 0 | nIndex = nIndexTh + rOldThSep.size(); |
325 | 0 | } |
326 | 0 | else if( nIndexDec != -1 ) |
327 | 0 | { |
328 | 0 | aBuf.append( OUString::Concat(std::u16string_view(pBuffer + nIndex, nIndexDec - nIndex )) + rNewDecSep ); |
329 | 0 | nIndex = nIndexDec + rOldDecSep.size(); |
330 | 0 | } |
331 | 0 | else |
332 | 0 | { |
333 | 0 | aBuf.append( pBuffer + nIndex ); |
334 | 0 | nIndex = -1; |
335 | 0 | } |
336 | 0 | } |
337 | |
|
338 | 0 | io_rText = aBuf.makeStringAndClear(); |
339 | 0 | } |
340 | | |
341 | | void ImplUpdateSeparators( std::u16string_view rOldDecSep, std::u16string_view rNewDecSep, |
342 | | std::u16string_view rOldThSep, std::u16string_view rNewThSep, |
343 | | Edit* pEdit ) |
344 | 0 | { |
345 | 0 | bool bChangeDec = (rOldDecSep != rNewDecSep); |
346 | 0 | bool bChangeTh = (rOldThSep != rNewThSep ); |
347 | |
|
348 | 0 | if( !(bChangeDec || bChangeTh) ) |
349 | 0 | return; |
350 | | |
351 | 0 | bool bUpdateMode = pEdit->IsUpdateMode(); |
352 | 0 | pEdit->SetUpdateMode( false ); |
353 | 0 | OUString aText = pEdit->GetText(); |
354 | 0 | ImplUpdateSeparatorString( aText, rOldDecSep, rNewDecSep, rOldThSep, rNewThSep ); |
355 | 0 | pEdit->SetText( aText ); |
356 | |
|
357 | 0 | ComboBox* pCombo = dynamic_cast<ComboBox*>(pEdit); |
358 | 0 | if( pCombo ) |
359 | 0 | { |
360 | | // update box entries |
361 | 0 | sal_Int32 nEntryCount = pCombo->GetEntryCount(); |
362 | 0 | for ( sal_Int32 i=0; i < nEntryCount; i++ ) |
363 | 0 | { |
364 | 0 | aText = pCombo->GetEntry( i ); |
365 | 0 | OUString* pEntryData = pCombo->GetEntryData(i); |
366 | 0 | ImplUpdateSeparatorString( aText, rOldDecSep, rNewDecSep, rOldThSep, rNewThSep ); |
367 | 0 | pCombo->RemoveEntryAt(i); |
368 | 0 | pCombo->InsertEntry( aText, i ); |
369 | 0 | pCombo->SetEntryData( i, pEntryData ); |
370 | 0 | } |
371 | 0 | } |
372 | 0 | if( bUpdateMode ) |
373 | 0 | pEdit->SetUpdateMode( bUpdateMode ); |
374 | 0 | } |
375 | | |
376 | | sal_Int64 clipDoubleAgainstMinMax(double n, sal_Int64 nMin, sal_Int64 nMax) |
377 | 0 | { |
378 | | // caution: precision loss in double cast |
379 | 0 | if (n <= static_cast<double>(nMin)) |
380 | 0 | return nMin; |
381 | 0 | else if (n >= static_cast<double>(nMax)) |
382 | 0 | return nMax; |
383 | 0 | else |
384 | 0 | return static_cast<sal_Int64>(std::round(n)); |
385 | 0 | } |
386 | | |
387 | | } // namespace |
388 | | |
389 | | FormatterBase::FormatterBase(Edit* pField) |
390 | 0 | { |
391 | 0 | mpField = pField; |
392 | 0 | mpLocaleDataWrapper = nullptr; |
393 | 0 | mbReformat = false; |
394 | 0 | mbStrictFormat = false; |
395 | 0 | mbEmptyFieldValue = false; |
396 | 0 | mbEmptyFieldValueEnabled = false; |
397 | 0 | } |
398 | | |
399 | | FormatterBase::~FormatterBase() |
400 | 0 | { |
401 | 0 | } |
402 | | |
403 | | LocaleDataWrapper& FormatterBase::ImplGetLocaleDataWrapper() const |
404 | 0 | { |
405 | 0 | if ( !mpLocaleDataWrapper ) |
406 | 0 | { |
407 | 0 | mpLocaleDataWrapper.reset( new LocaleDataWrapper( GetLanguageTag() ) ); |
408 | 0 | } |
409 | 0 | return *mpLocaleDataWrapper; |
410 | 0 | } |
411 | | |
412 | | /** reset the LocaleDataWrapper when the language tag changes */ |
413 | | void FormatterBase::ImplResetLocaleDataWrapper() const |
414 | 0 | { |
415 | | // just get rid of, the next time it is requested, it will get loaded with the right |
416 | | // language tag |
417 | 0 | mpLocaleDataWrapper.reset(); |
418 | 0 | } |
419 | | |
420 | | const LocaleDataWrapper& FormatterBase::GetLocaleDataWrapper() const |
421 | 0 | { |
422 | 0 | return ImplGetLocaleDataWrapper(); |
423 | 0 | } |
424 | | |
425 | | void FormatterBase::Reformat() |
426 | 0 | { |
427 | 0 | } |
428 | | |
429 | | void FormatterBase::ReformatAll() |
430 | 0 | { |
431 | 0 | Reformat(); |
432 | 0 | }; |
433 | | |
434 | | void FormatterBase::SetStrictFormat( bool bStrict ) |
435 | 0 | { |
436 | 0 | if ( bStrict != mbStrictFormat ) |
437 | 0 | { |
438 | 0 | mbStrictFormat = bStrict; |
439 | 0 | if ( mbStrictFormat ) |
440 | 0 | ReformatAll(); |
441 | 0 | } |
442 | 0 | } |
443 | | |
444 | | const css::lang::Locale& FormatterBase::GetLocale() const |
445 | 0 | { |
446 | 0 | if ( mpField ) |
447 | 0 | return mpField->GetSettings().GetLanguageTag().getLocale(); |
448 | 0 | else |
449 | 0 | return Application::GetSettings().GetLanguageTag().getLocale(); |
450 | 0 | } |
451 | | |
452 | | const LanguageTag& FormatterBase::GetLanguageTag() const |
453 | 0 | { |
454 | 0 | if ( mpField ) |
455 | 0 | return mpField->GetSettings().GetLanguageTag(); |
456 | 0 | else |
457 | 0 | return Application::GetSettings().GetLanguageTag(); |
458 | 0 | } |
459 | | |
460 | | void FormatterBase::ImplSetText( const OUString& rText, Selection const * pNewSelection ) |
461 | 0 | { |
462 | 0 | if ( mpField ) |
463 | 0 | { |
464 | 0 | if (pNewSelection) |
465 | 0 | mpField->SetText(rText, *pNewSelection); |
466 | 0 | else |
467 | 0 | { |
468 | 0 | Selection aSel = mpField->GetSelection(); |
469 | 0 | aSel.Min() = aSel.Max(); |
470 | 0 | mpField->SetText(rText, aSel); |
471 | 0 | } |
472 | 0 | MarkToBeReformatted( false ); |
473 | 0 | } |
474 | 0 | } |
475 | | |
476 | | void FormatterBase::SetEmptyFieldValue() |
477 | 0 | { |
478 | 0 | if ( mpField ) |
479 | 0 | mpField->SetText( OUString() ); |
480 | 0 | mbEmptyFieldValue = true; |
481 | 0 | } |
482 | | |
483 | | bool FormatterBase::IsEmptyFieldValue() const |
484 | 0 | { |
485 | 0 | return (!mpField || mpField->GetText().isEmpty()); |
486 | 0 | } |
487 | | |
488 | | void NumericFormatter::FormatValue(Selection const * pNewSelection) |
489 | 0 | { |
490 | 0 | mbFormatting = true; |
491 | 0 | ImplSetText(CreateFieldText(mnLastValue), pNewSelection); |
492 | 0 | mbFormatting = false; |
493 | 0 | } |
494 | | |
495 | | void NumericFormatter::ImplNumericReformat() |
496 | 0 | { |
497 | 0 | mnLastValue = GetValue(); |
498 | 0 | FormatValue(); |
499 | 0 | } |
500 | | |
501 | | NumericFormatter::NumericFormatter(Edit* pEdit) |
502 | 0 | : FormatterBase(pEdit) |
503 | 0 | , mnLastValue(0) |
504 | 0 | , mnMin(0) |
505 | | // a "large" value substantially smaller than SAL_MAX_INT64, to avoid |
506 | | // overflow in computations using this "dummy" value |
507 | 0 | , mnMax(SAL_MAX_INT32) |
508 | 0 | , mbFormatting(false) |
509 | 0 | , mnSpinSize(1) |
510 | | // for fields |
511 | 0 | , mnFirst(mnMin) |
512 | 0 | , mnLast(mnMax) |
513 | 0 | , mnDecimalDigits(0) |
514 | 0 | , mbThousandSep(true) |
515 | 0 | { |
516 | 0 | ReformatAll(); |
517 | 0 | } |
518 | | |
519 | | NumericFormatter::~NumericFormatter() |
520 | | { |
521 | | } |
522 | | |
523 | | void NumericFormatter::SetMin( sal_Int64 nNewMin ) |
524 | 0 | { |
525 | 0 | mnMin = nNewMin; |
526 | 0 | if ( !IsEmptyFieldValue() ) |
527 | 0 | ReformatAll(); |
528 | 0 | } |
529 | | |
530 | | void NumericFormatter::SetMax( sal_Int64 nNewMax ) |
531 | 0 | { |
532 | 0 | mnMax = nNewMax; |
533 | 0 | if ( !IsEmptyFieldValue() ) |
534 | 0 | ReformatAll(); |
535 | 0 | } |
536 | | |
537 | | void NumericFormatter::SetUseThousandSep( bool bValue ) |
538 | 0 | { |
539 | 0 | mbThousandSep = bValue; |
540 | 0 | ReformatAll(); |
541 | 0 | } |
542 | | |
543 | | void NumericFormatter::SetDecimalDigits( sal_uInt16 nDigits ) |
544 | 0 | { |
545 | 0 | mnDecimalDigits = nDigits; |
546 | 0 | ReformatAll(); |
547 | 0 | } |
548 | | |
549 | | void NumericFormatter::SetValue( sal_Int64 nNewValue ) |
550 | 0 | { |
551 | 0 | SetUserValue( nNewValue ); |
552 | 0 | SetEmptyFieldValueData( false ); |
553 | 0 | } |
554 | | |
555 | | OUString NumericFormatter::CreateFieldText( sal_Int64 nValue ) const |
556 | 0 | { |
557 | 0 | return ImplGetLocaleDataWrapper().getNum( nValue, GetDecimalDigits(), IsUseThousandSep(), /*ShowTrailingZeros*/true ); |
558 | 0 | } |
559 | | |
560 | | void NumericFormatter::ImplSetUserValue( sal_Int64 nNewValue, Selection const * pNewSelection ) |
561 | 0 | { |
562 | 0 | nNewValue = ClipAgainstMinMax(nNewValue); |
563 | 0 | mnLastValue = nNewValue; |
564 | |
|
565 | 0 | if ( GetField() ) |
566 | 0 | FormatValue(pNewSelection); |
567 | 0 | } |
568 | | |
569 | | void NumericFormatter::SetUserValue( sal_Int64 nNewValue ) |
570 | 0 | { |
571 | 0 | ImplSetUserValue( nNewValue ); |
572 | 0 | } |
573 | | |
574 | | sal_Int64 NumericFormatter::GetValueFromString(std::u16string_view rStr) const |
575 | 0 | { |
576 | 0 | sal_Int64 nTempValue; |
577 | |
|
578 | 0 | if (ImplNumericGetValue(rStr, nTempValue, |
579 | 0 | GetDecimalDigits(), ImplGetLocaleDataWrapper())) |
580 | 0 | { |
581 | 0 | return ClipAgainstMinMax(nTempValue); |
582 | 0 | } |
583 | 0 | else |
584 | 0 | return mnLastValue; |
585 | 0 | } |
586 | | |
587 | | OUString NumericFormatter::GetValueString() const |
588 | 0 | { |
589 | 0 | return Application::GetSettings().GetNeutralLocaleDataWrapper(). |
590 | 0 | getNum(GetValue(), GetDecimalDigits(), false, false); |
591 | 0 | } |
592 | | |
593 | | // currently used by online |
594 | | void NumericFormatter::SetValueFromString(const OUString& rStr) |
595 | 0 | { |
596 | 0 | sal_Int64 nValue; |
597 | |
|
598 | 0 | if (ImplNumericGetValue(rStr, nValue, GetDecimalDigits(), |
599 | 0 | Application::GetSettings().GetNeutralLocaleDataWrapper())) |
600 | 0 | { |
601 | 0 | ImplNewFieldValue(nValue); |
602 | 0 | } |
603 | 0 | else |
604 | 0 | { |
605 | 0 | SAL_WARN("vcl", "fail to convert the value: " << rStr ); |
606 | 0 | } |
607 | 0 | } |
608 | | |
609 | | sal_Int64 NumericFormatter::GetValue() const |
610 | 0 | { |
611 | 0 | if (mbFormatting) //don't parse the entry if we're currently formatting what to put in it |
612 | 0 | return mnLastValue; |
613 | | |
614 | 0 | return GetField() ? GetValueFromString(GetField()->GetText()) : 0; |
615 | 0 | } |
616 | | |
617 | | sal_Int64 NumericFormatter::Normalize( sal_Int64 nValue ) const |
618 | 0 | { |
619 | 0 | return (nValue * ImplPower10( GetDecimalDigits() ) ); |
620 | 0 | } |
621 | | |
622 | | sal_Int64 NumericFormatter::Denormalize( sal_Int64 nValue ) const |
623 | 0 | { |
624 | 0 | sal_Int64 nFactor = ImplPower10( GetDecimalDigits() ); |
625 | |
|
626 | 0 | if ((nValue < ( SAL_MIN_INT64 + nFactor )) || |
627 | 0 | (nValue > ( SAL_MAX_INT64 - nFactor ))) |
628 | 0 | { |
629 | 0 | return ( nValue / nFactor ); |
630 | 0 | } |
631 | | |
632 | 0 | if( nValue < 0 ) |
633 | 0 | { |
634 | 0 | sal_Int64 nHalf = nFactor / 2; |
635 | 0 | return ((nValue - nHalf) / nFactor ); |
636 | 0 | } |
637 | 0 | else |
638 | 0 | { |
639 | 0 | sal_Int64 nHalf = nFactor / 2; |
640 | 0 | return ((nValue + nHalf) / nFactor ); |
641 | 0 | } |
642 | 0 | } |
643 | | |
644 | | void NumericFormatter::Reformat() |
645 | 0 | { |
646 | 0 | if ( !GetField() ) |
647 | 0 | return; |
648 | | |
649 | 0 | if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() ) |
650 | 0 | return; |
651 | | |
652 | 0 | ImplNumericReformat(); |
653 | 0 | } |
654 | | |
655 | | void NumericFormatter::FieldUp() |
656 | 0 | { |
657 | 0 | sal_Int64 nValue = GetValue(); |
658 | 0 | sal_Int64 nRemainder = nValue % mnSpinSize; |
659 | 0 | if (nValue >= 0) |
660 | 0 | nValue = (nRemainder == 0) ? nValue + mnSpinSize : nValue + mnSpinSize - nRemainder; |
661 | 0 | else |
662 | 0 | nValue = (nRemainder == 0) ? nValue + mnSpinSize : nValue - nRemainder; |
663 | |
|
664 | 0 | nValue = ClipAgainstMinMax(nValue); |
665 | |
|
666 | 0 | ImplNewFieldValue( nValue ); |
667 | 0 | } |
668 | | |
669 | | void NumericFormatter::FieldDown() |
670 | 0 | { |
671 | 0 | sal_Int64 nValue = GetValue(); |
672 | 0 | sal_Int64 nRemainder = nValue % mnSpinSize; |
673 | 0 | if (nValue >= 0) |
674 | 0 | nValue = (nRemainder == 0) ? nValue - mnSpinSize : nValue - nRemainder; |
675 | 0 | else |
676 | 0 | nValue = (nRemainder == 0) ? nValue - mnSpinSize : nValue - mnSpinSize - nRemainder; |
677 | |
|
678 | 0 | nValue = ClipAgainstMinMax(nValue); |
679 | |
|
680 | 0 | ImplNewFieldValue( nValue ); |
681 | 0 | } |
682 | | |
683 | | void NumericFormatter::FieldFirst() |
684 | 0 | { |
685 | 0 | ImplNewFieldValue( mnFirst ); |
686 | 0 | } |
687 | | |
688 | | void NumericFormatter::FieldLast() |
689 | 0 | { |
690 | 0 | ImplNewFieldValue( mnLast ); |
691 | 0 | } |
692 | | |
693 | | void NumericFormatter::ImplNewFieldValue( sal_Int64 nNewValue ) |
694 | 0 | { |
695 | 0 | if ( !GetField() ) |
696 | 0 | return; |
697 | | |
698 | | // !!! We should check why we do not validate in ImplSetUserValue() if the value was |
699 | | // changed. This should be done there as well since otherwise the call to Modify would not |
700 | | // be allowed. Anyway, the paths from ImplNewFieldValue, ImplSetUserValue, and ImplSetText |
701 | | // should be checked and clearly traced (with comment) in order to find out what happens. |
702 | | |
703 | 0 | Selection aSelection = GetField()->GetSelection(); |
704 | 0 | aSelection.Normalize(); |
705 | 0 | OUString aText = GetField()->GetText(); |
706 | | // leave it as is if selected until end |
707 | 0 | if ( static_cast<sal_Int32>(aSelection.Max()) == aText.getLength() ) |
708 | 0 | { |
709 | 0 | if ( !aSelection.Len() ) |
710 | 0 | aSelection.Min() = SELECTION_MAX; |
711 | 0 | aSelection.Max() = SELECTION_MAX; |
712 | 0 | } |
713 | |
|
714 | 0 | sal_Int64 nOldLastValue = mnLastValue; |
715 | 0 | ImplSetUserValue( nNewValue, &aSelection ); |
716 | 0 | mnLastValue = nOldLastValue; |
717 | | |
718 | | // Modify during Edit is only set during KeyInput |
719 | 0 | if ( GetField()->GetText() != aText ) |
720 | 0 | { |
721 | 0 | GetField()->SetModifyFlag(); |
722 | 0 | GetField()->Modify(); |
723 | 0 | } |
724 | 0 | } |
725 | | |
726 | | sal_Int64 NumericFormatter::ClipAgainstMinMax(sal_Int64 nValue) const |
727 | 0 | { |
728 | 0 | return std::clamp(nValue, GetMin(), GetMax()); |
729 | 0 | } |
730 | | |
731 | | sal_Int64 NumericFormatter::ClipDoubleAgainstMinMax(double nValue) const |
732 | 0 | { |
733 | 0 | return clipDoubleAgainstMinMax(nValue, GetMin(), GetMax()); |
734 | 0 | } |
735 | | |
736 | | namespace |
737 | | { |
738 | | Size calcMinimumSize(const Edit &rSpinField, const NumericFormatter &rFormatter) |
739 | 0 | { |
740 | 0 | OUStringBuffer aBuf; |
741 | 0 | sal_Int32 nTextLen; |
742 | |
|
743 | 0 | nTextLen = OUString::number(rFormatter.GetMin()).length; |
744 | 0 | comphelper::string::padToLength(aBuf, nTextLen, '9'); |
745 | 0 | Size aMinTextSize = rSpinField.CalcMinimumSizeForText( |
746 | 0 | rFormatter.CreateFieldText(OUString::unacquired(aBuf).toInt64())); |
747 | 0 | aBuf.setLength(0); |
748 | |
|
749 | 0 | nTextLen = OUString::number(rFormatter.GetMax()).length; |
750 | 0 | comphelper::string::padToLength(aBuf, nTextLen, '9'); |
751 | 0 | Size aMaxTextSize = rSpinField.CalcMinimumSizeForText( |
752 | 0 | rFormatter.CreateFieldText(OUString::unacquired(aBuf).toInt64())); |
753 | 0 | aBuf.setLength(0); |
754 | |
|
755 | 0 | Size aRet(std::max(aMinTextSize.Width(), aMaxTextSize.Width()), |
756 | 0 | std::max(aMinTextSize.Height(), aMaxTextSize.Height())); |
757 | |
|
758 | 0 | aBuf = "999999999"; |
759 | 0 | sal_uInt16 nDigits = rFormatter.GetDecimalDigits(); |
760 | 0 | if (nDigits) |
761 | 0 | { |
762 | 0 | aBuf.append("." + RepeatedUChar('9', nDigits)); |
763 | 0 | } |
764 | 0 | aMaxTextSize = rSpinField.CalcMinimumSizeForText(aBuf.makeStringAndClear()); |
765 | 0 | aRet.setWidth( std::min(aRet.Width(), aMaxTextSize.Width()) ); |
766 | |
|
767 | 0 | return aRet; |
768 | 0 | } |
769 | | } |
770 | | |
771 | | NumericBox::NumericBox(vcl::Window* pParent, WinBits nWinStyle) |
772 | 0 | : ComboBox(pParent, nWinStyle) |
773 | 0 | , NumericFormatter(this) |
774 | 0 | { |
775 | 0 | Reformat(); |
776 | 0 | if ( !(nWinStyle & WB_HIDE ) ) |
777 | 0 | Show(); |
778 | 0 | } Unexecuted instantiation: NumericBox::NumericBox(vcl::Window*, long) Unexecuted instantiation: NumericBox::NumericBox(vcl::Window*, long) |
779 | | |
780 | | void NumericBox::dispose() |
781 | 0 | { |
782 | 0 | ClearField(); |
783 | 0 | ComboBox::dispose(); |
784 | 0 | } |
785 | | |
786 | | Size NumericBox::CalcMinimumSize() const |
787 | 0 | { |
788 | 0 | Size aRet(calcMinimumSize(*this, *this)); |
789 | |
|
790 | 0 | if (IsDropDownBox()) |
791 | 0 | { |
792 | 0 | Size aComboSugg(ComboBox::CalcMinimumSize()); |
793 | 0 | aRet.setWidth( std::max(aRet.Width(), aComboSugg.Width()) ); |
794 | 0 | aRet.setHeight( std::max(aRet.Height(), aComboSugg.Height()) ); |
795 | 0 | } |
796 | |
|
797 | 0 | return aRet; |
798 | 0 | } |
799 | | |
800 | | bool NumericBox::PreNotify( NotifyEvent& rNEvt ) |
801 | 0 | { |
802 | 0 | if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() ) |
803 | 0 | { |
804 | 0 | if ( ImplNumericProcessKeyInput( *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) ) |
805 | 0 | return true; |
806 | 0 | } |
807 | | |
808 | 0 | return ComboBox::PreNotify( rNEvt ); |
809 | 0 | } |
810 | | |
811 | | bool NumericBox::EventNotify( NotifyEvent& rNEvt ) |
812 | 0 | { |
813 | 0 | if ( rNEvt.GetType() == NotifyEventType::GETFOCUS ) |
814 | 0 | MarkToBeReformatted( false ); |
815 | 0 | else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS ) |
816 | 0 | { |
817 | 0 | if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) ) |
818 | 0 | Reformat(); |
819 | 0 | } |
820 | |
|
821 | 0 | return ComboBox::EventNotify( rNEvt ); |
822 | 0 | } |
823 | | |
824 | | void NumericBox::DataChanged( const DataChangedEvent& rDCEvt ) |
825 | 0 | { |
826 | 0 | ComboBox::DataChanged( rDCEvt ); |
827 | |
|
828 | 0 | if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) ) |
829 | 0 | { |
830 | 0 | OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep(); |
831 | 0 | OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep(); |
832 | 0 | ImplResetLocaleDataWrapper(); |
833 | 0 | OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep(); |
834 | 0 | OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep(); |
835 | 0 | ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this ); |
836 | 0 | ReformatAll(); |
837 | 0 | } |
838 | 0 | } |
839 | | |
840 | | void NumericBox::Modify() |
841 | 0 | { |
842 | 0 | MarkToBeReformatted( true ); |
843 | 0 | ComboBox::Modify(); |
844 | 0 | } |
845 | | |
846 | | void NumericBox::ImplNumericReformat( std::u16string_view rStr, sal_Int64& rValue, |
847 | | OUString& rOutStr ) |
848 | 0 | { |
849 | 0 | if (ImplNumericGetValue(rStr, rValue, GetDecimalDigits(), ImplGetLocaleDataWrapper())) |
850 | 0 | { |
851 | 0 | sal_Int64 nTempVal = ClipAgainstMinMax(rValue); |
852 | 0 | rOutStr = CreateFieldText( nTempVal ); |
853 | 0 | } |
854 | 0 | } |
855 | | |
856 | | void NumericBox::ReformatAll() |
857 | 0 | { |
858 | 0 | sal_Int64 nValue; |
859 | 0 | OUString aStr; |
860 | 0 | SetUpdateMode( false ); |
861 | 0 | sal_Int32 nEntryCount = GetEntryCount(); |
862 | 0 | for ( sal_Int32 i=0; i < nEntryCount; i++ ) |
863 | 0 | { |
864 | 0 | ImplNumericReformat( GetEntry( i ), nValue, aStr ); |
865 | 0 | RemoveEntryAt(i); |
866 | 0 | InsertEntry( aStr, i ); |
867 | 0 | } |
868 | 0 | NumericFormatter::Reformat(); |
869 | 0 | SetUpdateMode( true ); |
870 | 0 | } |
871 | | |
872 | | static OUString ImplMetricGetUnitText(std::u16string_view rStr) |
873 | 0 | { |
874 | | // fetch unit text |
875 | 0 | OUStringBuffer aStr; |
876 | 0 | for (sal_Int32 i = static_cast<sal_Int32>(rStr.size())-1; i >= 0; --i) |
877 | 0 | { |
878 | 0 | sal_Unicode c = rStr[i]; |
879 | 0 | if ( (c == '\'') || (c == '\"') || (c == '%') || (c == 0x2032) || (c == 0x2033) || unicode::isAlpha(c) || unicode::isControl(c) ) |
880 | 0 | aStr.insert(0, c); |
881 | 0 | else |
882 | 0 | { |
883 | 0 | if (!aStr.isEmpty()) |
884 | 0 | break; |
885 | 0 | } |
886 | 0 | } |
887 | 0 | return aStr.makeStringAndClear(); |
888 | 0 | } |
889 | | |
890 | | // #104355# support localized measurements |
891 | | |
892 | | static OUString ImplMetricToString( FieldUnit rUnit ) |
893 | 0 | { |
894 | | // return unit's default string (ie, the first one ) |
895 | 0 | for (auto const& elem : ImplGetFieldUnits()) |
896 | 0 | { |
897 | 0 | if (elem.second == rUnit) |
898 | 0 | return elem.first; |
899 | 0 | } |
900 | | |
901 | 0 | return OUString(); |
902 | 0 | } |
903 | | |
904 | | namespace |
905 | | { |
906 | | FieldUnit StringToMetric(const OUString &rMetricString) |
907 | 0 | { |
908 | | // return FieldUnit |
909 | 0 | OUString aStr = rMetricString.toAsciiLowerCase().replaceAll(" ", ""); |
910 | 0 | for (auto const& elem : ImplGetCleanedFieldUnits()) |
911 | 0 | { |
912 | 0 | if ( elem.first == aStr ) |
913 | 0 | return elem.second; |
914 | 0 | } |
915 | | |
916 | 0 | return FieldUnit::NONE; |
917 | 0 | } |
918 | | } |
919 | | |
920 | | static FieldUnit ImplMetricGetUnit(std::u16string_view rStr) |
921 | 0 | { |
922 | 0 | OUString aStr = ImplMetricGetUnitText(rStr); |
923 | 0 | return StringToMetric(aStr); |
924 | 0 | } |
925 | | |
926 | | static FieldUnit ImplMap2FieldUnit( MapUnit meUnit, tools::Long& nDecDigits ) |
927 | 0 | { |
928 | 0 | switch( meUnit ) |
929 | 0 | { |
930 | 0 | case MapUnit::Map100thMM : |
931 | 0 | nDecDigits -= 2; |
932 | 0 | return FieldUnit::MM; |
933 | 0 | case MapUnit::Map10thMM : |
934 | 0 | nDecDigits -= 1; |
935 | 0 | return FieldUnit::MM; |
936 | 0 | case MapUnit::MapMM : |
937 | 0 | return FieldUnit::MM; |
938 | 0 | case MapUnit::MapCM : |
939 | 0 | return FieldUnit::CM; |
940 | 0 | case MapUnit::Map1000thInch : |
941 | 0 | nDecDigits -= 3; |
942 | 0 | return FieldUnit::INCH; |
943 | 0 | case MapUnit::Map100thInch : |
944 | 0 | nDecDigits -= 2; |
945 | 0 | return FieldUnit::INCH; |
946 | 0 | case MapUnit::Map10thInch : |
947 | 0 | nDecDigits -= 1; |
948 | 0 | return FieldUnit::INCH; |
949 | 0 | case MapUnit::MapInch : |
950 | 0 | return FieldUnit::INCH; |
951 | 0 | case MapUnit::MapPoint : |
952 | 0 | return FieldUnit::POINT; |
953 | 0 | case MapUnit::MapTwip : |
954 | 0 | return FieldUnit::TWIP; |
955 | 0 | default: |
956 | 0 | OSL_FAIL( "default eInUnit" ); |
957 | 0 | break; |
958 | 0 | } |
959 | 0 | return FieldUnit::NONE; |
960 | 0 | } |
961 | | |
962 | | static double nonValueDoubleToValueDouble( double nValue ) |
963 | 0 | { |
964 | 0 | return std::isfinite( nValue ) ? nValue : 0.0; |
965 | 0 | } |
966 | | |
967 | | static double ConvertDoubleValue(double nValue, sal_Int64 mnBaseValue, sal_uInt16 nDecDigits, |
968 | | FieldUnit eInUnit, FieldUnit eOutUnit) |
969 | 0 | { |
970 | 0 | if ( eInUnit != eOutUnit ) |
971 | 0 | { |
972 | 0 | if (eInUnit == FieldUnit::PERCENT && mnBaseValue > 0 && nValue > 0) |
973 | 0 | { |
974 | 0 | sal_Int64 nDiv = 100 * ImplPower10(nDecDigits); |
975 | |
|
976 | 0 | if (mnBaseValue != 1) |
977 | 0 | nValue *= mnBaseValue; |
978 | |
|
979 | 0 | nValue += nDiv / 2; |
980 | 0 | nValue /= nDiv; |
981 | 0 | } |
982 | 0 | else |
983 | 0 | { |
984 | 0 | const o3tl::Length eFrom = FieldToO3tlLength(eInUnit, o3tl::Length::invalid); |
985 | 0 | const o3tl::Length eTo = FieldToO3tlLength(eOutUnit, o3tl::Length::invalid); |
986 | 0 | if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid) |
987 | 0 | nValue = o3tl::convert(nValue, eFrom, eTo); |
988 | 0 | } |
989 | 0 | } |
990 | |
|
991 | 0 | return nValue; |
992 | 0 | } |
993 | | |
994 | | namespace vcl |
995 | | { |
996 | | sal_Int64 ConvertValue(sal_Int64 nValue, sal_Int64 mnBaseValue, sal_uInt16 nDecDigits, |
997 | | FieldUnit eInUnit, FieldUnit eOutUnit) |
998 | 0 | { |
999 | 0 | double nDouble = nonValueDoubleToValueDouble(ConvertDoubleValue( |
1000 | 0 | static_cast<double>(nValue), mnBaseValue, nDecDigits, eInUnit, eOutUnit)); |
1001 | |
|
1002 | 0 | return clipDoubleAgainstMinMax(nDouble, SAL_MIN_INT64, SAL_MAX_INT64); |
1003 | 0 | } |
1004 | | } |
1005 | | |
1006 | | namespace { |
1007 | | |
1008 | | bool checkConversionUnits(FieldUnit eInUnit, FieldUnit eOutUnit) |
1009 | 0 | { |
1010 | 0 | return FieldToO3tlLength(eInUnit, o3tl::Length::invalid) != o3tl::Length::invalid |
1011 | 0 | && FieldToO3tlLength(eOutUnit, o3tl::Length::invalid) != o3tl::Length::invalid; |
1012 | 0 | } |
1013 | | |
1014 | | double convertValue( double nValue, tools::Long nDigits, FieldUnit eInUnit, FieldUnit eOutUnit ) |
1015 | 0 | { |
1016 | 0 | if ( nDigits < 0 ) |
1017 | 0 | { |
1018 | 0 | nValue /= ImplPower10(-nDigits); |
1019 | 0 | } |
1020 | 0 | else |
1021 | 0 | { |
1022 | 0 | nValue *= ImplPower10(nDigits); |
1023 | 0 | } |
1024 | |
|
1025 | 0 | if ( eInUnit != eOutUnit ) |
1026 | 0 | { |
1027 | 0 | const o3tl::Length eFrom = FieldToO3tlLength(eInUnit), eTo = FieldToO3tlLength(eOutUnit); |
1028 | 0 | if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid) |
1029 | 0 | nValue = o3tl::convert(nValue, eFrom, eTo); |
1030 | 0 | } |
1031 | |
|
1032 | 0 | return nValue; |
1033 | 0 | } |
1034 | | |
1035 | | } |
1036 | | |
1037 | | namespace vcl |
1038 | | { |
1039 | | // nValue is not scaled to nDecDigits; the result is scaled |
1040 | | sal_Int64 ConvertAndScaleValue( sal_Int64 nValue, sal_uInt16 nDigits, |
1041 | | MapUnit eInUnit, FieldUnit eOutUnit ) |
1042 | 0 | { |
1043 | 0 | tools::Long nDecDigits = nDigits; |
1044 | 0 | FieldUnit eFieldUnit = ImplMap2FieldUnit( eInUnit, nDecDigits ); |
1045 | |
|
1046 | 0 | if ( !checkConversionUnits(eFieldUnit, eOutUnit) ) |
1047 | 0 | { |
1048 | 0 | OSL_FAIL( "invalid parameters" ); |
1049 | 0 | return nValue; |
1050 | 0 | } |
1051 | | |
1052 | | // Avoid sal_Int64 <-> double conversion issues if possible: |
1053 | 0 | if (eFieldUnit == eOutUnit && nDecDigits == 0) |
1054 | 0 | { |
1055 | 0 | return nValue; |
1056 | 0 | } |
1057 | | |
1058 | 0 | return static_cast<sal_Int64>( |
1059 | 0 | nonValueDoubleToValueDouble( |
1060 | 0 | convertValue( nValue, nDecDigits, eFieldUnit, eOutUnit ) ) ); |
1061 | 0 | } |
1062 | | |
1063 | | // nValue is already scaled to nDecDigits; the result is unscaled |
1064 | | sal_Int64 ConvertAndUnscaleValue(sal_Int64 nValue, sal_uInt16 nDigits, FieldUnit eInUnit, |
1065 | | FieldUnit eOutUnit) |
1066 | 0 | { |
1067 | 0 | if (!checkConversionUnits(eInUnit, eOutUnit)) |
1068 | 0 | { |
1069 | 0 | SAL_WARN("vcl", "invalid parameters"); |
1070 | 0 | return nValue; |
1071 | 0 | } |
1072 | | |
1073 | | // Avoid sal_Int64 <-> double conversion issues if possible: |
1074 | 0 | if (eInUnit == eOutUnit && nDigits == 0) |
1075 | 0 | { |
1076 | 0 | return nValue; |
1077 | 0 | } |
1078 | | |
1079 | 0 | return static_cast<sal_Int64>( |
1080 | 0 | nonValueDoubleToValueDouble( |
1081 | 0 | convertValue( nValue, -static_cast<tools::Long>(nDigits), eInUnit, eOutUnit ) ) ); |
1082 | 0 | } |
1083 | | } |
1084 | | |
1085 | | namespace vcl |
1086 | | { |
1087 | | bool TextToValue(std::u16string_view rStr, double& rValue, sal_Int64 nBaseValue, |
1088 | | sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrapper, FieldUnit eUnit) |
1089 | 0 | { |
1090 | | // Get value |
1091 | 0 | double nValue; |
1092 | 0 | if (!ImplNumericGetDoubleValue(rStr, nValue, nDecDigits, rLocaleDataWrapper)) |
1093 | 0 | return false; |
1094 | | |
1095 | | // Determine unit |
1096 | 0 | FieldUnit eEntryUnit = ImplMetricGetUnit( rStr ); |
1097 | | |
1098 | | // Recalculate unit |
1099 | 0 | rValue = ConvertDoubleValue(nValue, nBaseValue, nDecDigits, eEntryUnit, eUnit); |
1100 | |
|
1101 | 0 | return true; |
1102 | 0 | } |
1103 | | |
1104 | 0 | FieldUnit GetTextMetricUnit(std::u16string_view aStr) { return ImplMetricGetUnit(aStr); } |
1105 | | } |
1106 | | |
1107 | | void MetricFormatter::ImplMetricReformat( std::u16string_view rStr, double& rValue, OUString& rOutStr ) |
1108 | 0 | { |
1109 | 0 | if (!vcl::TextToValue(rStr, rValue, 0, GetDecimalDigits(), ImplGetLocaleDataWrapper(), meUnit)) |
1110 | 0 | return; |
1111 | | |
1112 | 0 | rOutStr = CreateFieldText(ClipDoubleAgainstMinMax(rValue)); |
1113 | 0 | } |
1114 | | |
1115 | | MetricFormatter::MetricFormatter(Edit* pEdit) |
1116 | 0 | : NumericFormatter(pEdit) |
1117 | 0 | , meUnit(FieldUnit::NONE) |
1118 | 0 | { |
1119 | 0 | } |
1120 | | |
1121 | | MetricFormatter::~MetricFormatter() |
1122 | 0 | { |
1123 | 0 | } |
1124 | | |
1125 | | void MetricFormatter::SetUnit( FieldUnit eNewUnit ) |
1126 | 0 | { |
1127 | 0 | if (eNewUnit == FieldUnit::MM_100TH) |
1128 | 0 | { |
1129 | 0 | SetDecimalDigits( GetDecimalDigits() + 2 ); |
1130 | 0 | meUnit = FieldUnit::MM; |
1131 | 0 | } |
1132 | 0 | else |
1133 | 0 | meUnit = eNewUnit; |
1134 | 0 | ReformatAll(); |
1135 | 0 | } |
1136 | | |
1137 | | void MetricFormatter::SetCustomUnitText( const OUString& rStr ) |
1138 | 0 | { |
1139 | 0 | maCustomUnitText = rStr; |
1140 | 0 | ReformatAll(); |
1141 | 0 | } |
1142 | | |
1143 | | void MetricFormatter::SetValue( sal_Int64 nNewValue, FieldUnit eInUnit ) |
1144 | 0 | { |
1145 | 0 | SetUserValue( nNewValue, eInUnit ); |
1146 | 0 | } |
1147 | | |
1148 | | OUString MetricFormatter::CreateFieldText( sal_Int64 nValue ) const |
1149 | 0 | { |
1150 | | //whether percent is separated from its number is locale |
1151 | | //specific, pawn it off to icu to decide |
1152 | 0 | if (meUnit == FieldUnit::PERCENT) |
1153 | 0 | { |
1154 | 0 | double dValue = nValue; |
1155 | 0 | dValue /= ImplPower10(GetDecimalDigits()); |
1156 | 0 | return unicode::formatPercent(dValue, GetLanguageTag()); |
1157 | 0 | } |
1158 | | |
1159 | 0 | OUString aStr = NumericFormatter::CreateFieldText( nValue ); |
1160 | |
|
1161 | 0 | if( meUnit == FieldUnit::CUSTOM ) |
1162 | 0 | aStr += maCustomUnitText; |
1163 | 0 | else |
1164 | 0 | { |
1165 | 0 | OUString aSuffix = ImplMetricToString( meUnit ); |
1166 | 0 | if (meUnit != FieldUnit::NONE && meUnit != FieldUnit::DEGREE && meUnit != FieldUnit::INCH && meUnit != FieldUnit::FOOT) |
1167 | 0 | aStr += " "; |
1168 | 0 | if (meUnit == FieldUnit::INCH) |
1169 | 0 | { |
1170 | 0 | OUString sDoublePrime = u"\u2033"_ustr; |
1171 | 0 | if (aSuffix != "\"" && aSuffix != sDoublePrime) |
1172 | 0 | aStr += " "; |
1173 | 0 | else |
1174 | 0 | aSuffix = sDoublePrime; |
1175 | 0 | } |
1176 | 0 | else if (meUnit == FieldUnit::FOOT) |
1177 | 0 | { |
1178 | 0 | OUString sPrime = u"\u2032"_ustr; |
1179 | 0 | if (aSuffix != "'" && aSuffix != sPrime) |
1180 | 0 | aStr += " "; |
1181 | 0 | else |
1182 | 0 | aSuffix = sPrime; |
1183 | 0 | } |
1184 | |
|
1185 | 0 | assert(meUnit != FieldUnit::PERCENT); |
1186 | 0 | aStr += aSuffix; |
1187 | 0 | } |
1188 | 0 | return aStr; |
1189 | 0 | } |
1190 | | |
1191 | | void MetricFormatter::SetUserValue( sal_Int64 nNewValue, FieldUnit eInUnit ) |
1192 | 0 | { |
1193 | | // convert to previously configured units |
1194 | 0 | nNewValue = vcl::ConvertValue( nNewValue, 0, GetDecimalDigits(), eInUnit, meUnit ); |
1195 | 0 | NumericFormatter::SetUserValue( nNewValue ); |
1196 | 0 | } |
1197 | | |
1198 | | sal_Int64 MetricFormatter::GetValueFromStringUnit(std::u16string_view rStr, FieldUnit eOutUnit) const |
1199 | 0 | { |
1200 | 0 | double nTempValue; |
1201 | | // caution: precision loss in double cast |
1202 | 0 | if (!vcl::TextToValue(rStr, nTempValue, 0, GetDecimalDigits(), ImplGetLocaleDataWrapper(), meUnit)) |
1203 | 0 | nTempValue = static_cast<double>(mnLastValue); |
1204 | | |
1205 | | // convert to requested units |
1206 | 0 | return vcl::ConvertValue(ClipDoubleAgainstMinMax(nTempValue), 0, GetDecimalDigits(), meUnit, eOutUnit); |
1207 | 0 | } |
1208 | | |
1209 | | sal_Int64 MetricFormatter::GetValueFromString(std::u16string_view rStr) const |
1210 | 0 | { |
1211 | 0 | return GetValueFromStringUnit(rStr, FieldUnit::NONE); |
1212 | 0 | } |
1213 | | |
1214 | | sal_Int64 MetricFormatter::GetValue( FieldUnit eOutUnit ) const |
1215 | 0 | { |
1216 | 0 | return GetField() ? GetValueFromStringUnit(GetField()->GetText(), eOutUnit) : 0; |
1217 | 0 | } |
1218 | | |
1219 | | void MetricFormatter::SetValue( sal_Int64 nValue ) |
1220 | 0 | { |
1221 | | // Implementation not inline, because it is a virtual Function |
1222 | 0 | SetValue( nValue, FieldUnit::NONE ); |
1223 | 0 | } |
1224 | | |
1225 | | void MetricFormatter::SetMin( sal_Int64 nNewMin, FieldUnit eInUnit ) |
1226 | 0 | { |
1227 | | // convert to requested units |
1228 | 0 | NumericFormatter::SetMin(vcl::ConvertValue(nNewMin, 0, GetDecimalDigits(), eInUnit, meUnit)); |
1229 | 0 | } |
1230 | | |
1231 | | sal_Int64 MetricFormatter::GetMin( FieldUnit eOutUnit ) const |
1232 | 0 | { |
1233 | | // convert to requested units |
1234 | 0 | return vcl::ConvertValue(NumericFormatter::GetMin(), 0, GetDecimalDigits(), meUnit, eOutUnit); |
1235 | 0 | } |
1236 | | |
1237 | | void MetricFormatter::SetMax( sal_Int64 nNewMax, FieldUnit eInUnit ) |
1238 | 0 | { |
1239 | | // convert to requested units |
1240 | 0 | NumericFormatter::SetMax(vcl::ConvertValue(nNewMax, 0, GetDecimalDigits(), eInUnit, meUnit)); |
1241 | 0 | } |
1242 | | |
1243 | | sal_Int64 MetricFormatter::GetMax( FieldUnit eOutUnit ) const |
1244 | 0 | { |
1245 | | // convert to requested units |
1246 | 0 | return vcl::ConvertValue(NumericFormatter::GetMax(), 0, GetDecimalDigits(), meUnit, eOutUnit); |
1247 | 0 | } |
1248 | | |
1249 | | void MetricFormatter::Reformat() |
1250 | 0 | { |
1251 | 0 | if ( !GetField() ) |
1252 | 0 | return; |
1253 | | |
1254 | 0 | OUString aText = GetField()->GetText(); |
1255 | |
|
1256 | 0 | OUString aStr; |
1257 | | // caution: precision loss in double cast |
1258 | 0 | double nTemp = static_cast<double>(mnLastValue); |
1259 | 0 | ImplMetricReformat( aText, nTemp, aStr ); |
1260 | 0 | mnLastValue = static_cast<sal_Int64>(nTemp); |
1261 | |
|
1262 | 0 | if ( !aStr.isEmpty() ) |
1263 | 0 | { |
1264 | 0 | ImplSetText( aStr ); |
1265 | 0 | } |
1266 | 0 | else |
1267 | 0 | SetValue( mnLastValue ); |
1268 | 0 | } |
1269 | | |
1270 | | sal_Int64 MetricFormatter::GetCorrectedValue( FieldUnit eOutUnit ) const |
1271 | 0 | { |
1272 | | // convert to requested units |
1273 | 0 | return vcl::ConvertValue(0/*nCorrectedValue*/, 0, GetDecimalDigits(), |
1274 | 0 | meUnit, eOutUnit); |
1275 | 0 | } |
1276 | | |
1277 | | MetricField::MetricField(vcl::Window* pParent, WinBits nWinStyle) |
1278 | 0 | : SpinField(pParent, nWinStyle, WindowType::METRICFIELD) |
1279 | 0 | , MetricFormatter(this) |
1280 | 0 | { |
1281 | 0 | Reformat(); |
1282 | 0 | } Unexecuted instantiation: MetricField::MetricField(vcl::Window*, long) Unexecuted instantiation: MetricField::MetricField(vcl::Window*, long) |
1283 | | |
1284 | | void MetricField::dispose() |
1285 | 0 | { |
1286 | 0 | ClearField(); |
1287 | 0 | SpinField::dispose(); |
1288 | 0 | } |
1289 | | |
1290 | | Size MetricField::CalcMinimumSize() const |
1291 | 0 | { |
1292 | 0 | return calcMinimumSize(*this, *this); |
1293 | 0 | } |
1294 | | |
1295 | | bool MetricField::set_property(const OUString &rKey, const OUString &rValue) |
1296 | 0 | { |
1297 | 0 | if (rKey == "digits") |
1298 | 0 | SetDecimalDigits(rValue.toInt32()); |
1299 | 0 | else if (rKey == "spin-size") |
1300 | 0 | SetSpinSize(rValue.toInt32()); |
1301 | 0 | else |
1302 | 0 | return SpinField::set_property(rKey, rValue); |
1303 | 0 | return true; |
1304 | 0 | } |
1305 | | |
1306 | | void MetricField::SetUnit( FieldUnit nNewUnit ) |
1307 | 0 | { |
1308 | 0 | sal_Int64 nRawMax = GetMax( nNewUnit ); |
1309 | 0 | sal_Int64 nMax = Denormalize( nRawMax ); |
1310 | 0 | sal_Int64 nMin = Denormalize( GetMin( nNewUnit ) ); |
1311 | 0 | sal_Int64 nFirst = Denormalize( GetFirst( nNewUnit ) ); |
1312 | 0 | sal_Int64 nLast = Denormalize( GetLast( nNewUnit ) ); |
1313 | |
|
1314 | 0 | MetricFormatter::SetUnit( nNewUnit ); |
1315 | |
|
1316 | 0 | SetMax( Normalize( nMax ), nNewUnit ); |
1317 | 0 | SetMin( Normalize( nMin ), nNewUnit ); |
1318 | 0 | SetFirst( Normalize( nFirst ), nNewUnit ); |
1319 | 0 | SetLast( Normalize( nLast ), nNewUnit ); |
1320 | 0 | } |
1321 | | |
1322 | | void MetricField::SetFirst( sal_Int64 nNewFirst, FieldUnit eInUnit ) |
1323 | 0 | { |
1324 | | // convert |
1325 | 0 | nNewFirst = vcl::ConvertValue(nNewFirst, 0, GetDecimalDigits(), eInUnit, meUnit); |
1326 | 0 | mnFirst = nNewFirst; |
1327 | 0 | } |
1328 | | |
1329 | | sal_Int64 MetricField::GetFirst( FieldUnit eOutUnit ) const |
1330 | 0 | { |
1331 | | // convert |
1332 | 0 | return vcl::ConvertValue(mnFirst, 0, GetDecimalDigits(), meUnit, eOutUnit); |
1333 | 0 | } |
1334 | | |
1335 | | void MetricField::SetLast( sal_Int64 nNewLast, FieldUnit eInUnit ) |
1336 | 0 | { |
1337 | | // convert |
1338 | 0 | nNewLast = vcl::ConvertValue(nNewLast, 0, GetDecimalDigits(), eInUnit, meUnit); |
1339 | 0 | mnLast = nNewLast; |
1340 | 0 | } |
1341 | | |
1342 | | sal_Int64 MetricField::GetLast( FieldUnit eOutUnit ) const |
1343 | 0 | { |
1344 | | // convert |
1345 | 0 | return vcl::ConvertValue(mnLast, 0, GetDecimalDigits(), meUnit, eOutUnit); |
1346 | 0 | } |
1347 | | |
1348 | | bool MetricField::EventNotify( NotifyEvent& rNEvt ) |
1349 | 0 | { |
1350 | 0 | if ( rNEvt.GetType() == NotifyEventType::GETFOCUS ) |
1351 | 0 | MarkToBeReformatted( false ); |
1352 | 0 | else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS ) |
1353 | 0 | { |
1354 | 0 | if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) ) |
1355 | 0 | Reformat(); |
1356 | 0 | } |
1357 | |
|
1358 | 0 | return SpinField::EventNotify( rNEvt ); |
1359 | 0 | } |
1360 | | |
1361 | | void MetricField::DataChanged( const DataChangedEvent& rDCEvt ) |
1362 | 0 | { |
1363 | 0 | SpinField::DataChanged( rDCEvt ); |
1364 | |
|
1365 | 0 | if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) ) |
1366 | 0 | { |
1367 | 0 | OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep(); |
1368 | 0 | OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep(); |
1369 | 0 | ImplResetLocaleDataWrapper(); |
1370 | 0 | OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep(); |
1371 | 0 | OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep(); |
1372 | 0 | ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this ); |
1373 | 0 | ReformatAll(); |
1374 | 0 | } |
1375 | 0 | } |
1376 | | |
1377 | | void MetricField::Modify() |
1378 | 0 | { |
1379 | 0 | MarkToBeReformatted( true ); |
1380 | 0 | SpinField::Modify(); |
1381 | 0 | } |
1382 | | |
1383 | | void MetricField::Up() |
1384 | 0 | { |
1385 | 0 | FieldUp(); |
1386 | 0 | SpinField::Up(); |
1387 | 0 | } |
1388 | | |
1389 | | void MetricField::Down() |
1390 | 0 | { |
1391 | 0 | FieldDown(); |
1392 | 0 | SpinField::Down(); |
1393 | 0 | } |
1394 | | |
1395 | | void MetricField::First() |
1396 | 0 | { |
1397 | 0 | FieldFirst(); |
1398 | 0 | SpinField::First(); |
1399 | 0 | } |
1400 | | |
1401 | | void MetricField::Last() |
1402 | 0 | { |
1403 | 0 | FieldLast(); |
1404 | 0 | SpinField::Last(); |
1405 | 0 | } |
1406 | | |
1407 | | FactoryFunction MetricField::GetUITestFactory() const |
1408 | 0 | { |
1409 | 0 | return MetricFieldUIObject::create; |
1410 | 0 | } |
1411 | | |
1412 | | MetricBox::MetricBox(vcl::Window* pParent, WinBits nWinStyle) |
1413 | 0 | : ComboBox(pParent, nWinStyle) |
1414 | 0 | , MetricFormatter(this) |
1415 | 0 | { |
1416 | 0 | Reformat(); |
1417 | 0 | } Unexecuted instantiation: MetricBox::MetricBox(vcl::Window*, long) Unexecuted instantiation: MetricBox::MetricBox(vcl::Window*, long) |
1418 | | |
1419 | | void MetricBox::dispose() |
1420 | 0 | { |
1421 | 0 | ClearField(); |
1422 | 0 | ComboBox::dispose(); |
1423 | 0 | } |
1424 | | |
1425 | | Size MetricBox::CalcMinimumSize() const |
1426 | 0 | { |
1427 | 0 | Size aRet(calcMinimumSize(*this, *this)); |
1428 | |
|
1429 | 0 | if (IsDropDownBox()) |
1430 | 0 | { |
1431 | 0 | Size aComboSugg(ComboBox::CalcMinimumSize()); |
1432 | 0 | aRet.setWidth( std::max(aRet.Width(), aComboSugg.Width()) ); |
1433 | 0 | aRet.setHeight( std::max(aRet.Height(), aComboSugg.Height()) ); |
1434 | 0 | } |
1435 | |
|
1436 | 0 | return aRet; |
1437 | 0 | } |
1438 | | |
1439 | | bool MetricBox::EventNotify( NotifyEvent& rNEvt ) |
1440 | 0 | { |
1441 | 0 | if ( rNEvt.GetType() == NotifyEventType::GETFOCUS ) |
1442 | 0 | MarkToBeReformatted( false ); |
1443 | 0 | else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS ) |
1444 | 0 | { |
1445 | 0 | if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) ) |
1446 | 0 | Reformat(); |
1447 | 0 | } |
1448 | |
|
1449 | 0 | return ComboBox::EventNotify( rNEvt ); |
1450 | 0 | } |
1451 | | |
1452 | | void MetricBox::DataChanged( const DataChangedEvent& rDCEvt ) |
1453 | 0 | { |
1454 | 0 | ComboBox::DataChanged( rDCEvt ); |
1455 | |
|
1456 | 0 | if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) ) |
1457 | 0 | { |
1458 | 0 | OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep(); |
1459 | 0 | OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep(); |
1460 | 0 | ImplResetLocaleDataWrapper(); |
1461 | 0 | OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep(); |
1462 | 0 | OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep(); |
1463 | 0 | ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this ); |
1464 | 0 | ReformatAll(); |
1465 | 0 | } |
1466 | 0 | } |
1467 | | |
1468 | | void MetricBox::Modify() |
1469 | 0 | { |
1470 | 0 | MarkToBeReformatted( true ); |
1471 | 0 | ComboBox::Modify(); |
1472 | 0 | } |
1473 | | |
1474 | | void MetricBox::ReformatAll() |
1475 | 0 | { |
1476 | 0 | double nValue; |
1477 | 0 | OUString aStr; |
1478 | 0 | SetUpdateMode( false ); |
1479 | 0 | sal_Int32 nEntryCount = GetEntryCount(); |
1480 | 0 | for ( sal_Int32 i=0; i < nEntryCount; i++ ) |
1481 | 0 | { |
1482 | 0 | ImplMetricReformat( GetEntry( i ), nValue, aStr ); |
1483 | 0 | RemoveEntryAt(i); |
1484 | 0 | InsertEntry( aStr, i ); |
1485 | 0 | } |
1486 | 0 | MetricFormatter::Reformat(); |
1487 | 0 | SetUpdateMode( true ); |
1488 | 0 | } |
1489 | | |
1490 | | static bool ImplCurrencyGetValue( std::u16string_view rStr, sal_Int64& rValue, |
1491 | | sal_uInt16 nDecDigits, const LocaleDataWrapper& rWrapper ) |
1492 | 0 | { |
1493 | | // fetch number |
1494 | 0 | return ImplNumericGetValue( rStr, rValue, nDecDigits, rWrapper, true ); |
1495 | 0 | } |
1496 | | |
1497 | | void CurrencyFormatter::ImplCurrencyReformat(std::u16string_view rStr, OUString& rOutStr) |
1498 | 0 | { |
1499 | 0 | sal_Int64 nValue; |
1500 | 0 | if ( !ImplNumericGetValue( rStr, nValue, GetDecimalDigits(), ImplGetLocaleDataWrapper(), true ) ) |
1501 | 0 | return; |
1502 | 0 | rOutStr = CreateFieldText(ClipAgainstMinMax(nValue)); |
1503 | 0 | } |
1504 | | |
1505 | | CurrencyFormatter::CurrencyFormatter(Edit* pField) |
1506 | 0 | : NumericFormatter(pField) |
1507 | 0 | { |
1508 | 0 | } |
1509 | | |
1510 | | CurrencyFormatter::~CurrencyFormatter() |
1511 | | { |
1512 | | } |
1513 | | |
1514 | | void CurrencyFormatter::SetValue( sal_Int64 nNewValue ) |
1515 | 0 | { |
1516 | 0 | SetUserValue( nNewValue ); |
1517 | 0 | SetEmptyFieldValueData( false ); |
1518 | 0 | } |
1519 | | |
1520 | | OUString CurrencyFormatter::CreateFieldText( sal_Int64 nValue ) const |
1521 | 0 | { |
1522 | 0 | return ImplGetLocaleDataWrapper().getCurr( nValue, GetDecimalDigits(), |
1523 | 0 | ImplGetLocaleDataWrapper().getCurrSymbol(), |
1524 | 0 | IsUseThousandSep() ); |
1525 | 0 | } |
1526 | | |
1527 | | sal_Int64 CurrencyFormatter::GetValueFromString(std::u16string_view rStr) const |
1528 | 0 | { |
1529 | 0 | sal_Int64 nTempValue; |
1530 | 0 | if ( ImplCurrencyGetValue( rStr, nTempValue, GetDecimalDigits(), ImplGetLocaleDataWrapper() ) ) |
1531 | 0 | { |
1532 | 0 | return ClipAgainstMinMax(nTempValue); |
1533 | 0 | } |
1534 | 0 | else |
1535 | 0 | return mnLastValue; |
1536 | 0 | } |
1537 | | |
1538 | | void CurrencyFormatter::Reformat() |
1539 | 0 | { |
1540 | 0 | if ( !GetField() ) |
1541 | 0 | return; |
1542 | | |
1543 | 0 | OUString aStr; |
1544 | 0 | ImplCurrencyReformat( GetField()->GetText(), aStr ); |
1545 | |
|
1546 | 0 | if ( !aStr.isEmpty() ) |
1547 | 0 | { |
1548 | 0 | ImplSetText( aStr ); |
1549 | 0 | sal_Int64 nTemp = mnLastValue; |
1550 | 0 | ImplCurrencyGetValue( aStr, nTemp, GetDecimalDigits(), ImplGetLocaleDataWrapper() ); |
1551 | 0 | mnLastValue = nTemp; |
1552 | 0 | } |
1553 | 0 | else |
1554 | 0 | SetValue( mnLastValue ); |
1555 | 0 | } |
1556 | | |
1557 | | CurrencyField::CurrencyField(vcl::Window* pParent, WinBits nWinStyle) |
1558 | 0 | : SpinField(pParent, nWinStyle) |
1559 | 0 | , CurrencyFormatter(this) |
1560 | 0 | { |
1561 | 0 | Reformat(); |
1562 | 0 | } Unexecuted instantiation: CurrencyField::CurrencyField(vcl::Window*, long) Unexecuted instantiation: CurrencyField::CurrencyField(vcl::Window*, long) |
1563 | | |
1564 | | void CurrencyField::dispose() |
1565 | 0 | { |
1566 | 0 | ClearField(); |
1567 | 0 | SpinField::dispose(); |
1568 | 0 | } |
1569 | | |
1570 | | bool CurrencyField::EventNotify( NotifyEvent& rNEvt ) |
1571 | 0 | { |
1572 | 0 | if ( rNEvt.GetType() == NotifyEventType::GETFOCUS ) |
1573 | 0 | MarkToBeReformatted( false ); |
1574 | 0 | else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS ) |
1575 | 0 | { |
1576 | 0 | if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) ) |
1577 | 0 | Reformat(); |
1578 | 0 | } |
1579 | |
|
1580 | 0 | return SpinField::EventNotify( rNEvt ); |
1581 | 0 | } |
1582 | | |
1583 | | void CurrencyField::DataChanged( const DataChangedEvent& rDCEvt ) |
1584 | 0 | { |
1585 | 0 | SpinField::DataChanged( rDCEvt ); |
1586 | |
|
1587 | 0 | if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) ) |
1588 | 0 | { |
1589 | 0 | OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep(); |
1590 | 0 | OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep(); |
1591 | 0 | ImplResetLocaleDataWrapper(); |
1592 | 0 | OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep(); |
1593 | 0 | OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep(); |
1594 | 0 | ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this ); |
1595 | 0 | ReformatAll(); |
1596 | 0 | } |
1597 | 0 | } |
1598 | | |
1599 | | void CurrencyField::Modify() |
1600 | 0 | { |
1601 | 0 | MarkToBeReformatted( true ); |
1602 | 0 | SpinField::Modify(); |
1603 | 0 | } |
1604 | | |
1605 | | void CurrencyField::Up() |
1606 | 0 | { |
1607 | 0 | FieldUp(); |
1608 | 0 | SpinField::Up(); |
1609 | 0 | } |
1610 | | |
1611 | | void CurrencyField::Down() |
1612 | 0 | { |
1613 | 0 | FieldDown(); |
1614 | 0 | SpinField::Down(); |
1615 | 0 | } |
1616 | | |
1617 | | void CurrencyField::First() |
1618 | 0 | { |
1619 | 0 | FieldFirst(); |
1620 | 0 | SpinField::First(); |
1621 | 0 | } |
1622 | | |
1623 | | void CurrencyField::Last() |
1624 | 0 | { |
1625 | 0 | FieldLast(); |
1626 | 0 | SpinField::Last(); |
1627 | 0 | } |
1628 | | |
1629 | | CurrencyBox::CurrencyBox(vcl::Window* pParent, WinBits nWinStyle) |
1630 | 0 | : ComboBox(pParent, nWinStyle) |
1631 | 0 | , CurrencyFormatter(this) |
1632 | 0 | { |
1633 | 0 | Reformat(); |
1634 | 0 | } Unexecuted instantiation: CurrencyBox::CurrencyBox(vcl::Window*, long) Unexecuted instantiation: CurrencyBox::CurrencyBox(vcl::Window*, long) |
1635 | | |
1636 | | void CurrencyBox::dispose() |
1637 | 0 | { |
1638 | 0 | ClearField(); |
1639 | 0 | ComboBox::dispose(); |
1640 | 0 | } |
1641 | | |
1642 | | bool CurrencyBox::EventNotify( NotifyEvent& rNEvt ) |
1643 | 0 | { |
1644 | 0 | if ( rNEvt.GetType() == NotifyEventType::GETFOCUS ) |
1645 | 0 | MarkToBeReformatted( false ); |
1646 | 0 | else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS ) |
1647 | 0 | { |
1648 | 0 | if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) ) |
1649 | 0 | Reformat(); |
1650 | 0 | } |
1651 | |
|
1652 | 0 | return ComboBox::EventNotify( rNEvt ); |
1653 | 0 | } |
1654 | | |
1655 | | void CurrencyBox::DataChanged( const DataChangedEvent& rDCEvt ) |
1656 | 0 | { |
1657 | 0 | ComboBox::DataChanged( rDCEvt ); |
1658 | |
|
1659 | 0 | if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) ) |
1660 | 0 | { |
1661 | 0 | OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep(); |
1662 | 0 | OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep(); |
1663 | 0 | ImplResetLocaleDataWrapper(); |
1664 | 0 | OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep(); |
1665 | 0 | OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep(); |
1666 | 0 | ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this ); |
1667 | 0 | ReformatAll(); |
1668 | 0 | } |
1669 | 0 | } |
1670 | | |
1671 | | void CurrencyBox::Modify() |
1672 | 0 | { |
1673 | 0 | MarkToBeReformatted( true ); |
1674 | 0 | ComboBox::Modify(); |
1675 | 0 | } |
1676 | | |
1677 | | void CurrencyBox::ReformatAll() |
1678 | 0 | { |
1679 | 0 | OUString aStr; |
1680 | 0 | SetUpdateMode( false ); |
1681 | 0 | sal_Int32 nEntryCount = GetEntryCount(); |
1682 | 0 | for ( sal_Int32 i=0; i < nEntryCount; i++ ) |
1683 | 0 | { |
1684 | 0 | ImplCurrencyReformat( GetEntry( i ), aStr ); |
1685 | 0 | RemoveEntryAt(i); |
1686 | 0 | InsertEntry( aStr, i ); |
1687 | 0 | } |
1688 | 0 | CurrencyFormatter::Reformat(); |
1689 | 0 | SetUpdateMode( true ); |
1690 | 0 | } |
1691 | | |
1692 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |