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