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