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 <algorithm>
23 #include <string_view>
24
25 #include <tools/diagnose_ex.h>
26 #include <comphelper/processfactory.hxx>
27 #include <comphelper/string.hxx>
28 #include <officecfg/Office/Common.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/event.hxx>
31 #include <vcl/toolkit/field.hxx>
32 #include <vcl/unohelp.hxx>
33 #include <vcl/settings.hxx>
34 #include <vcl/weldutils.hxx>
35
36 #include <svdata.hxx>
37
38 #include <com/sun/star/i18n/XCharacterClassification.hpp>
39 #include <com/sun/star/i18n/CalendarFieldIndex.hdl>
40
41 #include <unotools/localedatawrapper.hxx>
42 #include <unotools/calendarwrapper.hxx>
43 #include <unotools/charclass.hxx>
44 #include <svl/zforlist.hxx>
45
46 using namespace ::com::sun::star;
47 using namespace ::comphelper;
48
49 #define EDITMASK_LITERAL 'L'
50 #define EDITMASK_ALPHA 'a'
51 #define EDITMASK_UPPERALPHA 'A'
52 #define EDITMASK_ALPHANUM 'c'
53 #define EDITMASK_UPPERALPHANUM 'C'
54 #define EDITMASK_NUM 'N'
55 #define EDITMASK_NUMSPACE 'n'
56 #define EDITMASK_ALLCHAR 'x'
57 #define EDITMASK_UPPERALLCHAR 'X'
58
ImplGetCharClass()59 uno::Reference< i18n::XCharacterClassification > const & ImplGetCharClass()
60 {
61 ImplSVData *const pSVData = ImplGetSVData();
62 assert(pSVData);
63
64 if (!pSVData->m_xCharClass.is())
65 {
66 pSVData->m_xCharClass = vcl::unohelper::CreateCharacterClassification();
67 }
68
69 return pSVData->m_xCharClass;
70 }
71
ImplAddString(sal_Unicode * pBuf,const OUString & rStr)72 static sal_Unicode* ImplAddString( sal_Unicode* pBuf, const OUString& rStr )
73 {
74 memcpy( pBuf, rStr.getStr(), rStr.getLength() * sizeof(sal_Unicode) );
75 pBuf += rStr.getLength();
76 return pBuf;
77 }
78
ImplAddNum(sal_Unicode * pBuf,sal_uLong nNumber,int nMinLen)79 static sal_Unicode* ImplAddNum( sal_Unicode* pBuf, sal_uLong nNumber, int nMinLen )
80 {
81 // fill temp buffer with digits
82 sal_Unicode aTempBuf[30];
83 sal_Unicode* pTempBuf = aTempBuf;
84 do
85 {
86 *pTempBuf = static_cast<sal_Unicode>(nNumber % 10) + '0';
87 pTempBuf++;
88 nNumber /= 10;
89 if ( nMinLen )
90 nMinLen--;
91 }
92 while ( nNumber );
93
94 // fill with zeros up to the minimal length
95 while ( nMinLen > 0 )
96 {
97 *pBuf = '0';
98 pBuf++;
99 nMinLen--;
100 }
101
102 // copy temp buffer to real buffer
103 do
104 {
105 pTempBuf--;
106 *pBuf = *pTempBuf;
107 pBuf++;
108 }
109 while ( pTempBuf != aTempBuf );
110
111 return pBuf;
112 }
113
ImplAddSNum(sal_Unicode * pBuf,sal_Int32 nNumber,int nMinLen)114 static sal_Unicode* ImplAddSNum( sal_Unicode* pBuf, sal_Int32 nNumber, int nMinLen )
115 {
116 if (nNumber < 0)
117 {
118 *pBuf++ = '-';
119 nNumber = -nNumber;
120 }
121 return ImplAddNum( pBuf, nNumber, nMinLen);
122 }
123
ImplGetNum(const sal_Unicode * & rpBuf,bool & rbError)124 static sal_uInt16 ImplGetNum( const sal_Unicode*& rpBuf, bool& rbError )
125 {
126 if ( !*rpBuf )
127 {
128 rbError = true;
129 return 0;
130 }
131
132 sal_uInt16 nNumber = 0;
133 while( ( *rpBuf >= '0' ) && ( *rpBuf <= '9' ) )
134 {
135 nNumber *= 10;
136 nNumber += *rpBuf - '0';
137 rpBuf++;
138 }
139
140 return nNumber;
141 }
142
ImplSkipDelimiters(const sal_Unicode * & rpBuf)143 static void ImplSkipDelimiters( const sal_Unicode*& rpBuf )
144 {
145 while( ( *rpBuf == ',' ) || ( *rpBuf == '.' ) || ( *rpBuf == ';' ) ||
146 ( *rpBuf == ':' ) || ( *rpBuf == '-' ) || ( *rpBuf == '/' ) )
147 {
148 rpBuf++;
149 }
150 }
151
ImplIsPatternChar(sal_Unicode cChar,char cEditMask)152 static bool ImplIsPatternChar( sal_Unicode cChar, char cEditMask )
153 {
154 sal_Int32 nType = 0;
155
156 try
157 {
158 OUString aCharStr(cChar);
159 nType = ImplGetCharClass()->getStringType( aCharStr, 0, aCharStr.getLength(),
160 Application::GetSettings().GetLanguageTag().getLocale() );
161 }
162 catch (const css::uno::Exception&)
163 {
164 DBG_UNHANDLED_EXCEPTION("vcl.control");
165 return false;
166 }
167
168 if ( (cEditMask == EDITMASK_ALPHA) || (cEditMask == EDITMASK_UPPERALPHA) )
169 {
170 if( !CharClass::isLetterType( nType ) )
171 return false;
172 }
173 else if ( cEditMask == EDITMASK_NUM )
174 {
175 if( !CharClass::isNumericType( nType ) )
176 return false;
177 }
178 else if ( (cEditMask == EDITMASK_ALPHANUM) || (cEditMask == EDITMASK_UPPERALPHANUM) )
179 {
180 if( !CharClass::isLetterNumericType( nType ) )
181 return false;
182 }
183 else if ( (cEditMask == EDITMASK_ALLCHAR) || (cEditMask == EDITMASK_UPPERALLCHAR) )
184 {
185 if ( cChar < 32 )
186 return false;
187 }
188 else if ( cEditMask == EDITMASK_NUMSPACE )
189 {
190 if ( !CharClass::isNumericType( nType ) && ( cChar != ' ' ) )
191 return false;
192 }
193 else
194 return false;
195
196 return true;
197 }
198
ImplPatternChar(sal_Unicode cChar,char cEditMask)199 static sal_Unicode ImplPatternChar( sal_Unicode cChar, char cEditMask )
200 {
201 if ( ImplIsPatternChar( cChar, cEditMask ) )
202 {
203 if ( (cEditMask == EDITMASK_UPPERALPHA) ||
204 (cEditMask == EDITMASK_UPPERALPHANUM) ||
205 ( cEditMask == EDITMASK_UPPERALLCHAR ) )
206 {
207 cChar = ImplGetCharClass()->toUpper(OUString(cChar), 0, 1,
208 Application::GetSettings().GetLanguageTag().getLocale())[0];
209 }
210 return cChar;
211 }
212 else
213 return 0;
214 }
215
ImplCommaPointCharEqual(sal_Unicode c1,sal_Unicode c2)216 static bool ImplCommaPointCharEqual( sal_Unicode c1, sal_Unicode c2 )
217 {
218 if ( c1 == c2 )
219 return true;
220 else if ( ((c1 == '.') || (c1 == ',')) &&
221 ((c2 == '.') || (c2 == ',')) )
222 return true;
223 else
224 return false;
225 }
226
ImplPatternReformat(const OUString & rStr,const OString & rEditMask,const OUString & rLiteralMask,sal_uInt16 nFormatFlags)227 static OUString ImplPatternReformat( const OUString& rStr,
228 const OString& rEditMask,
229 const OUString& rLiteralMask,
230 sal_uInt16 nFormatFlags )
231 {
232 if (rEditMask.isEmpty())
233 return rStr;
234
235 OUStringBuffer aOutStr = rLiteralMask;
236 sal_Unicode cTempChar;
237 sal_Unicode cChar;
238 sal_Unicode cLiteral;
239 char cMask;
240 sal_Int32 nStrIndex = 0;
241 sal_Int32 i = 0;
242 sal_Int32 n;
243
244 while ( i < rEditMask.getLength() )
245 {
246 if ( nStrIndex >= rStr.getLength() )
247 break;
248
249 cChar = rStr[nStrIndex];
250 cLiteral = rLiteralMask[i];
251 cMask = rEditMask[i];
252
253 // current position is a literal
254 if ( cMask == EDITMASK_LITERAL )
255 {
256 // if it is a literal copy otherwise ignore because it might be the next valid
257 // character of the string
258 if ( ImplCommaPointCharEqual( cChar, cLiteral ) )
259 nStrIndex++;
260 else
261 {
262 // Otherwise we check if it is an invalid character. This is the case if it does not
263 // fit in the pattern of the next non-literal character.
264 n = i+1;
265 while ( n < rEditMask.getLength() )
266 {
267 if ( rEditMask[n] != EDITMASK_LITERAL )
268 {
269 if ( !ImplIsPatternChar( cChar, rEditMask[n] ) )
270 nStrIndex++;
271 break;
272 }
273
274 n++;
275 }
276 }
277 }
278 else
279 {
280 // valid character at this position
281 cTempChar = ImplPatternChar( cChar, cMask );
282 if ( cTempChar )
283 {
284 // use this character
285 aOutStr[i] = cTempChar;
286 nStrIndex++;
287 }
288 else
289 {
290 // copy if it is a literal character
291 if ( cLiteral == cChar )
292 nStrIndex++;
293 else
294 {
295 // If the invalid character might be the next literal character then we jump
296 // ahead to it, otherwise we ignore it. Do only if empty literals are allowed.
297 if ( nFormatFlags & PATTERN_FORMAT_EMPTYLITERALS )
298 {
299 n = i;
300 while ( n < rEditMask.getLength() )
301 {
302 if ( rEditMask[n] == EDITMASK_LITERAL )
303 {
304 if ( ImplCommaPointCharEqual( cChar, rLiteralMask[n] ) )
305 i = n+1;
306
307 break;
308 }
309
310 n++;
311 }
312 }
313
314 nStrIndex++;
315 continue;
316 }
317 }
318 }
319
320 i++;
321 }
322
323 return aOutStr.makeStringAndClear();
324 }
325
ImplPatternMaxPos(const OUString & rStr,const OString & rEditMask,sal_uInt16 nFormatFlags,bool bSameMask,sal_Int32 nCursorPos,sal_Int32 & rPos)326 static void ImplPatternMaxPos( const OUString& rStr, const OString& rEditMask,
327 sal_uInt16 nFormatFlags, bool bSameMask,
328 sal_Int32 nCursorPos, sal_Int32& rPos )
329 {
330
331 // last position must not be longer than the contained string
332 sal_Int32 nMaxPos = rStr.getLength();
333
334 // if non empty literals are allowed ignore blanks at the end as well
335 if ( bSameMask && !(nFormatFlags & PATTERN_FORMAT_EMPTYLITERALS) )
336 {
337 while ( nMaxPos )
338 {
339 if ( (rEditMask[nMaxPos-1] != EDITMASK_LITERAL) &&
340 (rStr[nMaxPos-1] != ' ') )
341 break;
342 nMaxPos--;
343 }
344
345 // if we are in front of a literal, continue search until first character after the literal
346 sal_Int32 nTempPos = nMaxPos;
347 while ( nTempPos < rEditMask.getLength() )
348 {
349 if ( rEditMask[nTempPos] != EDITMASK_LITERAL )
350 {
351 nMaxPos = nTempPos;
352 break;
353 }
354 nTempPos++;
355 }
356 }
357
358 if ( rPos > nMaxPos )
359 rPos = nMaxPos;
360
361 // character should not move left
362 if ( rPos < nCursorPos )
363 rPos = nCursorPos;
364 }
365
ImplPatternProcessStrictModify(const OUString & rText,const OString & rEditMask,const OUString & rLiteralMask,bool bSameMask)366 static OUString ImplPatternProcessStrictModify(const OUString& rText,
367 const OString& rEditMask,
368 const OUString& rLiteralMask,
369 bool bSameMask)
370 {
371 OUString aText(rText);
372
373 // remove leading blanks
374 if (bSameMask && !rEditMask.isEmpty())
375 {
376 sal_Int32 i = 0;
377 sal_Int32 nMaxLen = aText.getLength();
378 while ( i < nMaxLen )
379 {
380 if ( (rEditMask[i] != EDITMASK_LITERAL) &&
381 (aText[i] != ' ') )
382 break;
383
384 i++;
385 }
386 // keep all literal characters
387 while ( i && (rEditMask[i] == EDITMASK_LITERAL) )
388 i--;
389 aText = aText.copy( i );
390 }
391
392 return ImplPatternReformat(aText, rEditMask, rLiteralMask, 0);
393 }
394
ImplPatternProcessStrictModify(Edit * pEdit,const OString & rEditMask,const OUString & rLiteralMask,bool bSameMask)395 static void ImplPatternProcessStrictModify( Edit* pEdit,
396 const OString& rEditMask,
397 const OUString& rLiteralMask,
398 bool bSameMask )
399 {
400 OUString aText = pEdit->GetText();
401 OUString aNewText = ImplPatternProcessStrictModify(aText,
402 rEditMask,
403 rLiteralMask,
404 bSameMask);
405
406 if ( aNewText == aText )
407 return;
408
409 // adjust selection such that it remains at the end if it was there before
410 Selection aSel = pEdit->GetSelection();
411 sal_Int64 nMaxSel = std::max( aSel.Min(), aSel.Max() );
412 if ( nMaxSel >= aText.getLength() )
413 {
414 sal_Int32 nMaxPos = aNewText.getLength();
415 ImplPatternMaxPos(aNewText, rEditMask, 0, bSameMask, nMaxSel, nMaxPos);
416 if ( aSel.Min() == aSel.Max() )
417 {
418 aSel.Min() = nMaxPos;
419 aSel.Max() = aSel.Min();
420 }
421 else if ( aSel.Min() > aSel.Max() )
422 aSel.Min() = nMaxPos;
423 else
424 aSel.Max() = nMaxPos;
425 }
426 pEdit->SetText( aNewText, aSel );
427 }
428
ImplPatternProcessStrictModify(weld::Entry & rEntry,const OString & rEditMask,const OUString & rLiteralMask,bool bSameMask)429 static void ImplPatternProcessStrictModify( weld::Entry& rEntry,
430 const OString& rEditMask,
431 const OUString& rLiteralMask,
432 bool bSameMask )
433 {
434 OUString aText = rEntry.get_text();
435 OUString aNewText = ImplPatternProcessStrictModify(aText,
436 rEditMask,
437 rLiteralMask,
438 bSameMask);
439
440 if (aNewText == aText)
441 return;
442
443 // adjust selection such that it remains at the end if it was there before
444 int nStartPos, nEndPos;
445 rEntry.get_selection_bounds(nStartPos, nEndPos);
446
447 int nMaxSel = std::max(nStartPos, nEndPos);
448 if (nMaxSel >= aText.getLength())
449 {
450 sal_Int32 nMaxPos = aNewText.getLength();
451 ImplPatternMaxPos(aNewText, rEditMask, 0, bSameMask, nMaxSel, nMaxPos);
452 if (nStartPos == nEndPos)
453 {
454 nStartPos = nMaxPos;
455 nEndPos = nMaxPos;
456 }
457 else if (nStartPos > nMaxPos)
458 nStartPos = nMaxPos;
459 else
460 nEndPos = nMaxPos;
461 }
462 rEntry.set_text(aNewText);
463 rEntry.select_region(nStartPos, nEndPos);
464 }
465
ImplPatternLeftPos(std::string_view rEditMask,sal_Int32 nCursorPos)466 static sal_Int32 ImplPatternLeftPos(std::string_view rEditMask, sal_Int32 nCursorPos)
467 {
468 // search non-literal predecessor
469 sal_Int32 nNewPos = nCursorPos;
470 sal_Int32 nTempPos = nNewPos;
471 while ( nTempPos )
472 {
473 if ( rEditMask[nTempPos-1] != EDITMASK_LITERAL )
474 {
475 nNewPos = nTempPos-1;
476 break;
477 }
478 nTempPos--;
479 }
480 return nNewPos;
481 }
482
ImplPatternRightPos(const OUString & rStr,const OString & rEditMask,sal_uInt16 nFormatFlags,bool bSameMask,sal_Int32 nCursorPos)483 static sal_Int32 ImplPatternRightPos( const OUString& rStr, const OString& rEditMask,
484 sal_uInt16 nFormatFlags, bool bSameMask,
485 sal_Int32 nCursorPos )
486 {
487 // search non-literal successor
488 sal_Int32 nNewPos = nCursorPos;
489 ;
490 for(sal_Int32 nTempPos = nNewPos+1; nTempPos < rEditMask.getLength(); ++nTempPos )
491 {
492 if ( rEditMask[nTempPos] != EDITMASK_LITERAL )
493 {
494 nNewPos = nTempPos;
495 break;
496 }
497 }
498 ImplPatternMaxPos( rStr, rEditMask, nFormatFlags, bSameMask, nCursorPos, nNewPos );
499 return nNewPos;
500 }
501
502 namespace
503 {
504 class IEditImplementation
505 {
506 public:
~IEditImplementation()507 virtual ~IEditImplementation() {}
508
509 virtual OUString GetText() const = 0;
510 virtual void SetText(const OUString& rStr, const Selection& rSelection) = 0;
511
512 virtual Selection GetSelection() const = 0;
513 virtual void SetSelection(const Selection& rSelection) = 0;
514
515 virtual bool IsInsertMode() const = 0;
516
517 virtual void SetModified() = 0;
518 };
519 }
520
ImplPatternProcessKeyInput(IEditImplementation & rEdit,const KeyEvent & rKEvt,const OString & rEditMask,const OUString & rLiteralMask,bool bStrictFormat,bool bSameMask,bool & rbInKeyInput)521 static bool ImplPatternProcessKeyInput( IEditImplementation& rEdit, const KeyEvent& rKEvt,
522 const OString& rEditMask,
523 const OUString& rLiteralMask,
524 bool bStrictFormat,
525 bool bSameMask,
526 bool& rbInKeyInput )
527 {
528 if ( rEditMask.isEmpty() || !bStrictFormat )
529 return false;
530
531 sal_uInt16 nFormatFlags = 0;
532 Selection aOldSel = rEdit.GetSelection();
533 vcl::KeyCode aCode = rKEvt.GetKeyCode();
534 sal_Unicode cChar = rKEvt.GetCharCode();
535 sal_uInt16 nKeyCode = aCode.GetCode();
536 bool bShift = aCode.IsShift();
537 sal_Int32 nCursorPos = static_cast<sal_Int32>(aOldSel.Max());
538 sal_Int32 nNewPos;
539 sal_Int32 nTempPos;
540
541 if ( nKeyCode && !aCode.IsMod1() && !aCode.IsMod2() )
542 {
543 if ( nKeyCode == KEY_LEFT )
544 {
545 Selection aSel( ImplPatternLeftPos( rEditMask, nCursorPos ) );
546 if ( bShift )
547 aSel.Min() = aOldSel.Min();
548 rEdit.SetSelection( aSel );
549 return true;
550 }
551 else if ( nKeyCode == KEY_RIGHT )
552 {
553 // Use the start of selection as minimum; even a small position is allowed in case that
554 // all was selected by the focus
555 Selection aSel( aOldSel );
556 aSel.Justify();
557 nCursorPos = aSel.Min();
558 aSel.Max() = ImplPatternRightPos( rEdit.GetText(), rEditMask, nFormatFlags, bSameMask, nCursorPos );
559 if ( bShift )
560 aSel.Min() = aOldSel.Min();
561 else
562 aSel.Min() = aSel.Max();
563 rEdit.SetSelection( aSel );
564 return true;
565 }
566 else if ( nKeyCode == KEY_HOME )
567 {
568 // Home is the position of the first non-literal character
569 nNewPos = 0;
570 while ( (nNewPos < rEditMask.getLength()) &&
571 (rEditMask[nNewPos] == EDITMASK_LITERAL) )
572 nNewPos++;
573
574 // Home should not move to the right
575 if ( nCursorPos < nNewPos )
576 nNewPos = nCursorPos;
577 Selection aSel( nNewPos );
578 if ( bShift )
579 aSel.Min() = aOldSel.Min();
580 rEdit.SetSelection( aSel );
581 return true;
582 }
583 else if ( nKeyCode == KEY_END )
584 {
585 // End is position of last non-literal character
586 nNewPos = rEditMask.getLength();
587 while ( nNewPos &&
588 (rEditMask[nNewPos-1] == EDITMASK_LITERAL) )
589 nNewPos--;
590 // Use the start of selection as minimum; even a small position is allowed in case that
591 // all was selected by the focus
592 Selection aSel( aOldSel );
593 aSel.Justify();
594 nCursorPos = static_cast<sal_Int32>(aSel.Min());
595 ImplPatternMaxPos( rEdit.GetText(), rEditMask, nFormatFlags, bSameMask, nCursorPos, nNewPos );
596 aSel.Max() = nNewPos;
597 if ( bShift )
598 aSel.Min() = aOldSel.Min();
599 else
600 aSel.Min() = aSel.Max();
601 rEdit.SetSelection( aSel );
602 return true;
603 }
604 else if ( (nKeyCode == KEY_BACKSPACE) || (nKeyCode == KEY_DELETE) )
605 {
606 OUString aOldStr( rEdit.GetText() );
607 OUStringBuffer aStr( aOldStr );
608 Selection aSel = aOldSel;
609
610 aSel.Justify();
611 nNewPos = static_cast<sal_Int32>(aSel.Min());
612
613 // if selection then delete it
614 if ( aSel.Len() )
615 {
616 if ( bSameMask )
617 aStr.remove( static_cast<sal_Int32>(aSel.Min()), static_cast<sal_Int32>(aSel.Len()) );
618 else
619 {
620 OUString aRep = rLiteralMask.copy( static_cast<sal_Int32>(aSel.Min()), static_cast<sal_Int32>(aSel.Len()) );
621 aStr.remove( aSel.Min(), aRep.getLength() );
622 aStr.insert( aSel.Min(), aRep );
623 }
624 }
625 else
626 {
627 if ( nKeyCode == KEY_BACKSPACE )
628 {
629 nTempPos = nNewPos;
630 nNewPos = ImplPatternLeftPos( rEditMask, nTempPos );
631 }
632 else
633 nTempPos = ImplPatternRightPos( aStr.toString(), rEditMask, nFormatFlags, bSameMask, nNewPos );
634
635 if ( nNewPos != nTempPos )
636 {
637 if ( bSameMask )
638 {
639 if ( rEditMask[nNewPos] != EDITMASK_LITERAL )
640 aStr.remove( nNewPos, 1 );
641 }
642 else
643 {
644 aStr[nNewPos] = rLiteralMask[nNewPos];
645 }
646 }
647 }
648
649 if ( aOldStr != aStr.toString() )
650 {
651 if ( bSameMask )
652 aStr = ImplPatternReformat( aStr.toString(), rEditMask, rLiteralMask, nFormatFlags );
653 rbInKeyInput = true;
654 rEdit.SetText( aStr.toString(), Selection( nNewPos ) );
655 rEdit.SetModified();
656 rbInKeyInput = false;
657 }
658 else
659 rEdit.SetSelection( Selection( nNewPos ) );
660
661 return true;
662 }
663 else if ( nKeyCode == KEY_INSERT )
664 {
665 // you can only set InsertMode for a PatternField if the
666 // mask is equal at all input positions
667 if ( !bSameMask )
668 {
669 return true;
670 }
671 }
672 }
673
674 if ( rKEvt.GetKeyCode().IsMod2() || (cChar < 32) || (cChar == 127) )
675 return false;
676
677 Selection aSel = aOldSel;
678 aSel.Justify();
679 nNewPos = aSel.Min();
680
681 if ( nNewPos < rEditMask.getLength() )
682 {
683 sal_Unicode cPattChar = ImplPatternChar( cChar, rEditMask[nNewPos] );
684 if ( cPattChar )
685 cChar = cPattChar;
686 else
687 {
688 // If no valid character, check if the user wanted to jump to next literal. We do this
689 // only if we're after a character, so that literals that were skipped automatically
690 // do not influence the position anymore.
691 if ( nNewPos &&
692 (rEditMask[nNewPos-1] != EDITMASK_LITERAL) &&
693 !aSel.Len() )
694 {
695 // search for next character not being a literal
696 nTempPos = nNewPos;
697 while ( nTempPos < rEditMask.getLength() )
698 {
699 if ( rEditMask[nTempPos] == EDITMASK_LITERAL )
700 {
701 // only valid if no literal present
702 if ( (rEditMask[nTempPos+1] != EDITMASK_LITERAL ) &&
703 ImplCommaPointCharEqual( cChar, rLiteralMask[nTempPos] ) )
704 {
705 nTempPos++;
706 ImplPatternMaxPos( rEdit.GetText(), rEditMask, nFormatFlags, bSameMask, nNewPos, nTempPos );
707 if ( nTempPos > nNewPos )
708 {
709 rEdit.SetSelection( Selection( nTempPos ) );
710 return true;
711 }
712 }
713 break;
714 }
715 nTempPos++;
716 }
717 }
718
719 cChar = 0;
720 }
721 }
722 else
723 cChar = 0;
724 if ( cChar )
725 {
726 OUStringBuffer aStr = rEdit.GetText();
727 bool bError = false;
728 if ( bSameMask && rEdit.IsInsertMode() )
729 {
730 // crop spaces and literals at the end until current position
731 sal_Int32 n = aStr.getLength();
732 while ( n && (n > nNewPos) )
733 {
734 if ( (aStr[n-1] != ' ') &&
735 ((n > rEditMask.getLength()) || (rEditMask[n-1] != EDITMASK_LITERAL)) )
736 break;
737
738 n--;
739 }
740 aStr.truncate( n );
741
742 if ( aSel.Len() )
743 aStr.remove( aSel.Min(), aSel.Len() );
744
745 if ( aStr.getLength() < rEditMask.getLength() )
746 {
747 // possibly extend string until cursor position
748 if ( aStr.getLength() < nNewPos )
749 aStr.append( rLiteralMask.subView(aStr.getLength(), nNewPos-aStr.getLength()) );
750 if ( nNewPos < aStr.getLength() )
751 aStr.insert( cChar, nNewPos );
752 else if ( nNewPos < rEditMask.getLength() )
753 aStr.append(cChar);
754 aStr = ImplPatternReformat( aStr.toString(), rEditMask, rLiteralMask, nFormatFlags );
755 }
756 else
757 bError = true;
758 }
759 else
760 {
761 if ( aSel.Len() )
762 {
763 // delete selection
764 OUString aRep = rLiteralMask.copy( aSel.Min(), aSel.Len() );
765 aStr.remove( aSel.Min(), aRep.getLength() );
766 aStr.insert( aSel.Min(), aRep );
767 }
768
769 if ( nNewPos < aStr.getLength() )
770 aStr[nNewPos] = cChar;
771 else if ( nNewPos < rEditMask.getLength() )
772 aStr.append(cChar);
773 }
774
775 if ( !bError )
776 {
777 rbInKeyInput = true;
778 Selection aNewSel( ImplPatternRightPos( aStr.toString(), rEditMask, nFormatFlags, bSameMask, nNewPos ) );
779 rEdit.SetText( aStr.toString(), aNewSel );
780 rEdit.SetModified();
781 rbInKeyInput = false;
782 }
783 }
784
785 return true;
786 }
787
788 namespace
789 {
ImplSetMask(const OString & rEditMask,OUString & rLiteralMask)790 bool ImplSetMask(const OString& rEditMask, OUString& rLiteralMask)
791 {
792 bool bSameMask = true;
793
794 if (rEditMask.getLength() != rLiteralMask.getLength())
795 {
796 OUStringBuffer aBuf(rLiteralMask);
797 if (rEditMask.getLength() < aBuf.getLength())
798 aBuf.remove(rEditMask.getLength(), aBuf.getLength() - rEditMask.getLength());
799 else
800 comphelper::string::padToLength(aBuf, rEditMask.getLength(), ' ');
801 rLiteralMask = aBuf.makeStringAndClear();
802 }
803
804 // Strict mode allows only the input mode if only equal characters are allowed as mask and if
805 // only spaces are specified which are not allowed by the mask
806 sal_Int32 i = 0;
807 char c = 0;
808 while ( i < rEditMask.getLength() )
809 {
810 char cTemp = rEditMask[i];
811 if ( cTemp != EDITMASK_LITERAL )
812 {
813 if ( (cTemp == EDITMASK_ALLCHAR) ||
814 (cTemp == EDITMASK_UPPERALLCHAR) ||
815 (cTemp == EDITMASK_NUMSPACE) )
816 {
817 bSameMask = false;
818 break;
819 }
820 if ( i < rLiteralMask.getLength() )
821 {
822 if ( rLiteralMask[i] != ' ' )
823 {
824 bSameMask = false;
825 break;
826 }
827 }
828 if ( !c )
829 c = cTemp;
830 if ( cTemp != c )
831 {
832 bSameMask = false;
833 break;
834 }
835 }
836 i++;
837 }
838
839 return bSameMask;
840 }
841 }
842
PatternFormatter(Edit * pEdit)843 PatternFormatter::PatternFormatter(Edit* pEdit)
844 : FormatterBase(pEdit)
845 {
846 mbSameMask = true;
847 mbInPattKeyInput = false;
848 }
849
~PatternFormatter()850 PatternFormatter::~PatternFormatter()
851 {
852 }
853
SetMask(const OString & rEditMask,const OUString & rLiteralMask)854 void PatternFormatter::SetMask( const OString& rEditMask,
855 const OUString& rLiteralMask )
856 {
857 m_aEditMask = rEditMask;
858 maLiteralMask = rLiteralMask;
859 mbSameMask = ImplSetMask(m_aEditMask, maLiteralMask);
860 ReformatAll();
861 }
862
863 namespace
864 {
865 class EntryImplementation : public IEditImplementation
866 {
867 public:
EntryImplementation(weld::PatternFormatter & rFormatter)868 EntryImplementation(weld::PatternFormatter& rFormatter)
869 : m_rFormatter(rFormatter)
870 , m_rEntry(rFormatter.get_widget())
871 {
872 }
873
GetText() const874 virtual OUString GetText() const override
875 {
876 return m_rEntry.get_text();
877 }
878
SetText(const OUString & rStr,const Selection & rSelection)879 virtual void SetText(const OUString& rStr, const Selection& rSelection) override
880 {
881 m_rEntry.set_text(rStr);
882 SetSelection(rSelection);
883 }
884
GetSelection() const885 virtual Selection GetSelection() const override
886 {
887 int nStartPos, nEndPos;
888 m_rEntry.get_selection_bounds(nStartPos, nEndPos);
889 return Selection(nStartPos, nEndPos);
890 }
891
SetSelection(const Selection & rSelection)892 virtual void SetSelection(const Selection& rSelection) override
893 {
894 auto nMin = rSelection.Min();
895 auto nMax = rSelection.Max();
896 m_rEntry.select_region(nMin < 0 ? 0 : nMin, nMax == SELECTION_MAX ? -1 : nMax);
897 }
898
IsInsertMode() const899 virtual bool IsInsertMode() const override
900 {
901 return !m_rEntry.get_overwrite_mode();
902 }
903
SetModified()904 virtual void SetModified() override
905 {
906 m_rFormatter.Modify();
907 }
908
909 private:
910 weld::PatternFormatter& m_rFormatter;
911 weld::Entry& m_rEntry;
912 };
913 }
914
915 namespace weld
916 {
SetStrictFormat(bool bStrict)917 void PatternFormatter::SetStrictFormat(bool bStrict)
918 {
919 if (bStrict != m_bStrictFormat)
920 {
921 m_bStrictFormat = bStrict;
922 if (m_bStrictFormat)
923 ReformatAll();
924 }
925 }
926
SetMask(const OString & rEditMask,const OUString & rLiteralMask)927 void PatternFormatter::SetMask(const OString& rEditMask,
928 const OUString& rLiteralMask)
929 {
930 m_aEditMask = rEditMask;
931 m_aLiteralMask = rLiteralMask;
932 m_bSameMask = ImplSetMask(m_aEditMask, m_aLiteralMask);
933 ReformatAll();
934 }
935
ReformatAll()936 void PatternFormatter::ReformatAll()
937 {
938 m_rEntry.set_text(ImplPatternReformat(m_rEntry.get_text(), m_aEditMask, m_aLiteralMask, 0/*nFormatFlags*/));
939 if (!m_bSameMask && m_bStrictFormat && m_rEntry.get_editable())
940 m_rEntry.set_overwrite_mode(true);
941 }
942
EntryGainFocus()943 void PatternFormatter::EntryGainFocus()
944 {
945 m_bReformat = false;
946 }
947
EntryLostFocus()948 void PatternFormatter::EntryLostFocus()
949 {
950 if (m_bReformat)
951 ReformatAll();
952 }
953
Modify()954 void PatternFormatter::Modify()
955 {
956 if (!m_bInPattKeyInput)
957 {
958 if (m_bStrictFormat)
959 ImplPatternProcessStrictModify(m_rEntry, m_aEditMask, m_aLiteralMask, m_bSameMask);
960 else
961 m_bReformat = true;
962 }
963 m_aModifyHdl.Call(m_rEntry);
964 }
965
IMPL_LINK(PatternFormatter,KeyInputHdl,const KeyEvent &,rKEvt,bool)966 IMPL_LINK(PatternFormatter, KeyInputHdl, const KeyEvent&, rKEvt, bool)
967 {
968 if (m_aKeyPressHdl.Call(rKEvt))
969 return true;
970 if (rKEvt.GetKeyCode().IsMod2())
971 return false;
972 EntryImplementation aAdapt(*this);
973 return ImplPatternProcessKeyInput(aAdapt, rKEvt, m_aEditMask, m_aLiteralMask,
974 m_bStrictFormat,
975 m_bSameMask, m_bInPattKeyInput);
976 }
977 }
978
SetString(const OUString & rStr)979 void PatternFormatter::SetString( const OUString& rStr )
980 {
981 if ( GetField() )
982 {
983 GetField()->SetText( rStr );
984 MarkToBeReformatted( false );
985 }
986 }
987
GetString() const988 OUString PatternFormatter::GetString() const
989 {
990 if ( !GetField() )
991 return OUString();
992 else
993 return ImplPatternReformat( GetField()->GetText(), m_aEditMask, maLiteralMask, 0/*nFormatFlags*/ );
994 }
995
Reformat()996 void PatternFormatter::Reformat()
997 {
998 if ( GetField() )
999 {
1000 ImplSetText( ImplPatternReformat( GetField()->GetText(), m_aEditMask, maLiteralMask, 0/*nFormatFlags*/ ) );
1001 if ( !mbSameMask && IsStrictFormat() && !GetField()->IsReadOnly() )
1002 GetField()->SetInsertMode( false );
1003 }
1004 }
1005
PatternField(vcl::Window * pParent,WinBits nWinStyle)1006 PatternField::PatternField(vcl::Window* pParent, WinBits nWinStyle)
1007 : SpinField(pParent, nWinStyle)
1008 , PatternFormatter(this)
1009 {
1010 Reformat();
1011 }
1012
dispose()1013 void PatternField::dispose()
1014 {
1015 ClearField();
1016 SpinField::dispose();
1017 }
1018
1019 namespace
1020 {
1021 class EditImplementation : public IEditImplementation
1022 {
1023 public:
EditImplementation(Edit & rEdit)1024 EditImplementation(Edit& rEdit)
1025 : m_rEdit(rEdit)
1026 {
1027 }
1028
GetText() const1029 virtual OUString GetText() const override
1030 {
1031 return m_rEdit.GetText();
1032 }
1033
SetText(const OUString & rStr,const Selection & rSelection)1034 virtual void SetText(const OUString& rStr, const Selection& rSelection) override
1035 {
1036 m_rEdit.SetText(rStr, rSelection);
1037 }
1038
GetSelection() const1039 virtual Selection GetSelection() const override
1040 {
1041 return m_rEdit.GetSelection();
1042 }
1043
SetSelection(const Selection & rSelection)1044 virtual void SetSelection(const Selection& rSelection) override
1045 {
1046 m_rEdit.SetSelection(rSelection);
1047 }
1048
IsInsertMode() const1049 virtual bool IsInsertMode() const override
1050 {
1051 return m_rEdit.IsInsertMode();
1052 }
1053
SetModified()1054 virtual void SetModified() override
1055 {
1056 m_rEdit.SetModifyFlag();
1057 m_rEdit.Modify();
1058 }
1059
1060 private:
1061 Edit& m_rEdit;
1062 };
1063 }
1064
PreNotify(NotifyEvent & rNEvt)1065 bool PatternField::PreNotify( NotifyEvent& rNEvt )
1066 {
1067 if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
1068 {
1069 EditImplementation aAdapt(*GetField());
1070 if ( ImplPatternProcessKeyInput( aAdapt, *rNEvt.GetKeyEvent(), GetEditMask(), GetLiteralMask(),
1071 IsStrictFormat(),
1072 ImplIsSameMask(), ImplGetInPattKeyInput() ) )
1073 return true;
1074 }
1075
1076 return SpinField::PreNotify( rNEvt );
1077 }
1078
EventNotify(NotifyEvent & rNEvt)1079 bool PatternField::EventNotify( NotifyEvent& rNEvt )
1080 {
1081 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
1082 MarkToBeReformatted( false );
1083 else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
1084 {
1085 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1086 Reformat();
1087 }
1088
1089 return SpinField::EventNotify( rNEvt );
1090 }
1091
Modify()1092 void PatternField::Modify()
1093 {
1094 if ( !ImplGetInPattKeyInput() )
1095 {
1096 if ( IsStrictFormat() )
1097 ImplPatternProcessStrictModify( GetField(), GetEditMask(), GetLiteralMask(), ImplIsSameMask() );
1098 else
1099 MarkToBeReformatted( true );
1100 }
1101
1102 SpinField::Modify();
1103 }
1104
PatternBox(vcl::Window * pParent,WinBits nWinStyle)1105 PatternBox::PatternBox(vcl::Window* pParent, WinBits nWinStyle)
1106 : ComboBox( pParent, nWinStyle )
1107 , PatternFormatter(this)
1108 {
1109 Reformat();
1110 }
1111
dispose()1112 void PatternBox::dispose()
1113 {
1114 ClearField();
1115 ComboBox::dispose();
1116 }
1117
PreNotify(NotifyEvent & rNEvt)1118 bool PatternBox::PreNotify( NotifyEvent& rNEvt )
1119 {
1120 if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
1121 {
1122 EditImplementation aAdapt(*GetField());
1123 if ( ImplPatternProcessKeyInput( aAdapt, *rNEvt.GetKeyEvent(), GetEditMask(), GetLiteralMask(),
1124 IsStrictFormat(),
1125 ImplIsSameMask(), ImplGetInPattKeyInput() ) )
1126 return true;
1127 }
1128
1129 return ComboBox::PreNotify( rNEvt );
1130 }
1131
EventNotify(NotifyEvent & rNEvt)1132 bool PatternBox::EventNotify( NotifyEvent& rNEvt )
1133 {
1134 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
1135 MarkToBeReformatted( false );
1136 else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
1137 {
1138 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1139 Reformat();
1140 }
1141
1142 return ComboBox::EventNotify( rNEvt );
1143 }
1144
Modify()1145 void PatternBox::Modify()
1146 {
1147 if ( !ImplGetInPattKeyInput() )
1148 {
1149 if ( IsStrictFormat() )
1150 ImplPatternProcessStrictModify( GetField(), GetEditMask(), GetLiteralMask(), ImplIsSameMask() );
1151 else
1152 MarkToBeReformatted( true );
1153 }
1154
1155 ComboBox::Modify();
1156 }
1157
ReformatAll()1158 void PatternBox::ReformatAll()
1159 {
1160 OUString aStr;
1161 SetUpdateMode( false );
1162 const sal_Int32 nEntryCount = GetEntryCount();
1163 for ( sal_Int32 i=0; i < nEntryCount; ++i )
1164 {
1165 aStr = ImplPatternReformat( GetEntry( i ), GetEditMask(), GetLiteralMask(), 0/*nFormatFlags*/ );
1166 RemoveEntryAt(i);
1167 InsertEntry( aStr, i );
1168 }
1169 PatternFormatter::Reformat();
1170 SetUpdateMode( true );
1171 }
1172
ImplGetExtFormat(LongDateOrder eOld)1173 static ExtDateFieldFormat ImplGetExtFormat( LongDateOrder eOld )
1174 {
1175 switch( eOld )
1176 {
1177 case LongDateOrder::YDM:
1178 case LongDateOrder::DMY: return ExtDateFieldFormat::ShortDDMMYY;
1179 case LongDateOrder::MDY: return ExtDateFieldFormat::ShortMMDDYY;
1180 case LongDateOrder::YMD:
1181 default: return ExtDateFieldFormat::ShortYYMMDD;
1182 }
1183 }
1184
ImplCutNumberFromString(OUString & rStr)1185 static sal_uInt16 ImplCutNumberFromString( OUString& rStr )
1186 {
1187 sal_Int32 i1 = 0;
1188 while (i1 != rStr.getLength() && (rStr[i1] < '0' || rStr[i1] > '9')) {
1189 ++i1;
1190 }
1191 sal_Int32 i2 = i1;
1192 while (i2 != rStr.getLength() && rStr[i2] >= '0' && rStr[i2] <= '9') {
1193 ++i2;
1194 }
1195 sal_Int32 nValue = rStr.copy(i1, i2-i1).toInt32();
1196 rStr = rStr.copy(std::min(i2+1, rStr.getLength()));
1197 return nValue;
1198 }
1199
ImplCutMonthName(OUString & rStr,std::u16string_view _rLookupMonthName)1200 static bool ImplCutMonthName( OUString& rStr, std::u16string_view _rLookupMonthName )
1201 {
1202 sal_Int32 index = 0;
1203 rStr = rStr.replaceFirst(_rLookupMonthName, "", &index);
1204 return index >= 0;
1205 }
1206
ImplGetMonthFromCalendarItem(OUString & rStr,const uno::Sequence<i18n::CalendarItem2> & rMonths)1207 static sal_uInt16 ImplGetMonthFromCalendarItem( OUString& rStr, const uno::Sequence< i18n::CalendarItem2 >& rMonths )
1208 {
1209 const sal_uInt16 nMonths = rMonths.getLength();
1210 for (sal_uInt16 i=0; i < nMonths; ++i)
1211 {
1212 // long month name?
1213 if ( ImplCutMonthName( rStr, rMonths[i].FullName ) )
1214 return i+1;
1215
1216 // short month name?
1217 if ( ImplCutMonthName( rStr, rMonths[i].AbbrevName ) )
1218 return i+1;
1219 }
1220 return 0;
1221 }
1222
ImplCutMonthFromString(OUString & rStr,OUString & rCalendarName,const LocaleDataWrapper & rLocaleData,const CalendarWrapper & rCalendarWrapper)1223 static sal_uInt16 ImplCutMonthFromString( OUString& rStr, OUString& rCalendarName,
1224 const LocaleDataWrapper& rLocaleData, const CalendarWrapper& rCalendarWrapper )
1225 {
1226 const OUString aDefaultCalendarName( rCalendarWrapper.getUniqueID());
1227 rCalendarName = aDefaultCalendarName;
1228
1229 // Search for a month name of the loaded default calendar.
1230 const uno::Sequence< i18n::CalendarItem2 > aMonths = rCalendarWrapper.getMonths();
1231 sal_uInt16 nMonth = ImplGetMonthFromCalendarItem( rStr, aMonths);
1232 if (nMonth > 0)
1233 return nMonth;
1234
1235 // And also possessive genitive and partitive month names.
1236 const uno::Sequence< i18n::CalendarItem2 > aGenitiveMonths = rCalendarWrapper.getGenitiveMonths();
1237 if (aGenitiveMonths != aMonths)
1238 {
1239 nMonth = ImplGetMonthFromCalendarItem( rStr, aGenitiveMonths);
1240 if (nMonth > 0)
1241 return nMonth;
1242 }
1243 const uno::Sequence< i18n::CalendarItem2 > aPartitiveMonths = rCalendarWrapper.getPartitiveMonths();
1244 if (aPartitiveMonths != aMonths)
1245 {
1246 nMonth = ImplGetMonthFromCalendarItem( rStr, aPartitiveMonths);
1247 if (nMonth > 0)
1248 return nMonth;
1249 }
1250
1251 // Check if there are more calendars and try them if so, as the long date
1252 // format is obtained from the number formatter this is possible (e.g.
1253 // ar_DZ "[~hijri] ...")
1254 const uno::Sequence< i18n::Calendar2 > aCalendars = rLocaleData.getAllCalendars();
1255 if (aCalendars.getLength() > 1)
1256 {
1257 for (const auto& rCalendar : aCalendars)
1258 {
1259 if (rCalendar.Name != aDefaultCalendarName)
1260 {
1261 rCalendarName = rCalendar.Name;
1262
1263 nMonth = ImplGetMonthFromCalendarItem( rStr, rCalendar.Months);
1264 if (nMonth > 0)
1265 return nMonth;
1266
1267 if (rCalendar.Months != rCalendar.GenitiveMonths)
1268 {
1269 nMonth = ImplGetMonthFromCalendarItem( rStr, rCalendar.GenitiveMonths);
1270 if (nMonth > 0)
1271 return nMonth;
1272 }
1273
1274 if (rCalendar.Months != rCalendar.PartitiveMonths)
1275 {
1276 nMonth = ImplGetMonthFromCalendarItem( rStr, rCalendar.PartitiveMonths);
1277 if (nMonth > 0)
1278 return nMonth;
1279 }
1280
1281 rCalendarName = aDefaultCalendarName;
1282 }
1283 }
1284 }
1285
1286 return ImplCutNumberFromString( rStr );
1287 }
1288
ImplGetDateSep(const LocaleDataWrapper & rLocaleDataWrapper,ExtDateFieldFormat eFormat)1289 static OUString ImplGetDateSep( const LocaleDataWrapper& rLocaleDataWrapper, ExtDateFieldFormat eFormat )
1290 {
1291 if ( ( eFormat == ExtDateFieldFormat::ShortYYMMDD_DIN5008 ) || ( eFormat == ExtDateFieldFormat::ShortYYYYMMDD_DIN5008 ) )
1292 return "-";
1293 else
1294 return rLocaleDataWrapper.getDateSep();
1295 }
1296
ImplDateProcessKeyInput(const KeyEvent & rKEvt,ExtDateFieldFormat eFormat,const LocaleDataWrapper & rLocaleDataWrapper)1297 static bool ImplDateProcessKeyInput( const KeyEvent& rKEvt, ExtDateFieldFormat eFormat,
1298 const LocaleDataWrapper& rLocaleDataWrapper )
1299 {
1300 sal_Unicode cChar = rKEvt.GetCharCode();
1301 sal_uInt16 nGroup = rKEvt.GetKeyCode().GetGroup();
1302 return !((nGroup == KEYGROUP_FKEYS) ||
1303 (nGroup == KEYGROUP_CURSOR) ||
1304 (nGroup == KEYGROUP_MISC)||
1305 ((cChar >= '0') && (cChar <= '9')) ||
1306 (cChar == ImplGetDateSep( rLocaleDataWrapper, eFormat )[0]));
1307 }
1308
TextToDate(const OUString & rStr,Date & rDate,ExtDateFieldFormat eDateOrder,const LocaleDataWrapper & rLocaleDataWrapper,const CalendarWrapper & rCalendarWrapper)1309 bool DateFormatter::TextToDate(const OUString& rStr, Date& rDate, ExtDateFieldFormat eDateOrder,
1310 const LocaleDataWrapper& rLocaleDataWrapper, const CalendarWrapper& rCalendarWrapper)
1311 {
1312 sal_uInt16 nDay = 0;
1313 sal_uInt16 nMonth = 0;
1314 sal_uInt16 nYear = 0;
1315 bool bError = false;
1316 OUString aStr( rStr );
1317
1318 if ( eDateOrder == ExtDateFieldFormat::SystemLong )
1319 {
1320 OUString aCalendarName;
1321 LongDateOrder eFormat = rLocaleDataWrapper.getLongDateOrder();
1322 switch( eFormat )
1323 {
1324 case LongDateOrder::MDY:
1325 nMonth = ImplCutMonthFromString( aStr, aCalendarName, rLocaleDataWrapper, rCalendarWrapper );
1326 nDay = ImplCutNumberFromString( aStr );
1327 nYear = ImplCutNumberFromString( aStr );
1328 break;
1329 case LongDateOrder::DMY:
1330 nDay = ImplCutNumberFromString( aStr );
1331 nMonth = ImplCutMonthFromString( aStr, aCalendarName, rLocaleDataWrapper, rCalendarWrapper );
1332 nYear = ImplCutNumberFromString( aStr );
1333 break;
1334 case LongDateOrder::YDM:
1335 nYear = ImplCutNumberFromString( aStr );
1336 nDay = ImplCutNumberFromString( aStr );
1337 nMonth = ImplCutMonthFromString( aStr, aCalendarName, rLocaleDataWrapper, rCalendarWrapper );
1338 break;
1339 case LongDateOrder::YMD:
1340 default:
1341 nYear = ImplCutNumberFromString( aStr );
1342 nMonth = ImplCutMonthFromString( aStr, aCalendarName, rLocaleDataWrapper, rCalendarWrapper );
1343 nDay = ImplCutNumberFromString( aStr );
1344 break;
1345 }
1346 if (aCalendarName != "gregorian")
1347 {
1348 // Calendar widget is Gregorian, convert date.
1349 // Need full date.
1350 bError = !nDay || !nMonth || !nYear;
1351 if (!bError)
1352 {
1353 CalendarWrapper aCW( rLocaleDataWrapper.getComponentContext());
1354 aCW.loadCalendar( aCalendarName, rLocaleDataWrapper.getLoadedLanguageTag().getLocale());
1355 aCW.setDateTime(0.5); // get rid of current time, set some day noon
1356 aCW.setValue( i18n::CalendarFieldIndex::DAY_OF_MONTH, nDay);
1357 aCW.setValue( i18n::CalendarFieldIndex::MONTH, nMonth - 1);
1358 aCW.setValue( i18n::CalendarFieldIndex::YEAR, nYear);
1359 bError = !aCW.isValid();
1360 if (!bError)
1361 {
1362 Date aDate = aCW.getEpochStart() + aCW.getDateTime();
1363 nYear = aDate.GetYear();
1364 nMonth = aDate.GetMonth();
1365 nDay = aDate.GetDay();
1366 }
1367 }
1368 }
1369 }
1370 else
1371 {
1372 bool bYear = true;
1373
1374 // Check if year is present:
1375 OUString aDateSep = ImplGetDateSep( rLocaleDataWrapper, eDateOrder );
1376 sal_Int32 nSepPos = aStr.indexOf( aDateSep );
1377 if ( nSepPos < 0 )
1378 return false;
1379 nSepPos = aStr.indexOf( aDateSep, nSepPos+1 );
1380 if ( ( nSepPos < 0 ) || ( nSepPos == (aStr.getLength()-1) ) )
1381 {
1382 bYear = false;
1383 nYear = Date( Date::SYSTEM ).GetYearUnsigned();
1384 }
1385
1386 const sal_Unicode* pBuf = aStr.getStr();
1387 ImplSkipDelimiters( pBuf );
1388
1389 switch ( eDateOrder )
1390 {
1391 case ExtDateFieldFormat::ShortDDMMYY:
1392 case ExtDateFieldFormat::ShortDDMMYYYY:
1393 {
1394 nDay = ImplGetNum( pBuf, bError );
1395 ImplSkipDelimiters( pBuf );
1396 nMonth = ImplGetNum( pBuf, bError );
1397 ImplSkipDelimiters( pBuf );
1398 if ( bYear )
1399 nYear = ImplGetNum( pBuf, bError );
1400 }
1401 break;
1402 case ExtDateFieldFormat::ShortMMDDYY:
1403 case ExtDateFieldFormat::ShortMMDDYYYY:
1404 {
1405 nMonth = ImplGetNum( pBuf, bError );
1406 ImplSkipDelimiters( pBuf );
1407 nDay = ImplGetNum( pBuf, bError );
1408 ImplSkipDelimiters( pBuf );
1409 if ( bYear )
1410 nYear = ImplGetNum( pBuf, bError );
1411 }
1412 break;
1413 case ExtDateFieldFormat::ShortYYMMDD:
1414 case ExtDateFieldFormat::ShortYYYYMMDD:
1415 case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
1416 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1417 {
1418 if ( bYear )
1419 nYear = ImplGetNum( pBuf, bError );
1420 ImplSkipDelimiters( pBuf );
1421 nMonth = ImplGetNum( pBuf, bError );
1422 ImplSkipDelimiters( pBuf );
1423 nDay = ImplGetNum( pBuf, bError );
1424 }
1425 break;
1426
1427 default:
1428 {
1429 OSL_FAIL( "DateOrder???" );
1430 }
1431 }
1432 }
1433
1434 if ( bError || !nDay || !nMonth )
1435 return false;
1436
1437 Date aNewDate( nDay, nMonth, nYear );
1438 DateFormatter::ExpandCentury( aNewDate, officecfg::Office::Common::DateFormat::TwoDigitYear::get() );
1439 if ( aNewDate.IsValidDate() )
1440 {
1441 rDate = aNewDate;
1442 return true;
1443 }
1444 return false;
1445 }
1446
ImplDateReformat(const OUString & rStr,OUString & rOutStr)1447 void DateFormatter::ImplDateReformat( const OUString& rStr, OUString& rOutStr )
1448 {
1449 Date aDate( Date::EMPTY );
1450 if (!TextToDate(rStr, aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper()))
1451 return;
1452
1453 Date aTempDate = aDate;
1454 if ( aTempDate > GetMax() )
1455 aTempDate = GetMax();
1456 else if ( aTempDate < GetMin() )
1457 aTempDate = GetMin();
1458
1459 rOutStr = ImplGetDateAsText( aTempDate );
1460 }
1461
1462 namespace
1463 {
ResolveSystemFormat(ExtDateFieldFormat eDateFormat,const LocaleDataWrapper & rLocaleData)1464 ExtDateFieldFormat ResolveSystemFormat(ExtDateFieldFormat eDateFormat, const LocaleDataWrapper& rLocaleData)
1465 {
1466 if (eDateFormat <= ExtDateFieldFormat::SystemShortYYYY)
1467 {
1468 bool bShowCentury = (eDateFormat == ExtDateFieldFormat::SystemShortYYYY);
1469 switch (rLocaleData.getDateOrder())
1470 {
1471 case DateOrder::DMY:
1472 eDateFormat = bShowCentury ? ExtDateFieldFormat::ShortDDMMYYYY : ExtDateFieldFormat::ShortDDMMYY;
1473 break;
1474 case DateOrder::MDY:
1475 eDateFormat = bShowCentury ? ExtDateFieldFormat::ShortMMDDYYYY : ExtDateFieldFormat::ShortMMDDYY;
1476 break;
1477 default:
1478 eDateFormat = bShowCentury ? ExtDateFieldFormat::ShortYYYYMMDD : ExtDateFieldFormat::ShortYYMMDD;
1479 }
1480 }
1481 return eDateFormat;
1482 }
1483 }
1484
FormatDate(const Date & rDate,ExtDateFieldFormat eExtFormat,const LocaleDataWrapper & rLocaleData,const Formatter::StaticFormatter & rStaticFormatter)1485 OUString DateFormatter::FormatDate(const Date& rDate, ExtDateFieldFormat eExtFormat,
1486 const LocaleDataWrapper& rLocaleData,
1487 const Formatter::StaticFormatter& rStaticFormatter)
1488 {
1489 bool bShowCentury = false;
1490 switch (eExtFormat)
1491 {
1492 case ExtDateFieldFormat::SystemShortYYYY:
1493 case ExtDateFieldFormat::SystemLong:
1494 case ExtDateFieldFormat::ShortDDMMYYYY:
1495 case ExtDateFieldFormat::ShortMMDDYYYY:
1496 case ExtDateFieldFormat::ShortYYYYMMDD:
1497 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1498 {
1499 bShowCentury = true;
1500 }
1501 break;
1502 default:
1503 {
1504 bShowCentury = false;
1505 }
1506 }
1507
1508 if ( !bShowCentury )
1509 {
1510 // Check if I have to use force showing the century
1511 sal_uInt16 nTwoDigitYearStart = officecfg::Office::Common::DateFormat::TwoDigitYear::get();
1512 sal_uInt16 nYear = rDate.GetYearUnsigned();
1513
1514 // If year is not in double digit range
1515 if ( (nYear < nTwoDigitYearStart) || (nYear >= nTwoDigitYearStart+100) )
1516 bShowCentury = true;
1517 }
1518
1519 sal_Unicode aBuf[128];
1520 sal_Unicode* pBuf = aBuf;
1521
1522 eExtFormat = ResolveSystemFormat(eExtFormat, rLocaleData);
1523
1524 OUString aDateSep = ImplGetDateSep( rLocaleData, eExtFormat );
1525 sal_uInt16 nDay = rDate.GetDay();
1526 sal_uInt16 nMonth = rDate.GetMonth();
1527 sal_Int16 nYear = rDate.GetYear();
1528 sal_uInt16 nYearLen = bShowCentury ? 4 : 2;
1529
1530 if ( !bShowCentury )
1531 nYear %= 100;
1532
1533 switch (eExtFormat)
1534 {
1535 case ExtDateFieldFormat::SystemLong:
1536 {
1537 SvNumberFormatter* pFormatter = rStaticFormatter;
1538 const LanguageTag aFormatterLang( pFormatter->GetLanguageTag());
1539 const sal_uInt32 nIndex = pFormatter->GetFormatIndex( NF_DATE_SYSTEM_LONG,
1540 rLocaleData.getLanguageTag().getLanguageType(false));
1541 OUString aStr;
1542 const Color* pCol;
1543 pFormatter->GetOutputString( rDate - pFormatter->GetNullDate(), nIndex, aStr, &pCol);
1544 // Reset to what other uses may expect.
1545 pFormatter->ChangeIntl( aFormatterLang.getLanguageType(false));
1546 return aStr;
1547 }
1548 case ExtDateFieldFormat::ShortDDMMYY:
1549 case ExtDateFieldFormat::ShortDDMMYYYY:
1550 {
1551 pBuf = ImplAddNum( pBuf, nDay, 2 );
1552 pBuf = ImplAddString( pBuf, aDateSep );
1553 pBuf = ImplAddNum( pBuf, nMonth, 2 );
1554 pBuf = ImplAddString( pBuf, aDateSep );
1555 pBuf = ImplAddSNum( pBuf, nYear, nYearLen );
1556 }
1557 break;
1558 case ExtDateFieldFormat::ShortMMDDYY:
1559 case ExtDateFieldFormat::ShortMMDDYYYY:
1560 {
1561 pBuf = ImplAddNum( pBuf, nMonth, 2 );
1562 pBuf = ImplAddString( pBuf, aDateSep );
1563 pBuf = ImplAddNum( pBuf, nDay, 2 );
1564 pBuf = ImplAddString( pBuf, aDateSep );
1565 pBuf = ImplAddSNum( pBuf, nYear, nYearLen );
1566 }
1567 break;
1568 case ExtDateFieldFormat::ShortYYMMDD:
1569 case ExtDateFieldFormat::ShortYYYYMMDD:
1570 case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
1571 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1572 {
1573 pBuf = ImplAddSNum( pBuf, nYear, nYearLen );
1574 pBuf = ImplAddString( pBuf, aDateSep );
1575 pBuf = ImplAddNum( pBuf, nMonth, 2 );
1576 pBuf = ImplAddString( pBuf, aDateSep );
1577 pBuf = ImplAddNum( pBuf, nDay, 2 );
1578 }
1579 break;
1580 default:
1581 {
1582 OSL_FAIL( "DateOrder???" );
1583 }
1584 }
1585
1586 return OUString(aBuf, pBuf-aBuf);
1587 }
1588
ImplGetDateAsText(const Date & rDate) const1589 OUString DateFormatter::ImplGetDateAsText( const Date& rDate ) const
1590 {
1591 return DateFormatter::FormatDate(rDate, GetExtDateFormat(), ImplGetLocaleDataWrapper(), maStaticFormatter);
1592 }
1593
ImplDateIncrementDay(Date & rDate,bool bUp)1594 static void ImplDateIncrementDay( Date& rDate, bool bUp )
1595 {
1596 DateFormatter::ExpandCentury( rDate );
1597
1598 if ( bUp )
1599 {
1600 if ( (rDate.GetDay() != 31) || (rDate.GetMonth() != 12) || (rDate.GetYear() != SAL_MAX_INT16) )
1601 ++rDate;
1602 }
1603 else
1604 {
1605 if ( (rDate.GetDay() != 1 ) || (rDate.GetMonth() != 1) || (rDate.GetYear() != SAL_MIN_INT16) )
1606 --rDate;
1607 }
1608 }
1609
ImplDateIncrementMonth(Date & rDate,bool bUp)1610 static void ImplDateIncrementMonth( Date& rDate, bool bUp )
1611 {
1612 DateFormatter::ExpandCentury( rDate );
1613
1614 sal_uInt16 nMonth = rDate.GetMonth();
1615 sal_Int16 nYear = rDate.GetYear();
1616 if ( bUp )
1617 {
1618 if ( (nMonth == 12) && (nYear < SAL_MAX_INT16) )
1619 {
1620 rDate.SetMonth( 1 );
1621 rDate.SetYear( rDate.GetNextYear() );
1622 }
1623 else
1624 {
1625 if ( nMonth < 12 )
1626 rDate.SetMonth( nMonth + 1 );
1627 }
1628 }
1629 else
1630 {
1631 if ( (nMonth == 1) && (nYear > SAL_MIN_INT16) )
1632 {
1633 rDate.SetMonth( 12 );
1634 rDate.SetYear( rDate.GetPrevYear() );
1635 }
1636 else
1637 {
1638 if ( nMonth > 1 )
1639 rDate.SetMonth( nMonth - 1 );
1640 }
1641 }
1642
1643 sal_uInt16 nDaysInMonth = Date::GetDaysInMonth( rDate.GetMonth(), rDate.GetYear());
1644 if ( rDate.GetDay() > nDaysInMonth )
1645 rDate.SetDay( nDaysInMonth );
1646 }
1647
ImplDateIncrementYear(Date & rDate,bool bUp)1648 static void ImplDateIncrementYear( Date& rDate, bool bUp )
1649 {
1650 DateFormatter::ExpandCentury( rDate );
1651
1652 sal_Int16 nYear = rDate.GetYear();
1653 sal_uInt16 nMonth = rDate.GetMonth();
1654 if ( bUp )
1655 {
1656 if ( nYear < SAL_MAX_INT16 )
1657 rDate.SetYear( rDate.GetNextYear() );
1658 }
1659 else
1660 {
1661 if ( nYear > SAL_MIN_INT16 )
1662 rDate.SetYear( rDate.GetPrevYear() );
1663 }
1664 if (nMonth != 2)
1665 return;
1666
1667 // Handle February 29 from leap year to non-leap year.
1668 sal_uInt16 nDay = rDate.GetDay();
1669 if (nDay > 28)
1670 {
1671 // The check would not be necessary if it was guaranteed that the
1672 // date was valid before and actually was a leap year,
1673 // de-/incrementing a leap year with 29 always results in 28.
1674 sal_uInt16 nDaysInMonth = Date::GetDaysInMonth( nMonth, rDate.GetYear());
1675 if (nDay > nDaysInMonth)
1676 rDate.SetDay( nDaysInMonth);
1677 }
1678 }
1679
ImplAllowMalformedInput() const1680 bool DateFormatter::ImplAllowMalformedInput() const
1681 {
1682 return !IsEnforceValidValue();
1683 }
1684
GetDateArea(ExtDateFieldFormat eFormat,const OUString & rText,int nCursor,const LocaleDataWrapper & rLocaleDataWrapper)1685 int DateFormatter::GetDateArea(ExtDateFieldFormat eFormat, const OUString& rText, int nCursor, const LocaleDataWrapper& rLocaleDataWrapper)
1686 {
1687 sal_Int8 nDateArea = 0;
1688
1689 if ( eFormat == ExtDateFieldFormat::SystemLong )
1690 {
1691 eFormat = ImplGetExtFormat(rLocaleDataWrapper.getLongDateOrder());
1692 nDateArea = 1;
1693 }
1694 else
1695 {
1696 // search area
1697 sal_Int32 nPos = 0;
1698 OUString aDateSep = ImplGetDateSep(rLocaleDataWrapper, eFormat);
1699 for ( sal_Int8 i = 1; i <= 3; i++ )
1700 {
1701 nPos = rText.indexOf( aDateSep, nPos );
1702 if (nPos < 0 || nPos >= nCursor)
1703 {
1704 nDateArea = i;
1705 break;
1706 }
1707 else
1708 nPos++;
1709 }
1710 }
1711
1712 return nDateArea;
1713 }
1714
ImplDateSpinArea(bool bUp)1715 void DateField::ImplDateSpinArea( bool bUp )
1716 {
1717 // increment days if all is selected
1718 if ( !GetField() )
1719 return;
1720
1721 Date aDate( GetDate() );
1722 Selection aSelection = GetField()->GetSelection();
1723 aSelection.Justify();
1724 OUString aText( GetText() );
1725 if ( static_cast<sal_Int32>(aSelection.Len()) == aText.getLength() )
1726 ImplDateIncrementDay( aDate, bUp );
1727 else
1728 {
1729 ExtDateFieldFormat eFormat = GetExtDateFormat( true );
1730 sal_Int8 nDateArea = GetDateArea(eFormat, aText, aSelection.Max(), ImplGetLocaleDataWrapper());
1731
1732 switch( eFormat )
1733 {
1734 case ExtDateFieldFormat::ShortMMDDYY:
1735 case ExtDateFieldFormat::ShortMMDDYYYY:
1736 switch( nDateArea )
1737 {
1738 case 1: ImplDateIncrementMonth( aDate, bUp );
1739 break;
1740 case 2: ImplDateIncrementDay( aDate, bUp );
1741 break;
1742 case 3: ImplDateIncrementYear( aDate, bUp );
1743 break;
1744 }
1745 break;
1746 case ExtDateFieldFormat::ShortDDMMYY:
1747 case ExtDateFieldFormat::ShortDDMMYYYY:
1748 switch( nDateArea )
1749 {
1750 case 1: ImplDateIncrementDay( aDate, bUp );
1751 break;
1752 case 2: ImplDateIncrementMonth( aDate, bUp );
1753 break;
1754 case 3: ImplDateIncrementYear( aDate, bUp );
1755 break;
1756 }
1757 break;
1758 case ExtDateFieldFormat::ShortYYMMDD:
1759 case ExtDateFieldFormat::ShortYYYYMMDD:
1760 case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
1761 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1762 switch( nDateArea )
1763 {
1764 case 1: ImplDateIncrementYear( aDate, bUp );
1765 break;
1766 case 2: ImplDateIncrementMonth( aDate, bUp );
1767 break;
1768 case 3: ImplDateIncrementDay( aDate, bUp );
1769 break;
1770 }
1771 break;
1772 default:
1773 OSL_FAIL( "invalid conversion" );
1774 break;
1775 }
1776 }
1777
1778 ImplNewFieldValue( aDate );
1779 }
1780
DateFormatter(Edit * pEdit)1781 DateFormatter::DateFormatter(Edit* pEdit)
1782 : FormatterBase(pEdit)
1783 , maFieldDate(0)
1784 , maLastDate(0)
1785 , maMin(1, 1, 1900)
1786 , maMax(31, 12, 2200)
1787 , mbLongFormat(false)
1788 , mbShowDateCentury(true)
1789 , mnExtDateFormat(ExtDateFieldFormat::SystemShort)
1790 , mbEnforceValidValue(true)
1791 {
1792 }
1793
~DateFormatter()1794 DateFormatter::~DateFormatter()
1795 {
1796 }
1797
GetCalendarWrapper() const1798 CalendarWrapper& DateFormatter::GetCalendarWrapper() const
1799 {
1800 if (!mxCalendarWrapper)
1801 {
1802 const_cast<DateFormatter*>(this)->mxCalendarWrapper.reset( new CalendarWrapper( comphelper::getProcessComponentContext() ) );
1803 mxCalendarWrapper->loadDefaultCalendar( GetLocale() );
1804 }
1805
1806 return *mxCalendarWrapper;
1807 }
1808
SetExtDateFormat(ExtDateFieldFormat eFormat)1809 void DateFormatter::SetExtDateFormat( ExtDateFieldFormat eFormat )
1810 {
1811 mnExtDateFormat = eFormat;
1812 ReformatAll();
1813 }
1814
GetExtDateFormat(bool bResolveSystemFormat) const1815 ExtDateFieldFormat DateFormatter::GetExtDateFormat( bool bResolveSystemFormat ) const
1816 {
1817 ExtDateFieldFormat eDateFormat = mnExtDateFormat;
1818
1819 if (bResolveSystemFormat)
1820 eDateFormat = ResolveSystemFormat(eDateFormat, ImplGetLocaleDataWrapper());
1821
1822 return eDateFormat;
1823 }
1824
ReformatAll()1825 void DateFormatter::ReformatAll()
1826 {
1827 Reformat();
1828 }
1829
SetMin(const Date & rNewMin)1830 void DateFormatter::SetMin( const Date& rNewMin )
1831 {
1832 maMin = rNewMin;
1833 if ( !IsEmptyFieldValue() )
1834 ReformatAll();
1835 }
1836
SetMax(const Date & rNewMax)1837 void DateFormatter::SetMax( const Date& rNewMax )
1838 {
1839 maMax = rNewMax;
1840 if ( !IsEmptyFieldValue() )
1841 ReformatAll();
1842 }
1843
SetLongFormat(bool bLong)1844 void DateFormatter::SetLongFormat( bool bLong )
1845 {
1846 mbLongFormat = bLong;
1847
1848 // #91913# Remove LongFormat and DateShowCentury - redundant
1849 if ( bLong )
1850 {
1851 SetExtDateFormat( ExtDateFieldFormat::SystemLong );
1852 }
1853 else
1854 {
1855 if( mnExtDateFormat == ExtDateFieldFormat::SystemLong )
1856 SetExtDateFormat( ExtDateFieldFormat::SystemShort );
1857 }
1858
1859 ReformatAll();
1860 }
1861
1862 namespace
1863 {
ChangeDateCentury(ExtDateFieldFormat eExtDateFormat,bool bShowDateCentury)1864 ExtDateFieldFormat ChangeDateCentury(ExtDateFieldFormat eExtDateFormat, bool bShowDateCentury)
1865 {
1866 // #91913# Remove LongFormat and DateShowCentury - redundant
1867 if (bShowDateCentury)
1868 {
1869 switch (eExtDateFormat)
1870 {
1871 case ExtDateFieldFormat::SystemShort:
1872 case ExtDateFieldFormat::SystemShortYY:
1873 eExtDateFormat = ExtDateFieldFormat::SystemShortYYYY; break;
1874 case ExtDateFieldFormat::ShortDDMMYY:
1875 eExtDateFormat = ExtDateFieldFormat::ShortDDMMYYYY; break;
1876 case ExtDateFieldFormat::ShortMMDDYY:
1877 eExtDateFormat = ExtDateFieldFormat::ShortMMDDYYYY; break;
1878 case ExtDateFieldFormat::ShortYYMMDD:
1879 eExtDateFormat = ExtDateFieldFormat::ShortYYYYMMDD; break;
1880 case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
1881 eExtDateFormat = ExtDateFieldFormat::ShortYYYYMMDD_DIN5008; break;
1882 default:
1883 ;
1884 }
1885 }
1886 else
1887 {
1888 switch (eExtDateFormat)
1889 {
1890 case ExtDateFieldFormat::SystemShort:
1891 case ExtDateFieldFormat::SystemShortYYYY:
1892 eExtDateFormat = ExtDateFieldFormat::SystemShortYY; break;
1893 case ExtDateFieldFormat::ShortDDMMYYYY:
1894 eExtDateFormat = ExtDateFieldFormat::ShortDDMMYY; break;
1895 case ExtDateFieldFormat::ShortMMDDYYYY:
1896 eExtDateFormat = ExtDateFieldFormat::ShortMMDDYY; break;
1897 case ExtDateFieldFormat::ShortYYYYMMDD:
1898 eExtDateFormat = ExtDateFieldFormat::ShortYYMMDD; break;
1899 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1900 eExtDateFormat = ExtDateFieldFormat::ShortYYMMDD_DIN5008; break;
1901 default:
1902 ;
1903 }
1904 }
1905
1906 return eExtDateFormat;
1907 }
1908 }
1909
SetShowDateCentury(bool bShowDateCentury)1910 void DateFormatter::SetShowDateCentury( bool bShowDateCentury )
1911 {
1912 mbShowDateCentury = bShowDateCentury;
1913
1914 SetExtDateFormat(ChangeDateCentury(GetExtDateFormat(), bShowDateCentury));
1915
1916 ReformatAll();
1917 }
1918
SetDate(const Date & rNewDate)1919 void DateFormatter::SetDate( const Date& rNewDate )
1920 {
1921 ImplSetUserDate( rNewDate );
1922 maFieldDate = maLastDate;
1923 maLastDate = GetDate();
1924 }
1925
ImplSetUserDate(const Date & rNewDate,Selection const * pNewSelection)1926 void DateFormatter::ImplSetUserDate( const Date& rNewDate, Selection const * pNewSelection )
1927 {
1928 Date aNewDate = rNewDate;
1929 if ( aNewDate > maMax )
1930 aNewDate = maMax;
1931 else if ( aNewDate < maMin )
1932 aNewDate = maMin;
1933 maLastDate = aNewDate;
1934
1935 if ( GetField() )
1936 ImplSetText( ImplGetDateAsText( aNewDate ), pNewSelection );
1937 }
1938
ImplNewFieldValue(const Date & rDate)1939 void DateFormatter::ImplNewFieldValue( const Date& rDate )
1940 {
1941 if ( !GetField() )
1942 return;
1943
1944 Selection aSelection = GetField()->GetSelection();
1945 aSelection.Justify();
1946 OUString aText = GetField()->GetText();
1947
1948 // If selected until the end then keep it that way
1949 if ( static_cast<sal_Int32>(aSelection.Max()) == aText.getLength() )
1950 {
1951 if ( !aSelection.Len() )
1952 aSelection.Min() = SELECTION_MAX;
1953 aSelection.Max() = SELECTION_MAX;
1954 }
1955
1956 Date aOldLastDate = maLastDate;
1957 ImplSetUserDate( rDate, &aSelection );
1958 maLastDate = aOldLastDate;
1959
1960 // Modify at Edit is only set at KeyInput
1961 if ( GetField()->GetText() != aText )
1962 {
1963 GetField()->SetModifyFlag();
1964 GetField()->Modify();
1965 }
1966 }
1967
GetDate() const1968 Date DateFormatter::GetDate() const
1969 {
1970 Date aDate( Date::EMPTY );
1971
1972 if ( GetField() )
1973 {
1974 if (TextToDate(GetField()->GetText(), aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper()))
1975 {
1976 if ( aDate > maMax )
1977 aDate = maMax;
1978 else if ( aDate < maMin )
1979 aDate = maMin;
1980 }
1981 else
1982 {
1983 // !!! We should find out why dates are treated differently than other fields (see
1984 // also bug: 52384)
1985
1986 if ( !ImplAllowMalformedInput() )
1987 {
1988 if ( maLastDate.GetDate() )
1989 aDate = maLastDate;
1990 else if ( !IsEmptyFieldValueEnabled() )
1991 aDate = Date( Date::SYSTEM );
1992 }
1993 else
1994 aDate = Date( Date::EMPTY ); // set invalid date
1995 }
1996 }
1997
1998 return aDate;
1999 }
2000
SetEmptyDate()2001 void DateFormatter::SetEmptyDate()
2002 {
2003 FormatterBase::SetEmptyFieldValue();
2004 }
2005
IsEmptyDate() const2006 bool DateFormatter::IsEmptyDate() const
2007 {
2008 bool bEmpty = FormatterBase::IsEmptyFieldValue();
2009
2010 if ( GetField() && MustBeReformatted() && IsEmptyFieldValueEnabled() )
2011 {
2012 if ( GetField()->GetText().isEmpty() )
2013 {
2014 bEmpty = true;
2015 }
2016 else if ( !maLastDate.GetDate() )
2017 {
2018 Date aDate( Date::EMPTY );
2019 bEmpty = !TextToDate(GetField()->GetText(), aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper());
2020 }
2021 }
2022 return bEmpty;
2023 }
2024
Reformat()2025 void DateFormatter::Reformat()
2026 {
2027 if ( !GetField() )
2028 return;
2029
2030 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
2031 return;
2032
2033 OUString aStr;
2034 ImplDateReformat( GetField()->GetText(), aStr );
2035
2036 if ( !aStr.isEmpty() )
2037 {
2038 ImplSetText( aStr );
2039 (void)TextToDate(aStr, maLastDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper());
2040 }
2041 else
2042 {
2043 if ( maLastDate.GetDate() )
2044 SetDate( maLastDate );
2045 else if ( !IsEmptyFieldValueEnabled() )
2046 SetDate( Date( Date::SYSTEM ) );
2047 else
2048 {
2049 ImplSetText( OUString() );
2050 SetEmptyFieldValueData( true );
2051 }
2052 }
2053 }
2054
ExpandCentury(Date & rDate)2055 void DateFormatter::ExpandCentury( Date& rDate )
2056 {
2057 ExpandCentury(rDate, officecfg::Office::Common::DateFormat::TwoDigitYear::get());
2058 }
2059
ExpandCentury(Date & rDate,sal_uInt16 nTwoDigitYearStart)2060 void DateFormatter::ExpandCentury( Date& rDate, sal_uInt16 nTwoDigitYearStart )
2061 {
2062 sal_Int16 nDateYear = rDate.GetYear();
2063 if ( 0 <= nDateYear && nDateYear < 100 )
2064 {
2065 sal_uInt16 nCentury = nTwoDigitYearStart / 100;
2066 if ( nDateYear < (nTwoDigitYearStart % 100) )
2067 nCentury++;
2068 rDate.SetYear( nDateYear + (nCentury*100) );
2069 }
2070 }
2071
DateField(vcl::Window * pParent,WinBits nWinStyle)2072 DateField::DateField( vcl::Window* pParent, WinBits nWinStyle ) :
2073 SpinField( pParent, nWinStyle ),
2074 DateFormatter(this),
2075 maFirst( GetMin() ),
2076 maLast( GetMax() )
2077 {
2078 SetText( ImplGetLocaleDataWrapper().getDate( ImplGetFieldDate() ) );
2079 Reformat();
2080 ResetLastDate();
2081 }
2082
dispose()2083 void DateField::dispose()
2084 {
2085 ClearField();
2086 SpinField::dispose();
2087 }
2088
PreNotify(NotifyEvent & rNEvt)2089 bool DateField::PreNotify( NotifyEvent& rNEvt )
2090 {
2091 if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && IsStrictFormat() &&
2092 ( GetExtDateFormat() != ExtDateFieldFormat::SystemLong ) &&
2093 !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
2094 {
2095 if ( ImplDateProcessKeyInput( *rNEvt.GetKeyEvent(), GetExtDateFormat( true ), ImplGetLocaleDataWrapper() ) )
2096 return true;
2097 }
2098
2099 return SpinField::PreNotify( rNEvt );
2100 }
2101
EventNotify(NotifyEvent & rNEvt)2102 bool DateField::EventNotify( NotifyEvent& rNEvt )
2103 {
2104 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
2105 MarkToBeReformatted( false );
2106 else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
2107 {
2108 if ( MustBeReformatted() )
2109 {
2110 // !!! We should find out why dates are treated differently than other fields (see
2111 // also bug: 52384)
2112
2113 bool bTextLen = !GetText().isEmpty();
2114 if ( bTextLen || !IsEmptyFieldValueEnabled() )
2115 {
2116 if ( !ImplAllowMalformedInput() )
2117 Reformat();
2118 else
2119 {
2120 Date aDate( 0, 0, 0 );
2121 if (TextToDate(GetText(), aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper()))
2122 // even with strict text analysis, our text is a valid date -> do a complete
2123 // reformat
2124 Reformat();
2125 }
2126 }
2127 else
2128 {
2129 ResetLastDate();
2130 SetEmptyFieldValueData( true );
2131 }
2132 }
2133 }
2134
2135 return SpinField::EventNotify( rNEvt );
2136 }
2137
DataChanged(const DataChangedEvent & rDCEvt)2138 void DateField::DataChanged( const DataChangedEvent& rDCEvt )
2139 {
2140 SpinField::DataChanged( rDCEvt );
2141
2142 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & (AllSettingsFlags::LOCALE|AllSettingsFlags::MISC)) )
2143 {
2144 if (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE)
2145 ImplResetLocaleDataWrapper();
2146 ReformatAll();
2147 }
2148 }
2149
Modify()2150 void DateField::Modify()
2151 {
2152 MarkToBeReformatted( true );
2153 SpinField::Modify();
2154 }
2155
Up()2156 void DateField::Up()
2157 {
2158 ImplDateSpinArea( true );
2159 SpinField::Up();
2160 }
2161
Down()2162 void DateField::Down()
2163 {
2164 ImplDateSpinArea( false );
2165 SpinField::Down();
2166 }
2167
First()2168 void DateField::First()
2169 {
2170 ImplNewFieldValue( maFirst );
2171 SpinField::First();
2172 }
2173
Last()2174 void DateField::Last()
2175 {
2176 ImplNewFieldValue( maLast );
2177 SpinField::Last();
2178 }
2179
DateBox(vcl::Window * pParent,WinBits nWinStyle)2180 DateBox::DateBox(vcl::Window* pParent, WinBits nWinStyle)
2181 : ComboBox( pParent, nWinStyle )
2182 , DateFormatter(this)
2183 {
2184 SetText( ImplGetLocaleDataWrapper().getDate( ImplGetFieldDate() ) );
2185 Reformat();
2186 }
2187
dispose()2188 void DateBox::dispose()
2189 {
2190 ClearField();
2191 ComboBox::dispose();
2192 }
2193
PreNotify(NotifyEvent & rNEvt)2194 bool DateBox::PreNotify( NotifyEvent& rNEvt )
2195 {
2196 if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && IsStrictFormat() &&
2197 ( GetExtDateFormat() != ExtDateFieldFormat::SystemLong ) &&
2198 !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
2199 {
2200 if ( ImplDateProcessKeyInput( *rNEvt.GetKeyEvent(), GetExtDateFormat( true ), ImplGetLocaleDataWrapper() ) )
2201 return true;
2202 }
2203
2204 return ComboBox::PreNotify( rNEvt );
2205 }
2206
DataChanged(const DataChangedEvent & rDCEvt)2207 void DateBox::DataChanged( const DataChangedEvent& rDCEvt )
2208 {
2209 ComboBox::DataChanged( rDCEvt );
2210
2211 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
2212 {
2213 ImplResetLocaleDataWrapper();
2214 ReformatAll();
2215 }
2216 }
2217
EventNotify(NotifyEvent & rNEvt)2218 bool DateBox::EventNotify( NotifyEvent& rNEvt )
2219 {
2220 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
2221 MarkToBeReformatted( false );
2222 else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
2223 {
2224 if ( MustBeReformatted() )
2225 {
2226 bool bTextLen = !GetText().isEmpty();
2227 if ( bTextLen || !IsEmptyFieldValueEnabled() )
2228 Reformat();
2229 else
2230 {
2231 ResetLastDate();
2232 SetEmptyFieldValueData( true );
2233 }
2234 }
2235 }
2236
2237 return ComboBox::EventNotify( rNEvt );
2238 }
2239
Modify()2240 void DateBox::Modify()
2241 {
2242 MarkToBeReformatted( true );
2243 ComboBox::Modify();
2244 }
2245
ReformatAll()2246 void DateBox::ReformatAll()
2247 {
2248 OUString aStr;
2249 SetUpdateMode( false );
2250 const sal_Int32 nEntryCount = GetEntryCount();
2251 for ( sal_Int32 i=0; i < nEntryCount; ++i )
2252 {
2253 ImplDateReformat( GetEntry( i ), aStr );
2254 RemoveEntryAt(i);
2255 InsertEntry( aStr, i );
2256 }
2257 DateFormatter::Reformat();
2258 SetUpdateMode( true );
2259 }
2260
2261 namespace weld
2262 {
GetCalendarWrapper() const2263 CalendarWrapper& DateFormatter::GetCalendarWrapper() const
2264 {
2265 if (!m_xCalendarWrapper)
2266 {
2267 m_xCalendarWrapper.reset(new CalendarWrapper(comphelper::getProcessComponentContext()));
2268 m_xCalendarWrapper->loadDefaultCalendar(Application::GetSettings().GetLanguageTag().getLocale());
2269 }
2270 return *m_xCalendarWrapper;
2271 }
2272
SetShowDateCentury(bool bShowDateCentury)2273 void DateFormatter::SetShowDateCentury(bool bShowDateCentury)
2274 {
2275 m_eFormat = ChangeDateCentury(m_eFormat, bShowDateCentury);
2276
2277 ReFormat();
2278 }
2279
SetDate(const Date & rDate)2280 void DateFormatter::SetDate(const Date& rDate)
2281 {
2282 auto nDate = rDate.GetDate();
2283 bool bForceOutput = GetEntryText().isEmpty() && rDate == GetDate();
2284 if (bForceOutput)
2285 {
2286 ImplSetValue(nDate, true);
2287 return;
2288 }
2289 SetValue(nDate);
2290 }
2291
GetDate()2292 Date DateFormatter::GetDate()
2293 {
2294 return Date(GetValue());
2295 }
2296
SetMin(const Date & rNewMin)2297 void DateFormatter::SetMin(const Date& rNewMin)
2298 {
2299 SetMinValue(rNewMin.GetDate());
2300 }
2301
SetMax(const Date & rNewMax)2302 void DateFormatter::SetMax(const Date& rNewMax)
2303 {
2304 SetMaxValue(rNewMax.GetDate());
2305 }
2306
FormatNumber(int nValue) const2307 OUString DateFormatter::FormatNumber(int nValue) const
2308 {
2309 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
2310 return ::DateFormatter::FormatDate(Date(nValue), m_eFormat, rLocaleData, m_aStaticFormatter);
2311 }
2312
IMPL_LINK_NOARG(DateFormatter,FormatOutputHdl,LinkParamNone *,bool)2313 IMPL_LINK_NOARG(DateFormatter, FormatOutputHdl, LinkParamNone*, bool)
2314 {
2315 OUString sText = FormatNumber(GetValue());
2316 ImplSetTextImpl(sText, nullptr);
2317 return true;
2318 }
2319
IMPL_LINK(DateFormatter,ParseInputHdl,sal_Int64 *,result,TriState)2320 IMPL_LINK(DateFormatter, ParseInputHdl, sal_Int64*, result, TriState)
2321 {
2322 const LocaleDataWrapper& rLocaleDataWrapper = Application::GetSettings().GetLocaleDataWrapper();
2323
2324 Date aResult(Date::EMPTY);
2325 bool bRet = ::DateFormatter::TextToDate(GetEntryText(), aResult, ResolveSystemFormat(m_eFormat, rLocaleDataWrapper),
2326 rLocaleDataWrapper, GetCalendarWrapper());
2327 if (bRet)
2328 *result = aResult.GetDate();
2329
2330 return bRet ? TRISTATE_TRUE : TRISTATE_FALSE;
2331 }
2332 }
2333
ImplTimeProcessKeyInput(const KeyEvent & rKEvt,bool bStrictFormat,bool bDuration,TimeFieldFormat eFormat,const LocaleDataWrapper & rLocaleDataWrapper)2334 static bool ImplTimeProcessKeyInput( const KeyEvent& rKEvt,
2335 bool bStrictFormat, bool bDuration,
2336 TimeFieldFormat eFormat,
2337 const LocaleDataWrapper& rLocaleDataWrapper )
2338 {
2339 sal_Unicode cChar = rKEvt.GetCharCode();
2340
2341 if ( !bStrictFormat )
2342 return false;
2343 else
2344 {
2345 sal_uInt16 nGroup = rKEvt.GetKeyCode().GetGroup();
2346 if ( (nGroup == KEYGROUP_FKEYS) || (nGroup == KEYGROUP_CURSOR) ||
2347 (nGroup == KEYGROUP_MISC) ||
2348 ((cChar >= '0') && (cChar <= '9')) ||
2349 rLocaleDataWrapper.getTimeSep() == OUStringChar(cChar) ||
2350 (rLocaleDataWrapper.getTimeAM().indexOf(cChar) != -1) ||
2351 (rLocaleDataWrapper.getTimePM().indexOf(cChar) != -1) ||
2352 // Accept AM/PM:
2353 (cChar == 'a') || (cChar == 'A') || (cChar == 'm') || (cChar == 'M') || (cChar == 'p') || (cChar == 'P') ||
2354 ((eFormat == TimeFieldFormat::F_SEC_CS) && rLocaleDataWrapper.getTime100SecSep() == OUStringChar(cChar)) ||
2355 (bDuration && (cChar == '-')) )
2356 return false;
2357 else
2358 return true;
2359 }
2360 }
2361
ImplIsOnlyDigits(const OUStringBuffer & _rStr)2362 static bool ImplIsOnlyDigits( const OUStringBuffer& _rStr )
2363 {
2364 const sal_Unicode* _pChr = _rStr.getStr();
2365 for ( sal_Int32 i = 0; i < _rStr.getLength(); ++i, ++_pChr )
2366 {
2367 if ( *_pChr < '0' || *_pChr > '9' )
2368 return false;
2369 }
2370 return true;
2371 }
2372
ImplIsValidTimePortion(bool _bSkipInvalidCharacters,const OUStringBuffer & _rStr)2373 static bool ImplIsValidTimePortion( bool _bSkipInvalidCharacters, const OUStringBuffer& _rStr )
2374 {
2375 if ( !_bSkipInvalidCharacters )
2376 {
2377 if ( ( _rStr.getLength() > 2 ) || _rStr.isEmpty() || !ImplIsOnlyDigits( _rStr ) )
2378 return false;
2379 }
2380 return true;
2381 }
2382
ImplCutTimePortion(OUStringBuffer & _rStr,sal_Int32 _nSepPos,bool _bSkipInvalidCharacters,short * _pPortion)2383 static bool ImplCutTimePortion( OUStringBuffer& _rStr, sal_Int32 _nSepPos, bool _bSkipInvalidCharacters, short* _pPortion )
2384 {
2385 OUString sPortion(_rStr.getStr(), _nSepPos );
2386
2387 if (_nSepPos < _rStr.getLength())
2388 _rStr.remove(0, _nSepPos + 1);
2389 else
2390 _rStr.truncate();
2391
2392 if ( !ImplIsValidTimePortion( _bSkipInvalidCharacters, sPortion ) )
2393 return false;
2394 *_pPortion = static_cast<short>(sPortion.toInt32());
2395 return true;
2396 }
2397
TextToTime(const OUString & rStr,tools::Time & rTime,TimeFieldFormat eFormat,bool bDuration,const LocaleDataWrapper & rLocaleDataWrapper,bool _bSkipInvalidCharacters)2398 bool TimeFormatter::TextToTime(const OUString& rStr, tools::Time& rTime, TimeFieldFormat eFormat,
2399 bool bDuration, const LocaleDataWrapper& rLocaleDataWrapper, bool _bSkipInvalidCharacters)
2400 {
2401 OUStringBuffer aStr = rStr;
2402 short nHour = 0;
2403 short nMinute = 0;
2404 short nSecond = 0;
2405 sal_Int64 nNanoSec = 0;
2406 tools::Time aTime( 0, 0, 0 );
2407
2408 if ( rStr.isEmpty() )
2409 return false;
2410
2411 // Search for separators
2412 if (!rLocaleDataWrapper.getTimeSep().isEmpty())
2413 {
2414 OUStringBuffer aSepStr(",.;:/");
2415 if ( !bDuration )
2416 aSepStr.append('-');
2417
2418 // Replace characters above by the separator character
2419 for (sal_Int32 i = 0; i < aSepStr.getLength(); ++i)
2420 {
2421 if (rLocaleDataWrapper.getTimeSep() == OUStringChar(aSepStr[i]))
2422 continue;
2423 for ( sal_Int32 j = 0; j < aStr.getLength(); j++ )
2424 {
2425 if (aStr[j] == aSepStr[i])
2426 aStr[j] = rLocaleDataWrapper.getTimeSep()[0];
2427 }
2428 }
2429 }
2430
2431 bool bNegative = false;
2432 sal_Int32 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2433 if ( aStr[0] == '-' )
2434 bNegative = true;
2435 if ( eFormat != TimeFieldFormat::F_SEC_CS )
2436 {
2437 if ( nSepPos < 0 )
2438 nSepPos = aStr.getLength();
2439 if ( !ImplCutTimePortion( aStr, nSepPos, _bSkipInvalidCharacters, &nHour ) )
2440 return false;
2441
2442 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2443 if ( !aStr.isEmpty() && aStr[0] == '-' )
2444 bNegative = true;
2445 if ( nSepPos >= 0 )
2446 {
2447 if ( !ImplCutTimePortion( aStr, nSepPos, _bSkipInvalidCharacters, &nMinute ) )
2448 return false;
2449
2450 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2451 if ( !aStr.isEmpty() && aStr[0] == '-' )
2452 bNegative = true;
2453 if ( nSepPos >= 0 )
2454 {
2455 if ( !ImplCutTimePortion( aStr, nSepPos, _bSkipInvalidCharacters, &nSecond ) )
2456 return false;
2457 if ( !aStr.isEmpty() && aStr[0] == '-' )
2458 bNegative = true;
2459 nNanoSec = aStr.toString().toInt64();
2460 }
2461 else
2462 nSecond = static_cast<short>(aStr.toString().toInt32());
2463 }
2464 else
2465 nMinute = static_cast<short>(aStr.toString().toInt32());
2466 }
2467 else if ( nSepPos < 0 )
2468 {
2469 nSecond = static_cast<short>(aStr.toString().toInt32());
2470 nMinute += nSecond / 60;
2471 nSecond %= 60;
2472 nHour += nMinute / 60;
2473 nMinute %= 60;
2474 }
2475 else
2476 {
2477 nSecond = static_cast<short>(aStr.copy( 0, nSepPos ).makeStringAndClear().toInt32());
2478 aStr.remove( 0, nSepPos+1 );
2479
2480 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2481 if ( !aStr.isEmpty() && aStr[0] == '-' )
2482 bNegative = true;
2483 if ( nSepPos >= 0 )
2484 {
2485 nMinute = nSecond;
2486 nSecond = static_cast<short>(aStr.copy( 0, nSepPos ).makeStringAndClear().toInt32());
2487 aStr.remove( 0, nSepPos+1 );
2488
2489 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2490 if ( !aStr.isEmpty() && aStr[0] == '-' )
2491 bNegative = true;
2492 if ( nSepPos >= 0 )
2493 {
2494 nHour = nMinute;
2495 nMinute = nSecond;
2496 nSecond = static_cast<short>(aStr.copy( 0, nSepPos ).makeStringAndClear().toInt32());
2497 aStr.remove( 0, nSepPos+1 );
2498 }
2499 else
2500 {
2501 nHour += nMinute / 60;
2502 nMinute %= 60;
2503 }
2504 }
2505 else
2506 {
2507 nMinute += nSecond / 60;
2508 nSecond %= 60;
2509 nHour += nMinute / 60;
2510 nMinute %= 60;
2511 }
2512 nNanoSec = aStr.toString().toInt64();
2513 }
2514
2515 if ( nNanoSec )
2516 {
2517 assert(aStr.getLength() >= 1);
2518
2519 sal_Int32 nLen = 1; // at least one digit, otherwise nNanoSec==0
2520
2521 while ( aStr.getLength() > nLen && aStr[nLen] >= '0' && aStr[nLen] <= '9' )
2522 nLen++;
2523
2524 while ( nLen < 9)
2525 {
2526 nNanoSec *= 10;
2527 ++nLen;
2528 }
2529 while ( nLen > 9 )
2530 {
2531 // round if negative?
2532 nNanoSec = (nNanoSec + 5) / 10;
2533 --nLen;
2534 }
2535 }
2536
2537 assert(nNanoSec > -1000000000 && nNanoSec < 1000000000);
2538 if ( (nMinute > 59) || (nSecond > 59) || (nNanoSec > 1000000000) )
2539 return false;
2540
2541 if ( eFormat == TimeFieldFormat::F_NONE )
2542 nSecond = nNanoSec = 0;
2543 else if ( eFormat == TimeFieldFormat::F_SEC )
2544 nNanoSec = 0;
2545
2546 if ( !bDuration )
2547 {
2548 if ( bNegative || (nHour < 0) || (nMinute < 0) ||
2549 (nSecond < 0) || (nNanoSec < 0) )
2550 return false;
2551
2552 OUString aUpperCaseStr = aStr.toString().toAsciiUpperCase();
2553 OUString aAMlocalised(rLocaleDataWrapper.getTimeAM().toAsciiUpperCase());
2554 OUString aPMlocalised(rLocaleDataWrapper.getTimePM().toAsciiUpperCase());
2555
2556 if ( (nHour < 12) && ( ( aUpperCaseStr.indexOf( "PM" ) >= 0 ) || ( aUpperCaseStr.indexOf( aPMlocalised ) >= 0 ) ) )
2557 nHour += 12;
2558
2559 if ( (nHour == 12) && ( ( aUpperCaseStr.indexOf( "AM" ) >= 0 ) || ( aUpperCaseStr.indexOf( aAMlocalised ) >= 0 ) ) )
2560 nHour = 0;
2561
2562 aTime = tools::Time( static_cast<sal_uInt16>(nHour), static_cast<sal_uInt16>(nMinute), static_cast<sal_uInt16>(nSecond),
2563 static_cast<sal_uInt32>(nNanoSec) );
2564 }
2565 else
2566 {
2567 assert( !bNegative || (nHour < 0) || (nMinute < 0) ||
2568 (nSecond < 0) || (nNanoSec < 0) );
2569 if ( bNegative || (nHour < 0) || (nMinute < 0) ||
2570 (nSecond < 0) || (nNanoSec < 0) )
2571 {
2572 // LEM TODO: this looks weird... I think buggy when parsing "05:-02:18"
2573 bNegative = true;
2574 nHour = nHour < 0 ? -nHour : nHour;
2575 nMinute = nMinute < 0 ? -nMinute : nMinute;
2576 nSecond = nSecond < 0 ? -nSecond : nSecond;
2577 nNanoSec = nNanoSec < 0 ? -nNanoSec : nNanoSec;
2578 }
2579
2580 aTime = tools::Time( static_cast<sal_uInt16>(nHour), static_cast<sal_uInt16>(nMinute), static_cast<sal_uInt16>(nSecond),
2581 static_cast<sal_uInt32>(nNanoSec) );
2582 if ( bNegative )
2583 aTime = -aTime;
2584 }
2585
2586 rTime = aTime;
2587
2588 return true;
2589 }
2590
ImplTimeReformat(const OUString & rStr,OUString & rOutStr)2591 void TimeFormatter::ImplTimeReformat( const OUString& rStr, OUString& rOutStr )
2592 {
2593 tools::Time aTime( 0, 0, 0 );
2594 if ( !TextToTime( rStr, aTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper() ) )
2595 return;
2596
2597 tools::Time aTempTime = aTime;
2598 if ( aTempTime > GetMax() )
2599 aTempTime = GetMax() ;
2600 else if ( aTempTime < GetMin() )
2601 aTempTime = GetMin();
2602
2603 bool bSecond = false;
2604 bool b100Sec = false;
2605 if ( meFormat != TimeFieldFormat::F_NONE )
2606 bSecond = true;
2607
2608 if ( meFormat == TimeFieldFormat::F_SEC_CS )
2609 {
2610 sal_uLong n = aTempTime.GetHour() * 3600L;
2611 n += aTempTime.GetMin() * 60L;
2612 n += aTempTime.GetSec();
2613 rOutStr = OUString::number( n );
2614 rOutStr += ImplGetLocaleDataWrapper().getTime100SecSep();
2615 std::ostringstream ostr;
2616 ostr.fill('0');
2617 ostr.width(9);
2618 ostr << aTempTime.GetNanoSec();
2619 rOutStr += OUString::createFromAscii(ostr.str().c_str());
2620 }
2621 else if ( mbDuration )
2622 rOutStr = ImplGetLocaleDataWrapper().getDuration( aTempTime, bSecond, b100Sec );
2623 else
2624 {
2625 rOutStr = ImplGetLocaleDataWrapper().getTime( aTempTime, bSecond, b100Sec );
2626 if ( GetTimeFormat() == TimeFormat::Hour12 )
2627 {
2628 if ( aTempTime.GetHour() > 12 )
2629 {
2630 tools::Time aT( aTempTime );
2631 aT.SetHour( aT.GetHour() % 12 );
2632 rOutStr = ImplGetLocaleDataWrapper().getTime( aT, bSecond, b100Sec );
2633 }
2634 // Don't use LocaleDataWrapper, we want AM/PM
2635 if ( aTempTime.GetHour() < 12 )
2636 rOutStr += "AM"; // ImplGetLocaleDataWrapper().getTimeAM();
2637 else
2638 rOutStr += "PM"; // ImplGetLocaleDataWrapper().getTimePM();
2639 }
2640 }
2641 }
2642
ImplAllowMalformedInput() const2643 bool TimeFormatter::ImplAllowMalformedInput() const
2644 {
2645 return !IsEnforceValidValue();
2646 }
2647
GetTimeArea(TimeFieldFormat eFormat,const OUString & rText,int nCursor,const LocaleDataWrapper & rLocaleDataWrapper)2648 int TimeFormatter::GetTimeArea(TimeFieldFormat eFormat, const OUString& rText, int nCursor,
2649 const LocaleDataWrapper& rLocaleDataWrapper)
2650 {
2651 int nTimeArea = 0;
2652
2653 // Area search
2654 if (eFormat != TimeFieldFormat::F_SEC_CS)
2655 {
2656 //Which area is the cursor in of HH:MM:SS.TT
2657 for ( sal_Int32 i = 1, nPos = 0; i <= 4; i++ )
2658 {
2659 sal_Int32 nPos1 = rText.indexOf(rLocaleDataWrapper.getTimeSep(), nPos);
2660 sal_Int32 nPos2 = rText.indexOf(rLocaleDataWrapper.getTime100SecSep(), nPos);
2661 //which ever comes first, bearing in mind that one might not be there
2662 if (nPos1 >= 0 && nPos2 >= 0)
2663 nPos = std::min(nPos1, nPos2);
2664 else if (nPos1 >= 0)
2665 nPos = nPos1;
2666 else
2667 nPos = nPos2;
2668 if (nPos < 0 || nPos >= nCursor)
2669 {
2670 nTimeArea = i;
2671 break;
2672 }
2673 else
2674 nPos++;
2675 }
2676 }
2677 else
2678 {
2679 sal_Int32 nPos = rText.indexOf(rLocaleDataWrapper.getTime100SecSep());
2680 if (nPos < 0 || nPos >= nCursor)
2681 nTimeArea = 3;
2682 else
2683 nTimeArea = 4;
2684 }
2685
2686 return nTimeArea;
2687 }
2688
SpinTime(bool bUp,const tools::Time & rTime,TimeFieldFormat eFormat,bool bDuration,const OUString & rText,int nCursor,const LocaleDataWrapper & rLocaleDataWrapper)2689 tools::Time TimeFormatter::SpinTime(bool bUp, const tools::Time& rTime, TimeFieldFormat eFormat,
2690 bool bDuration, const OUString& rText, int nCursor,
2691 const LocaleDataWrapper& rLocaleDataWrapper)
2692 {
2693 tools::Time aTime(rTime);
2694
2695 int nTimeArea = GetTimeArea(eFormat, rText, nCursor, rLocaleDataWrapper);
2696
2697 if ( nTimeArea )
2698 {
2699 tools::Time aAddTime( 0, 0, 0 );
2700 if ( nTimeArea == 1 )
2701 aAddTime = tools::Time( 1, 0 );
2702 else if ( nTimeArea == 2 )
2703 aAddTime = tools::Time( 0, 1 );
2704 else if ( nTimeArea == 3 )
2705 aAddTime = tools::Time( 0, 0, 1 );
2706 else if ( nTimeArea == 4 )
2707 aAddTime = tools::Time( 0, 0, 0, 1 );
2708
2709 if ( !bUp )
2710 aAddTime = -aAddTime;
2711
2712 aTime += aAddTime;
2713 if (!bDuration)
2714 {
2715 tools::Time aAbsMaxTime( 23, 59, 59, 999999999 );
2716 if ( aTime > aAbsMaxTime )
2717 aTime = aAbsMaxTime;
2718 tools::Time aAbsMinTime( 0, 0 );
2719 if ( aTime < aAbsMinTime )
2720 aTime = aAbsMinTime;
2721 }
2722 }
2723
2724 return aTime;
2725 }
2726
ImplTimeSpinArea(bool bUp)2727 void TimeField::ImplTimeSpinArea( bool bUp )
2728 {
2729 if ( GetField() )
2730 {
2731 tools::Time aTime( GetTime() );
2732 OUString aText( GetText() );
2733 Selection aSelection( GetField()->GetSelection() );
2734
2735 aTime = TimeFormatter::SpinTime(bUp, aTime, GetFormat(), IsDuration(), aText, aSelection.Max(), ImplGetLocaleDataWrapper());
2736
2737 ImplNewFieldValue( aTime );
2738 }
2739 }
2740
TimeFormatter(Edit * pEdit)2741 TimeFormatter::TimeFormatter(Edit* pEdit)
2742 : FormatterBase(pEdit)
2743 , maLastTime(0, 0)
2744 , maMin(0, 0)
2745 , maMax(23, 59, 59, 999999999)
2746 , meFormat(TimeFieldFormat::F_NONE)
2747 , mnTimeFormat(TimeFormat::Hour24) // Should become an ExtTimeFieldFormat in next implementation, merge with mbDuration and meFormat
2748 , mbDuration(false)
2749 , mbEnforceValidValue(true)
2750 , maFieldTime(0, 0)
2751 {
2752 }
2753
~TimeFormatter()2754 TimeFormatter::~TimeFormatter()
2755 {
2756 }
2757
ReformatAll()2758 void TimeFormatter::ReformatAll()
2759 {
2760 Reformat();
2761 }
2762
SetMin(const tools::Time & rNewMin)2763 void TimeFormatter::SetMin( const tools::Time& rNewMin )
2764 {
2765 maMin = rNewMin;
2766 if ( !IsEmptyFieldValue() )
2767 ReformatAll();
2768 }
2769
SetMax(const tools::Time & rNewMax)2770 void TimeFormatter::SetMax( const tools::Time& rNewMax )
2771 {
2772 maMax = rNewMax;
2773 if ( !IsEmptyFieldValue() )
2774 ReformatAll();
2775 }
2776
SetTimeFormat(TimeFormat eNewFormat)2777 void TimeFormatter::SetTimeFormat( TimeFormat eNewFormat )
2778 {
2779 mnTimeFormat = eNewFormat;
2780 }
2781
2782
SetFormat(TimeFieldFormat eNewFormat)2783 void TimeFormatter::SetFormat( TimeFieldFormat eNewFormat )
2784 {
2785 meFormat = eNewFormat;
2786 ReformatAll();
2787 }
2788
SetDuration(bool bNewDuration)2789 void TimeFormatter::SetDuration( bool bNewDuration )
2790 {
2791 mbDuration = bNewDuration;
2792 ReformatAll();
2793 }
2794
SetTime(const tools::Time & rNewTime)2795 void TimeFormatter::SetTime( const tools::Time& rNewTime )
2796 {
2797 SetUserTime( rNewTime );
2798 maFieldTime = maLastTime;
2799 SetEmptyFieldValueData( false );
2800 }
2801
ImplNewFieldValue(const tools::Time & rTime)2802 void TimeFormatter::ImplNewFieldValue( const tools::Time& rTime )
2803 {
2804 if ( !GetField() )
2805 return;
2806
2807 Selection aSelection = GetField()->GetSelection();
2808 aSelection.Justify();
2809 OUString aText = GetField()->GetText();
2810
2811 // If selected until the end then keep it that way
2812 if ( static_cast<sal_Int32>(aSelection.Max()) == aText.getLength() )
2813 {
2814 if ( !aSelection.Len() )
2815 aSelection.Min() = SELECTION_MAX;
2816 aSelection.Max() = SELECTION_MAX;
2817 }
2818
2819 tools::Time aOldLastTime = maLastTime;
2820 ImplSetUserTime( rTime, &aSelection );
2821 maLastTime = aOldLastTime;
2822
2823 // Modify at Edit is only set at KeyInput
2824 if ( GetField()->GetText() != aText )
2825 {
2826 GetField()->SetModifyFlag();
2827 GetField()->Modify();
2828 }
2829 }
2830
FormatTime(const tools::Time & rNewTime,TimeFieldFormat eFormat,TimeFormat eHourFormat,bool bDuration,const LocaleDataWrapper & rLocaleData)2831 OUString TimeFormatter::FormatTime(const tools::Time& rNewTime, TimeFieldFormat eFormat, TimeFormat eHourFormat, bool bDuration, const LocaleDataWrapper& rLocaleData)
2832 {
2833 OUString aStr;
2834 bool bSec = false;
2835 bool b100Sec = false;
2836 if ( eFormat != TimeFieldFormat::F_NONE )
2837 bSec = true;
2838 if ( eFormat == TimeFieldFormat::F_SEC_CS )
2839 b100Sec = true;
2840 if ( eFormat == TimeFieldFormat::F_SEC_CS )
2841 {
2842 sal_uLong n = rNewTime.GetHour() * 3600L;
2843 n += rNewTime.GetMin() * 60L;
2844 n += rNewTime.GetSec();
2845 aStr = OUString::number( n ) + rLocaleData.getTime100SecSep();
2846 std::ostringstream ostr;
2847 ostr.fill('0');
2848 ostr.width(9);
2849 ostr << rNewTime.GetNanoSec();
2850 aStr += OUString::createFromAscii(ostr.str().c_str());
2851 }
2852 else if ( bDuration )
2853 {
2854 aStr = rLocaleData.getDuration( rNewTime, bSec, b100Sec );
2855 }
2856 else
2857 {
2858 aStr = rLocaleData.getTime( rNewTime, bSec, b100Sec );
2859 if ( eHourFormat == TimeFormat::Hour12 )
2860 {
2861 if ( rNewTime.GetHour() > 12 )
2862 {
2863 tools::Time aT( rNewTime );
2864 aT.SetHour( aT.GetHour() % 12 );
2865 aStr = rLocaleData.getTime( aT, bSec, b100Sec );
2866 }
2867 // Don't use LocaleDataWrapper, we want AM/PM
2868 if ( rNewTime.GetHour() < 12 )
2869 aStr += "AM"; // rLocaleData.getTimeAM();
2870 else
2871 aStr += "PM"; // rLocaleData.getTimePM();
2872 }
2873 }
2874
2875 return aStr;
2876 }
2877
ImplSetUserTime(const tools::Time & rNewTime,Selection const * pNewSelection)2878 void TimeFormatter::ImplSetUserTime( const tools::Time& rNewTime, Selection const * pNewSelection )
2879 {
2880 tools::Time aNewTime = rNewTime;
2881 if ( aNewTime > GetMax() )
2882 aNewTime = GetMax();
2883 else if ( aNewTime < GetMin() )
2884 aNewTime = GetMin();
2885 maLastTime = aNewTime;
2886
2887 if ( GetField() )
2888 {
2889 OUString aStr = TimeFormatter::FormatTime(aNewTime, meFormat, GetTimeFormat(), mbDuration, ImplGetLocaleDataWrapper());
2890 ImplSetText( aStr, pNewSelection );
2891 }
2892 }
2893
SetUserTime(const tools::Time & rNewTime)2894 void TimeFormatter::SetUserTime( const tools::Time& rNewTime )
2895 {
2896 ImplSetUserTime( rNewTime );
2897 }
2898
GetTime() const2899 tools::Time TimeFormatter::GetTime() const
2900 {
2901 tools::Time aTime( 0, 0, 0 );
2902
2903 if ( GetField() )
2904 {
2905 bool bAllowMalformed = ImplAllowMalformedInput();
2906 if ( TextToTime( GetField()->GetText(), aTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper(), !bAllowMalformed ) )
2907 {
2908 if ( aTime > GetMax() )
2909 aTime = GetMax();
2910 else if ( aTime < GetMin() )
2911 aTime = GetMin();
2912 }
2913 else
2914 {
2915 if ( bAllowMalformed )
2916 aTime = tools::Time( 99, 99, 99 ); // set invalid time
2917 else
2918 aTime = maLastTime;
2919 }
2920 }
2921
2922 return aTime;
2923 }
2924
Reformat()2925 void TimeFormatter::Reformat()
2926 {
2927 if ( !GetField() )
2928 return;
2929
2930 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
2931 return;
2932
2933 OUString aStr;
2934 ImplTimeReformat( GetField()->GetText(), aStr );
2935
2936 if ( !aStr.isEmpty() )
2937 {
2938 ImplSetText( aStr );
2939 (void)TextToTime(aStr, maLastTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper());
2940 }
2941 else
2942 SetTime( maLastTime );
2943 }
2944
TimeField(vcl::Window * pParent,WinBits nWinStyle)2945 TimeField::TimeField( vcl::Window* pParent, WinBits nWinStyle ) :
2946 SpinField( pParent, nWinStyle ),
2947 TimeFormatter(this),
2948 maFirst( GetMin() ),
2949 maLast( GetMax() )
2950 {
2951 SetText( ImplGetLocaleDataWrapper().getTime( maFieldTime, false ) );
2952 Reformat();
2953 }
2954
dispose()2955 void TimeField::dispose()
2956 {
2957 ClearField();
2958 SpinField::dispose();
2959 }
2960
PreNotify(NotifyEvent & rNEvt)2961 bool TimeField::PreNotify( NotifyEvent& rNEvt )
2962 {
2963 if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
2964 {
2965 if ( ImplTimeProcessKeyInput( *rNEvt.GetKeyEvent(), IsStrictFormat(), IsDuration(), GetFormat(), ImplGetLocaleDataWrapper() ) )
2966 return true;
2967 }
2968
2969 return SpinField::PreNotify( rNEvt );
2970 }
2971
EventNotify(NotifyEvent & rNEvt)2972 bool TimeField::EventNotify( NotifyEvent& rNEvt )
2973 {
2974 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
2975 MarkToBeReformatted( false );
2976 else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
2977 {
2978 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
2979 {
2980 if ( !ImplAllowMalformedInput() )
2981 Reformat();
2982 else
2983 {
2984 tools::Time aTime( 0, 0, 0 );
2985 if ( TextToTime( GetText(), aTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper(), false ) )
2986 // even with strict text analysis, our text is a valid time -> do a complete
2987 // reformat
2988 Reformat();
2989 }
2990 }
2991 }
2992
2993 return SpinField::EventNotify( rNEvt );
2994 }
2995
DataChanged(const DataChangedEvent & rDCEvt)2996 void TimeField::DataChanged( const DataChangedEvent& rDCEvt )
2997 {
2998 SpinField::DataChanged( rDCEvt );
2999
3000 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
3001 {
3002 ImplResetLocaleDataWrapper();
3003 ReformatAll();
3004 }
3005 }
3006
Modify()3007 void TimeField::Modify()
3008 {
3009 MarkToBeReformatted( true );
3010 SpinField::Modify();
3011 }
3012
Up()3013 void TimeField::Up()
3014 {
3015 ImplTimeSpinArea( true );
3016 SpinField::Up();
3017 }
3018
Down()3019 void TimeField::Down()
3020 {
3021 ImplTimeSpinArea( false );
3022 SpinField::Down();
3023 }
3024
First()3025 void TimeField::First()
3026 {
3027 ImplNewFieldValue( maFirst );
3028 SpinField::First();
3029 }
3030
Last()3031 void TimeField::Last()
3032 {
3033 ImplNewFieldValue( maLast );
3034 SpinField::Last();
3035 }
3036
SetExtFormat(ExtTimeFieldFormat eFormat)3037 void TimeField::SetExtFormat( ExtTimeFieldFormat eFormat )
3038 {
3039 switch ( eFormat )
3040 {
3041 case ExtTimeFieldFormat::Short24H:
3042 {
3043 SetTimeFormat( TimeFormat::Hour24 );
3044 SetDuration( false );
3045 SetFormat( TimeFieldFormat::F_NONE );
3046 }
3047 break;
3048 case ExtTimeFieldFormat::Long24H:
3049 {
3050 SetTimeFormat( TimeFormat::Hour24 );
3051 SetDuration( false );
3052 SetFormat( TimeFieldFormat::F_SEC );
3053 }
3054 break;
3055 case ExtTimeFieldFormat::Short12H:
3056 {
3057 SetTimeFormat( TimeFormat::Hour12 );
3058 SetDuration( false );
3059 SetFormat( TimeFieldFormat::F_NONE );
3060 }
3061 break;
3062 case ExtTimeFieldFormat::Long12H:
3063 {
3064 SetTimeFormat( TimeFormat::Hour12 );
3065 SetDuration( false );
3066 SetFormat( TimeFieldFormat::F_SEC );
3067 }
3068 break;
3069 case ExtTimeFieldFormat::ShortDuration:
3070 {
3071 SetDuration( true );
3072 SetFormat( TimeFieldFormat::F_NONE );
3073 }
3074 break;
3075 case ExtTimeFieldFormat::LongDuration:
3076 {
3077 SetDuration( true );
3078 SetFormat( TimeFieldFormat::F_SEC );
3079 }
3080 break;
3081 default: OSL_FAIL( "ExtTimeFieldFormat unknown!" );
3082 }
3083
3084 if ( GetField() && !GetField()->GetText().isEmpty() )
3085 SetUserTime( GetTime() );
3086 ReformatAll();
3087 }
3088
TimeBox(vcl::Window * pParent,WinBits nWinStyle)3089 TimeBox::TimeBox(vcl::Window* pParent, WinBits nWinStyle)
3090 : ComboBox(pParent, nWinStyle)
3091 , TimeFormatter(this)
3092 {
3093 SetText( ImplGetLocaleDataWrapper().getTime( maFieldTime, false ) );
3094 Reformat();
3095 }
3096
dispose()3097 void TimeBox::dispose()
3098 {
3099 ClearField();
3100 ComboBox::dispose();
3101 }
3102
PreNotify(NotifyEvent & rNEvt)3103 bool TimeBox::PreNotify( NotifyEvent& rNEvt )
3104 {
3105 if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
3106 {
3107 if ( ImplTimeProcessKeyInput( *rNEvt.GetKeyEvent(), IsStrictFormat(), IsDuration(), GetFormat(), ImplGetLocaleDataWrapper() ) )
3108 return true;
3109 }
3110
3111 return ComboBox::PreNotify( rNEvt );
3112 }
3113
EventNotify(NotifyEvent & rNEvt)3114 bool TimeBox::EventNotify( NotifyEvent& rNEvt )
3115 {
3116 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
3117 MarkToBeReformatted( false );
3118 else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
3119 {
3120 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
3121 Reformat();
3122 }
3123
3124 return ComboBox::EventNotify( rNEvt );
3125 }
3126
DataChanged(const DataChangedEvent & rDCEvt)3127 void TimeBox::DataChanged( const DataChangedEvent& rDCEvt )
3128 {
3129 ComboBox::DataChanged( rDCEvt );
3130
3131 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
3132 {
3133 ImplResetLocaleDataWrapper();
3134 ReformatAll();
3135 }
3136 }
3137
Modify()3138 void TimeBox::Modify()
3139 {
3140 MarkToBeReformatted( true );
3141 ComboBox::Modify();
3142 }
3143
ReformatAll()3144 void TimeBox::ReformatAll()
3145 {
3146 OUString aStr;
3147 SetUpdateMode( false );
3148 const sal_Int32 nEntryCount = GetEntryCount();
3149 for ( sal_Int32 i=0; i < nEntryCount; ++i )
3150 {
3151 ImplTimeReformat( GetEntry( i ), aStr );
3152 RemoveEntryAt(i);
3153 InsertEntry( aStr, i );
3154 }
3155 TimeFormatter::Reformat();
3156 SetUpdateMode( true );
3157 }
3158
3159 namespace weld
3160 {
ConvertValue(int nValue)3161 tools::Time TimeFormatter::ConvertValue(int nValue)
3162 {
3163 tools::Time aTime(0);
3164 aTime.MakeTimeFromMS(nValue);
3165 return aTime;
3166 }
3167
ConvertValue(const tools::Time & rTime)3168 int TimeFormatter::ConvertValue(const tools::Time& rTime)
3169 {
3170 return rTime.GetMSFromTime();
3171 }
3172
SetTime(const tools::Time & rTime)3173 void TimeFormatter::SetTime(const tools::Time& rTime)
3174 {
3175 auto nTime = ConvertValue(rTime);
3176 bool bForceOutput = GetEntryText().isEmpty() && rTime == GetTime();
3177 if (bForceOutput)
3178 {
3179 ImplSetValue(nTime, true);
3180 return;
3181 }
3182 SetValue(nTime);
3183 }
3184
GetTime()3185 tools::Time TimeFormatter::GetTime()
3186 {
3187 return ConvertValue(GetValue());
3188 }
3189
SetMin(const tools::Time & rNewMin)3190 void TimeFormatter::SetMin(const tools::Time& rNewMin)
3191 {
3192 SetMinValue(ConvertValue(rNewMin));
3193 }
3194
SetMax(const tools::Time & rNewMax)3195 void TimeFormatter::SetMax(const tools::Time& rNewMax)
3196 {
3197 SetMaxValue(ConvertValue(rNewMax));
3198 }
3199
FormatNumber(int nValue) const3200 OUString TimeFormatter::FormatNumber(int nValue) const
3201 {
3202 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
3203 return ::TimeFormatter::FormatTime(ConvertValue(nValue), m_eFormat, m_eTimeFormat, m_bDuration, rLocaleData);
3204 }
3205
IMPL_LINK_NOARG(TimeFormatter,FormatOutputHdl,LinkParamNone *,bool)3206 IMPL_LINK_NOARG(TimeFormatter, FormatOutputHdl, LinkParamNone*, bool)
3207 {
3208 OUString sText = FormatNumber(GetValue());
3209 ImplSetTextImpl(sText, nullptr);
3210 return true;
3211 }
3212
IMPL_LINK(TimeFormatter,ParseInputHdl,sal_Int64 *,result,TriState)3213 IMPL_LINK(TimeFormatter, ParseInputHdl, sal_Int64*, result, TriState)
3214 {
3215 const LocaleDataWrapper& rLocaleDataWrapper = Application::GetSettings().GetLocaleDataWrapper();
3216
3217 tools::Time aResult(0);
3218 bool bRet = ::TimeFormatter::TextToTime(GetEntryText(), aResult, m_eFormat, m_bDuration, rLocaleDataWrapper);
3219 if (bRet)
3220 *result = ConvertValue(aResult);
3221
3222 return bRet ? TRISTATE_TRUE : TRISTATE_FALSE;
3223 }
3224
IMPL_LINK(TimeFormatter,CursorChangedHdl,weld::Entry &,rEntry,void)3225 IMPL_LINK(TimeFormatter, CursorChangedHdl, weld::Entry&, rEntry, void)
3226 {
3227 int nStartPos, nEndPos;
3228 rEntry.get_selection_bounds(nStartPos, nEndPos);
3229
3230 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
3231 const int nTimeArea = ::TimeFormatter::GetTimeArea(m_eFormat, GetEntryText(), nEndPos, rLocaleData);
3232
3233 int nIncrements = 1;
3234
3235 if (nTimeArea == 1)
3236 nIncrements = 1000 * 60 * 60;
3237 else if (nTimeArea == 2)
3238 nIncrements = 1000 * 60;
3239 else if (nTimeArea == 3)
3240 nIncrements = 1000;
3241
3242 SetSpinSize(nIncrements);
3243 }
3244 }
3245
3246 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3247