bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / source / control / field2.cxx
blob3142106609ed7331f34dd85140e323af54218de2
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <algorithm>
23 #include <string_view>
25 #include <comphelper/diagnose_ex.hxx>
26 #include <comphelper/processfactory.hxx>
27 #include <comphelper/string.hxx>
28 #include <o3tl/string_view.hxx>
29 #include <officecfg/Office/Common.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/event.hxx>
32 #include <vcl/toolkit/field.hxx>
33 #include <vcl/unohelp.hxx>
34 #include <vcl/settings.hxx>
35 #include <vcl/weldutils.hxx>
37 #include <svdata.hxx>
39 #include <com/sun/star/i18n/XCharacterClassification.hpp>
40 #include <com/sun/star/i18n/CalendarFieldIndex.hdl>
42 #include <unotools/localedatawrapper.hxx>
43 #include <unotools/calendarwrapper.hxx>
44 #include <unotools/charclass.hxx>
45 #include <svl/numformat.hxx>
46 #include <svl/zforlist.hxx>
48 using namespace ::com::sun::star;
49 using namespace ::comphelper;
51 #define EDITMASK_LITERAL 'L'
52 #define EDITMASK_ALPHA 'a'
53 #define EDITMASK_UPPERALPHA 'A'
54 #define EDITMASK_ALPHANUM 'c'
55 #define EDITMASK_UPPERALPHANUM 'C'
56 #define EDITMASK_NUM 'N'
57 #define EDITMASK_NUMSPACE 'n'
58 #define EDITMASK_ALLCHAR 'x'
59 #define EDITMASK_UPPERALLCHAR 'X'
61 uno::Reference< i18n::XCharacterClassification > const & ImplGetCharClass()
63 ImplSVData *const pSVData = ImplGetSVData();
64 assert(pSVData);
66 if (!pSVData->m_xCharClass.is())
68 pSVData->m_xCharClass = vcl::unohelper::CreateCharacterClassification();
71 return pSVData->m_xCharClass;
74 static sal_Unicode* ImplAddString( sal_Unicode* pBuf, const OUString& rStr )
76 memcpy( pBuf, rStr.getStr(), rStr.getLength() * sizeof(sal_Unicode) );
77 pBuf += rStr.getLength();
78 return pBuf;
81 static sal_Unicode* ImplAddNum( sal_Unicode* pBuf, sal_uLong nNumber, int nMinLen )
83 // fill temp buffer with digits
84 sal_Unicode aTempBuf[30];
85 sal_Unicode* pTempBuf = aTempBuf;
88 *pTempBuf = static_cast<sal_Unicode>(nNumber % 10) + '0';
89 pTempBuf++;
90 nNumber /= 10;
91 if ( nMinLen )
92 nMinLen--;
94 while ( nNumber );
96 // fill with zeros up to the minimal length
97 while ( nMinLen > 0 )
99 *pBuf = '0';
100 pBuf++;
101 nMinLen--;
104 // copy temp buffer to real buffer
107 pTempBuf--;
108 *pBuf = *pTempBuf;
109 pBuf++;
111 while ( pTempBuf != aTempBuf );
113 return pBuf;
116 static sal_Unicode* ImplAddSNum( sal_Unicode* pBuf, sal_Int32 nNumber, int nMinLen )
118 if (nNumber < 0)
120 *pBuf++ = '-';
121 nNumber = -nNumber;
123 return ImplAddNum( pBuf, nNumber, nMinLen);
126 static sal_uInt16 ImplGetNum( const sal_Unicode*& rpBuf, bool& rbError )
128 if ( !*rpBuf )
130 rbError = true;
131 return 0;
134 sal_uInt16 nNumber = 0;
135 while( ( *rpBuf >= '0' ) && ( *rpBuf <= '9' ) )
137 nNumber *= 10;
138 nNumber += *rpBuf - '0';
139 rpBuf++;
142 return nNumber;
145 static void ImplSkipDelimiters( const sal_Unicode*& rpBuf )
147 while( ( *rpBuf == ',' ) || ( *rpBuf == '.' ) || ( *rpBuf == ';' ) ||
148 ( *rpBuf == ':' ) || ( *rpBuf == '-' ) || ( *rpBuf == '/' ) )
150 rpBuf++;
154 static bool ImplIsPatternChar( sal_Unicode cChar, char cEditMask )
156 sal_Int32 nType = 0;
160 OUString aCharStr(cChar);
161 nType = ImplGetCharClass()->getCharacterType( aCharStr, 0,
162 Application::GetSettings().GetLanguageTag().getLocale() );
164 catch (const css::uno::Exception&)
166 DBG_UNHANDLED_EXCEPTION("vcl.control");
167 return false;
170 if ( (cEditMask == EDITMASK_ALPHA) || (cEditMask == EDITMASK_UPPERALPHA) )
172 if( !CharClass::isLetterType( nType ) )
173 return false;
175 else if ( cEditMask == EDITMASK_NUM )
177 if( !CharClass::isNumericType( nType ) )
178 return false;
180 else if ( (cEditMask == EDITMASK_ALPHANUM) || (cEditMask == EDITMASK_UPPERALPHANUM) )
182 if( !CharClass::isLetterNumericType( nType ) )
183 return false;
185 else if ( (cEditMask == EDITMASK_ALLCHAR) || (cEditMask == EDITMASK_UPPERALLCHAR) )
187 if ( cChar < 32 )
188 return false;
190 else if ( cEditMask == EDITMASK_NUMSPACE )
192 if ( !CharClass::isNumericType( nType ) && ( cChar != ' ' ) )
193 return false;
195 else
196 return false;
198 return true;
201 static sal_Unicode ImplPatternChar( sal_Unicode cChar, char cEditMask )
203 if ( ImplIsPatternChar( cChar, cEditMask ) )
205 if ( (cEditMask == EDITMASK_UPPERALPHA) ||
206 (cEditMask == EDITMASK_UPPERALPHANUM) ||
207 ( cEditMask == EDITMASK_UPPERALLCHAR ) )
209 cChar = ImplGetCharClass()->toUpper(OUString(cChar), 0, 1,
210 Application::GetSettings().GetLanguageTag().getLocale())[0];
212 return cChar;
214 else
215 return 0;
218 static bool ImplCommaPointCharEqual( sal_Unicode c1, sal_Unicode c2 )
220 if ( c1 == c2 )
221 return true;
222 else if ( ((c1 == '.') || (c1 == ',')) &&
223 ((c2 == '.') || (c2 == ',')) )
224 return true;
225 else
226 return false;
229 static OUString ImplPatternReformat( const OUString& rStr,
230 const OString& rEditMask,
231 std::u16string_view rLiteralMask,
232 sal_uInt16 nFormatFlags )
234 if (rEditMask.isEmpty())
235 return rStr;
237 OUStringBuffer aOutStr(rLiteralMask);
238 sal_Unicode cTempChar;
239 sal_Unicode cChar;
240 sal_Unicode cLiteral;
241 char cMask;
242 sal_Int32 nStrIndex = 0;
243 sal_Int32 i = 0;
244 sal_Int32 n;
246 while ( i < rEditMask.getLength() )
248 if ( nStrIndex >= rStr.getLength() )
249 break;
251 cChar = rStr[nStrIndex];
252 cLiteral = rLiteralMask[i];
253 cMask = rEditMask[i];
255 // current position is a literal
256 if ( cMask == EDITMASK_LITERAL )
258 // if it is a literal copy otherwise ignore because it might be the next valid
259 // character of the string
260 if ( ImplCommaPointCharEqual( cChar, cLiteral ) )
261 nStrIndex++;
262 else
264 // Otherwise we check if it is an invalid character. This is the case if it does not
265 // fit in the pattern of the next non-literal character.
266 n = i+1;
267 while ( n < rEditMask.getLength() )
269 if ( rEditMask[n] != EDITMASK_LITERAL )
271 if ( !ImplIsPatternChar( cChar, rEditMask[n] ) )
272 nStrIndex++;
273 break;
276 n++;
280 else
282 // valid character at this position
283 cTempChar = ImplPatternChar( cChar, cMask );
284 if ( cTempChar )
286 // use this character
287 aOutStr[i] = cTempChar;
288 nStrIndex++;
290 else
292 // copy if it is a literal character
293 if ( cLiteral == cChar )
294 nStrIndex++;
295 else
297 // If the invalid character might be the next literal character then we jump
298 // ahead to it, otherwise we ignore it. Do only if empty literals are allowed.
299 if ( nFormatFlags & PATTERN_FORMAT_EMPTYLITERALS )
301 n = i;
302 while ( n < rEditMask.getLength() )
304 if ( rEditMask[n] == EDITMASK_LITERAL )
306 if ( ImplCommaPointCharEqual( cChar, rLiteralMask[n] ) )
307 i = n+1;
309 break;
312 n++;
316 nStrIndex++;
317 continue;
322 i++;
325 return aOutStr.makeStringAndClear();
328 static void ImplPatternMaxPos( std::u16string_view rStr, const OString& rEditMask,
329 sal_uInt16 nFormatFlags, bool bSameMask,
330 sal_Int32 nCursorPos, sal_Int32& rPos )
333 // last position must not be longer than the contained string
334 sal_Int32 nMaxPos = rStr.size();
336 // if non empty literals are allowed ignore blanks at the end as well
337 if ( bSameMask && !(nFormatFlags & PATTERN_FORMAT_EMPTYLITERALS) )
339 while ( nMaxPos )
341 if ( (rEditMask[nMaxPos-1] != EDITMASK_LITERAL) &&
342 (rStr[nMaxPos-1] != ' ') )
343 break;
344 nMaxPos--;
347 // if we are in front of a literal, continue search until first character after the literal
348 sal_Int32 nTempPos = nMaxPos;
349 while ( nTempPos < rEditMask.getLength() )
351 if ( rEditMask[nTempPos] != EDITMASK_LITERAL )
353 nMaxPos = nTempPos;
354 break;
356 nTempPos++;
360 if ( rPos > nMaxPos )
361 rPos = nMaxPos;
363 // character should not move left
364 if ( rPos < nCursorPos )
365 rPos = nCursorPos;
368 static OUString ImplPatternProcessStrictModify(const OUString& rText,
369 const OString& rEditMask,
370 std::u16string_view rLiteralMask,
371 bool bSameMask)
373 OUString aText(rText);
375 // remove leading blanks
376 if (bSameMask && !rEditMask.isEmpty())
378 sal_Int32 i = 0;
379 sal_Int32 nMaxLen = aText.getLength();
380 while ( i < nMaxLen )
382 if ( (rEditMask[i] != EDITMASK_LITERAL) &&
383 (aText[i] != ' ') )
384 break;
386 i++;
388 // keep all literal characters
389 while ( i && (rEditMask[i] == EDITMASK_LITERAL) )
390 i--;
391 aText = aText.copy( i );
394 return ImplPatternReformat(aText, rEditMask, rLiteralMask, 0);
397 static void ImplPatternProcessStrictModify( Edit* pEdit,
398 const OString& rEditMask,
399 std::u16string_view rLiteralMask,
400 bool bSameMask )
402 OUString aText = pEdit->GetText();
403 OUString aNewText = ImplPatternProcessStrictModify(aText,
404 rEditMask,
405 rLiteralMask,
406 bSameMask);
408 if ( aNewText == aText )
409 return;
411 // adjust selection such that it remains at the end if it was there before
412 Selection aSel = pEdit->GetSelection();
413 sal_Int64 nMaxSel = std::max( aSel.Min(), aSel.Max() );
414 if ( nMaxSel >= aText.getLength() )
416 sal_Int32 nMaxPos = aNewText.getLength();
417 ImplPatternMaxPos(aNewText, rEditMask, 0, bSameMask, nMaxSel, nMaxPos);
418 if ( aSel.Min() == aSel.Max() )
420 aSel.Min() = nMaxPos;
421 aSel.Max() = aSel.Min();
423 else if ( aSel.Min() > aSel.Max() )
424 aSel.Min() = nMaxPos;
425 else
426 aSel.Max() = nMaxPos;
428 pEdit->SetText( aNewText, aSel );
431 static void ImplPatternProcessStrictModify( weld::Entry& rEntry,
432 const OString& rEditMask,
433 std::u16string_view rLiteralMask,
434 bool bSameMask )
436 OUString aText = rEntry.get_text();
437 OUString aNewText = ImplPatternProcessStrictModify(aText,
438 rEditMask,
439 rLiteralMask,
440 bSameMask);
442 if (aNewText == aText)
443 return;
445 // adjust selection such that it remains at the end if it was there before
446 int nStartPos, nEndPos;
447 rEntry.get_selection_bounds(nStartPos, nEndPos);
449 int nMaxSel = std::max(nStartPos, nEndPos);
450 if (nMaxSel >= aText.getLength())
452 sal_Int32 nMaxPos = aNewText.getLength();
453 ImplPatternMaxPos(aNewText, rEditMask, 0, bSameMask, nMaxSel, nMaxPos);
454 if (nStartPos == nEndPos)
456 nStartPos = nMaxPos;
457 nEndPos = nMaxPos;
459 else if (nStartPos > nMaxPos)
460 nStartPos = nMaxPos;
461 else
462 nEndPos = nMaxPos;
464 rEntry.set_text(aNewText);
465 rEntry.select_region(nStartPos, nEndPos);
468 static sal_Int32 ImplPatternLeftPos(std::string_view rEditMask, sal_Int32 nCursorPos)
470 // search non-literal predecessor
471 sal_Int32 nNewPos = nCursorPos;
472 sal_Int32 nTempPos = nNewPos;
473 while ( nTempPos )
475 if ( rEditMask[nTempPos-1] != EDITMASK_LITERAL )
477 nNewPos = nTempPos-1;
478 break;
480 nTempPos--;
482 return nNewPos;
485 static sal_Int32 ImplPatternRightPos( std::u16string_view rStr, const OString& rEditMask,
486 sal_uInt16 nFormatFlags, bool bSameMask,
487 sal_Int32 nCursorPos )
489 // search non-literal successor
490 sal_Int32 nNewPos = nCursorPos;
492 for(sal_Int32 nTempPos = nNewPos+1; nTempPos < rEditMask.getLength(); ++nTempPos )
494 if ( rEditMask[nTempPos] != EDITMASK_LITERAL )
496 nNewPos = nTempPos;
497 break;
500 ImplPatternMaxPos( rStr, rEditMask, nFormatFlags, bSameMask, nCursorPos, nNewPos );
501 return nNewPos;
504 namespace
506 class IEditImplementation
508 public:
509 virtual ~IEditImplementation() {}
511 virtual OUString GetText() const = 0;
512 virtual void SetText(const OUString& rStr, const Selection& rSelection) = 0;
514 virtual Selection GetSelection() const = 0;
515 virtual void SetSelection(const Selection& rSelection) = 0;
517 virtual bool IsInsertMode() const = 0;
519 virtual void SetModified() = 0;
523 static bool ImplPatternProcessKeyInput( IEditImplementation& rEdit, const KeyEvent& rKEvt,
524 const OString& rEditMask,
525 std::u16string_view rLiteralMask,
526 bool bStrictFormat,
527 bool bSameMask,
528 bool& rbInKeyInput )
530 if ( rEditMask.isEmpty() || !bStrictFormat )
531 return false;
533 sal_uInt16 nFormatFlags = 0;
534 Selection aOldSel = rEdit.GetSelection();
535 vcl::KeyCode aCode = rKEvt.GetKeyCode();
536 sal_Unicode cChar = rKEvt.GetCharCode();
537 sal_uInt16 nKeyCode = aCode.GetCode();
538 bool bShift = aCode.IsShift();
539 sal_Int32 nCursorPos = static_cast<sal_Int32>(aOldSel.Max());
540 sal_Int32 nNewPos;
541 sal_Int32 nTempPos;
543 if ( nKeyCode && !aCode.IsMod1() && !aCode.IsMod2() )
545 if ( nKeyCode == KEY_LEFT )
547 Selection aSel( ImplPatternLeftPos( rEditMask, nCursorPos ) );
548 if ( bShift )
549 aSel.Min() = aOldSel.Min();
550 rEdit.SetSelection( aSel );
551 return true;
553 else if ( nKeyCode == KEY_RIGHT )
555 // Use the start of selection as minimum; even a small position is allowed in case that
556 // all was selected by the focus
557 Selection aSel( aOldSel );
558 aSel.Normalize();
559 nCursorPos = aSel.Min();
560 aSel.Max() = ImplPatternRightPos( rEdit.GetText(), rEditMask, nFormatFlags, bSameMask, nCursorPos );
561 if ( bShift )
562 aSel.Min() = aOldSel.Min();
563 else
564 aSel.Min() = aSel.Max();
565 rEdit.SetSelection( aSel );
566 return true;
568 else if ( nKeyCode == KEY_HOME )
570 // Home is the position of the first non-literal character
571 nNewPos = 0;
572 while ( (nNewPos < rEditMask.getLength()) &&
573 (rEditMask[nNewPos] == EDITMASK_LITERAL) )
574 nNewPos++;
576 // Home should not move to the right
577 if ( nCursorPos < nNewPos )
578 nNewPos = nCursorPos;
579 Selection aSel( nNewPos );
580 if ( bShift )
581 aSel.Min() = aOldSel.Min();
582 rEdit.SetSelection( aSel );
583 return true;
585 else if ( nKeyCode == KEY_END )
587 // End is position of last non-literal character
588 nNewPos = rEditMask.getLength();
589 while ( nNewPos &&
590 (rEditMask[nNewPos-1] == EDITMASK_LITERAL) )
591 nNewPos--;
592 // Use the start of selection as minimum; even a small position is allowed in case that
593 // all was selected by the focus
594 Selection aSel( aOldSel );
595 aSel.Normalize();
596 nCursorPos = static_cast<sal_Int32>(aSel.Min());
597 ImplPatternMaxPos( rEdit.GetText(), rEditMask, nFormatFlags, bSameMask, nCursorPos, nNewPos );
598 aSel.Max() = nNewPos;
599 if ( bShift )
600 aSel.Min() = aOldSel.Min();
601 else
602 aSel.Min() = aSel.Max();
603 rEdit.SetSelection( aSel );
604 return true;
606 else if ( (nKeyCode == KEY_BACKSPACE) || (nKeyCode == KEY_DELETE) )
608 OUString aOldStr( rEdit.GetText() );
609 OUStringBuffer aStr( aOldStr );
610 Selection aSel = aOldSel;
612 aSel.Normalize();
613 nNewPos = static_cast<sal_Int32>(aSel.Min());
615 // if selection then delete it
616 if ( aSel.Len() )
618 if ( bSameMask )
619 aStr.remove( static_cast<sal_Int32>(aSel.Min()), static_cast<sal_Int32>(aSel.Len()) );
620 else
622 std::u16string_view aRep = rLiteralMask.substr( static_cast<sal_Int32>(aSel.Min()), static_cast<sal_Int32>(aSel.Len()) );
623 aStr.remove( aSel.Min(), aRep.size() );
624 aStr.insert( aSel.Min(), aRep );
627 else
629 if ( nKeyCode == KEY_BACKSPACE )
631 nTempPos = nNewPos;
632 nNewPos = ImplPatternLeftPos( rEditMask, nTempPos );
634 else
635 nTempPos = ImplPatternRightPos( aStr, rEditMask, nFormatFlags, bSameMask, nNewPos );
637 if ( nNewPos != nTempPos )
639 if ( bSameMask )
641 if ( rEditMask[nNewPos] != EDITMASK_LITERAL )
642 aStr.remove( nNewPos, 1 );
644 else
646 aStr[nNewPos] = rLiteralMask[nNewPos];
651 OUString sStr = aStr.makeStringAndClear();
652 if ( aOldStr != sStr )
654 if ( bSameMask )
655 sStr = ImplPatternReformat( sStr, rEditMask, rLiteralMask, nFormatFlags );
656 rbInKeyInput = true;
657 rEdit.SetText( sStr, Selection( nNewPos ) );
658 rEdit.SetModified();
659 rbInKeyInput = false;
661 else
662 rEdit.SetSelection( Selection( nNewPos ) );
664 return true;
666 else if ( nKeyCode == KEY_INSERT )
668 // you can only set InsertMode for a PatternField if the
669 // mask is equal at all input positions
670 if ( !bSameMask )
672 return true;
677 if ( rKEvt.GetKeyCode().IsMod2() || (cChar < 32) || (cChar == 127) )
678 return false;
680 Selection aSel = aOldSel;
681 aSel.Normalize();
682 nNewPos = aSel.Min();
684 if ( nNewPos < rEditMask.getLength() )
686 sal_Unicode cPattChar = ImplPatternChar( cChar, rEditMask[nNewPos] );
687 if ( cPattChar )
688 cChar = cPattChar;
689 else
691 // If no valid character, check if the user wanted to jump to next literal. We do this
692 // only if we're after a character, so that literals that were skipped automatically
693 // do not influence the position anymore.
694 if ( nNewPos &&
695 (rEditMask[nNewPos-1] != EDITMASK_LITERAL) &&
696 !aSel.Len() )
698 // search for next character not being a literal
699 nTempPos = nNewPos;
700 while ( nTempPos < rEditMask.getLength() )
702 if ( rEditMask[nTempPos] == EDITMASK_LITERAL )
704 // only valid if no literal present
705 if ( (rEditMask[nTempPos+1] != EDITMASK_LITERAL ) &&
706 ImplCommaPointCharEqual( cChar, rLiteralMask[nTempPos] ) )
708 nTempPos++;
709 ImplPatternMaxPos( rEdit.GetText(), rEditMask, nFormatFlags, bSameMask, nNewPos, nTempPos );
710 if ( nTempPos > nNewPos )
712 rEdit.SetSelection( Selection( nTempPos ) );
713 return true;
716 break;
718 nTempPos++;
722 cChar = 0;
725 else
726 cChar = 0;
727 if ( cChar )
729 OUStringBuffer aStr(rEdit.GetText());
730 bool bError = false;
731 if ( bSameMask && rEdit.IsInsertMode() )
733 // crop spaces and literals at the end until current position
734 sal_Int32 n = aStr.getLength();
735 while ( n && (n > nNewPos) )
737 if ( (aStr[n-1] != ' ') &&
738 ((n > rEditMask.getLength()) || (rEditMask[n-1] != EDITMASK_LITERAL)) )
739 break;
741 n--;
743 aStr.truncate( n );
745 if ( aSel.Len() )
746 aStr.remove( aSel.Min(), aSel.Len() );
748 if ( aStr.getLength() < rEditMask.getLength() )
750 // possibly extend string until cursor position
751 if ( aStr.getLength() < nNewPos )
752 aStr.append( rLiteralMask.substr(aStr.getLength(), nNewPos-aStr.getLength()) );
753 if ( nNewPos < aStr.getLength() )
754 aStr.insert( cChar, nNewPos );
755 else if ( nNewPos < rEditMask.getLength() )
756 aStr.append(cChar);
757 aStr = ImplPatternReformat( aStr.toString(), rEditMask, rLiteralMask, nFormatFlags );
759 else
760 bError = true;
762 else
764 if ( aSel.Len() )
766 // delete selection
767 std::u16string_view aRep = rLiteralMask.substr( aSel.Min(), aSel.Len() );
768 aStr.remove( aSel.Min(), aRep.size() );
769 aStr.insert( aSel.Min(), aRep );
772 if ( nNewPos < aStr.getLength() )
773 aStr[nNewPos] = cChar;
774 else if ( nNewPos < rEditMask.getLength() )
775 aStr.append(cChar);
778 if ( !bError )
780 rbInKeyInput = true;
781 const OUString sStr = aStr.makeStringAndClear();
782 Selection aNewSel( ImplPatternRightPos( sStr, rEditMask, nFormatFlags, bSameMask, nNewPos ) );
783 rEdit.SetText( sStr, aNewSel );
784 rEdit.SetModified();
785 rbInKeyInput = false;
789 return true;
792 namespace
794 bool ImplSetMask(const OString& rEditMask, OUString& rLiteralMask)
796 bool bSameMask = true;
798 if (rEditMask.getLength() != rLiteralMask.getLength())
800 OUStringBuffer aBuf(rLiteralMask);
801 if (rEditMask.getLength() < aBuf.getLength())
802 aBuf.setLength(rEditMask.getLength());
803 else
804 comphelper::string::padToLength(aBuf, rEditMask.getLength(), ' ');
805 rLiteralMask = aBuf.makeStringAndClear();
808 // Strict mode allows only the input mode if only equal characters are allowed as mask and if
809 // only spaces are specified which are not allowed by the mask
810 sal_Int32 i = 0;
811 char c = 0;
812 while ( i < rEditMask.getLength() )
814 char cTemp = rEditMask[i];
815 if ( cTemp != EDITMASK_LITERAL )
817 if ( (cTemp == EDITMASK_ALLCHAR) ||
818 (cTemp == EDITMASK_UPPERALLCHAR) ||
819 (cTemp == EDITMASK_NUMSPACE) )
821 bSameMask = false;
822 break;
824 if ( i < rLiteralMask.getLength() )
826 if ( rLiteralMask[i] != ' ' )
828 bSameMask = false;
829 break;
832 if ( !c )
833 c = cTemp;
834 if ( cTemp != c )
836 bSameMask = false;
837 break;
840 i++;
843 return bSameMask;
847 PatternFormatter::PatternFormatter(Edit* pEdit)
848 : FormatterBase(pEdit)
850 mbSameMask = true;
851 mbInPattKeyInput = false;
854 PatternFormatter::~PatternFormatter()
858 void PatternFormatter::SetMask( const OString& rEditMask,
859 const OUString& rLiteralMask )
861 m_aEditMask = rEditMask;
862 maLiteralMask = rLiteralMask;
863 mbSameMask = ImplSetMask(m_aEditMask, maLiteralMask);
864 ReformatAll();
867 namespace
869 class EntryImplementation : public IEditImplementation
871 public:
872 EntryImplementation(weld::PatternFormatter& rFormatter)
873 : m_rFormatter(rFormatter)
874 , m_rEntry(rFormatter.get_widget())
878 virtual OUString GetText() const override
880 return m_rEntry.get_text();
883 virtual void SetText(const OUString& rStr, const Selection& rSelection) override
885 m_rEntry.set_text(rStr);
886 SetSelection(rSelection);
889 virtual Selection GetSelection() const override
891 int nStartPos, nEndPos;
892 m_rEntry.get_selection_bounds(nStartPos, nEndPos);
893 return Selection(nStartPos, nEndPos);
896 virtual void SetSelection(const Selection& rSelection) override
898 auto nMin = rSelection.Min();
899 auto nMax = rSelection.Max();
900 m_rEntry.select_region(nMin < 0 ? 0 : nMin, nMax == SELECTION_MAX ? -1 : nMax);
903 virtual bool IsInsertMode() const override
905 return !m_rEntry.get_overwrite_mode();
908 virtual void SetModified() override
910 m_rFormatter.Modify();
913 private:
914 weld::PatternFormatter& m_rFormatter;
915 weld::Entry& m_rEntry;
919 namespace weld
921 void PatternFormatter::SetStrictFormat(bool bStrict)
923 if (bStrict != m_bStrictFormat)
925 m_bStrictFormat = bStrict;
926 if (m_bStrictFormat)
927 ReformatAll();
931 void PatternFormatter::SetMask(const OString& rEditMask,
932 const OUString& rLiteralMask)
934 m_aEditMask = rEditMask;
935 m_aLiteralMask = rLiteralMask;
936 m_bSameMask = ImplSetMask(m_aEditMask, m_aLiteralMask);
937 ReformatAll();
940 void PatternFormatter::ReformatAll()
942 m_rEntry.set_text(ImplPatternReformat(m_rEntry.get_text(), m_aEditMask, m_aLiteralMask, 0/*nFormatFlags*/));
943 if (!m_bSameMask && m_bStrictFormat && m_rEntry.get_editable())
944 m_rEntry.set_overwrite_mode(true);
947 void PatternFormatter::EntryGainFocus()
949 m_bReformat = false;
952 void PatternFormatter::EntryLostFocus()
954 if (m_bReformat)
955 ReformatAll();
958 void PatternFormatter::Modify()
960 if (!m_bInPattKeyInput)
962 if (m_bStrictFormat)
963 ImplPatternProcessStrictModify(m_rEntry, m_aEditMask, m_aLiteralMask, m_bSameMask);
964 else
965 m_bReformat = true;
967 m_aModifyHdl.Call(m_rEntry);
970 IMPL_LINK(PatternFormatter, KeyInputHdl, const KeyEvent&, rKEvt, bool)
972 if (m_aKeyPressHdl.Call(rKEvt))
973 return true;
974 if (rKEvt.GetKeyCode().IsMod2())
975 return false;
976 EntryImplementation aAdapt(*this);
977 return ImplPatternProcessKeyInput(aAdapt, rKEvt, m_aEditMask, m_aLiteralMask,
978 m_bStrictFormat,
979 m_bSameMask, m_bInPattKeyInput);
983 void PatternFormatter::SetString( const OUString& rStr )
985 if ( GetField() )
987 GetField()->SetText( rStr );
988 MarkToBeReformatted( false );
992 OUString PatternFormatter::GetString() const
994 if ( !GetField() )
995 return OUString();
996 else
997 return ImplPatternReformat( GetField()->GetText(), m_aEditMask, maLiteralMask, 0/*nFormatFlags*/ );
1000 void PatternFormatter::Reformat()
1002 if ( GetField() )
1004 ImplSetText( ImplPatternReformat( GetField()->GetText(), m_aEditMask, maLiteralMask, 0/*nFormatFlags*/ ) );
1005 if ( !mbSameMask && IsStrictFormat() && !GetField()->IsReadOnly() )
1006 GetField()->SetInsertMode( false );
1010 PatternField::PatternField(vcl::Window* pParent, WinBits nWinStyle)
1011 : SpinField(pParent, nWinStyle)
1012 , PatternFormatter(this)
1014 Reformat();
1017 void PatternField::dispose()
1019 ClearField();
1020 SpinField::dispose();
1023 namespace
1025 class EditImplementation : public IEditImplementation
1027 public:
1028 EditImplementation(Edit& rEdit)
1029 : m_rEdit(rEdit)
1033 virtual OUString GetText() const override
1035 return m_rEdit.GetText();
1038 virtual void SetText(const OUString& rStr, const Selection& rSelection) override
1040 m_rEdit.SetText(rStr, rSelection);
1043 virtual Selection GetSelection() const override
1045 return m_rEdit.GetSelection();
1048 virtual void SetSelection(const Selection& rSelection) override
1050 m_rEdit.SetSelection(rSelection);
1053 virtual bool IsInsertMode() const override
1055 return m_rEdit.IsInsertMode();
1058 virtual void SetModified() override
1060 m_rEdit.SetModifyFlag();
1061 m_rEdit.Modify();
1064 private:
1065 Edit& m_rEdit;
1069 bool PatternField::PreNotify( NotifyEvent& rNEvt )
1071 if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
1073 EditImplementation aAdapt(*GetField());
1074 if ( ImplPatternProcessKeyInput( aAdapt, *rNEvt.GetKeyEvent(), GetEditMask(), GetLiteralMask(),
1075 IsStrictFormat(),
1076 ImplIsSameMask(), ImplGetInPattKeyInput() ) )
1077 return true;
1080 return SpinField::PreNotify( rNEvt );
1083 bool PatternField::EventNotify( NotifyEvent& rNEvt )
1085 if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
1086 MarkToBeReformatted( false );
1087 else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
1089 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1090 Reformat();
1093 return SpinField::EventNotify( rNEvt );
1096 void PatternField::Modify()
1098 if ( !ImplGetInPattKeyInput() )
1100 if ( IsStrictFormat() )
1101 ImplPatternProcessStrictModify( GetField(), GetEditMask(), GetLiteralMask(), ImplIsSameMask() );
1102 else
1103 MarkToBeReformatted( true );
1106 SpinField::Modify();
1109 PatternBox::PatternBox(vcl::Window* pParent, WinBits nWinStyle)
1110 : ComboBox( pParent, nWinStyle )
1111 , PatternFormatter(this)
1113 Reformat();
1116 void PatternBox::dispose()
1118 ClearField();
1119 ComboBox::dispose();
1122 bool PatternBox::PreNotify( NotifyEvent& rNEvt )
1124 if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
1126 EditImplementation aAdapt(*GetField());
1127 if ( ImplPatternProcessKeyInput( aAdapt, *rNEvt.GetKeyEvent(), GetEditMask(), GetLiteralMask(),
1128 IsStrictFormat(),
1129 ImplIsSameMask(), ImplGetInPattKeyInput() ) )
1130 return true;
1133 return ComboBox::PreNotify( rNEvt );
1136 bool PatternBox::EventNotify( NotifyEvent& rNEvt )
1138 if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
1139 MarkToBeReformatted( false );
1140 else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
1142 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1143 Reformat();
1146 return ComboBox::EventNotify( rNEvt );
1149 void PatternBox::Modify()
1151 if ( !ImplGetInPattKeyInput() )
1153 if ( IsStrictFormat() )
1154 ImplPatternProcessStrictModify( GetField(), GetEditMask(), GetLiteralMask(), ImplIsSameMask() );
1155 else
1156 MarkToBeReformatted( true );
1159 ComboBox::Modify();
1162 void PatternBox::ReformatAll()
1164 OUString aStr;
1165 SetUpdateMode( false );
1166 const sal_Int32 nEntryCount = GetEntryCount();
1167 for ( sal_Int32 i=0; i < nEntryCount; ++i )
1169 aStr = ImplPatternReformat( GetEntry( i ), GetEditMask(), GetLiteralMask(), 0/*nFormatFlags*/ );
1170 RemoveEntryAt(i);
1171 InsertEntry( aStr, i );
1173 PatternFormatter::Reformat();
1174 SetUpdateMode( true );
1177 static ExtDateFieldFormat ImplGetExtFormat( LongDateOrder eOld )
1179 switch( eOld )
1181 case LongDateOrder::YDM:
1182 case LongDateOrder::DMY: return ExtDateFieldFormat::ShortDDMMYY;
1183 case LongDateOrder::MDY: return ExtDateFieldFormat::ShortMMDDYY;
1184 case LongDateOrder::YMD:
1185 default: return ExtDateFieldFormat::ShortYYMMDD;
1189 static sal_uInt16 ImplCutNumberFromString( OUString& rStr )
1191 sal_Int32 i1 = 0;
1192 while (i1 != rStr.getLength() && (rStr[i1] < '0' || rStr[i1] > '9')) {
1193 ++i1;
1195 sal_Int32 i2 = i1;
1196 while (i2 != rStr.getLength() && rStr[i2] >= '0' && rStr[i2] <= '9') {
1197 ++i2;
1199 sal_Int32 nValue = o3tl::toInt32(rStr.subView(i1, i2-i1));
1200 rStr = rStr.copy(std::min(i2+1, rStr.getLength()));
1201 return nValue;
1204 static bool ImplCutMonthName( OUString& rStr, std::u16string_view _rLookupMonthName )
1206 sal_Int32 index = 0;
1207 rStr = rStr.replaceFirst(_rLookupMonthName, "", &index);
1208 return index >= 0;
1211 static sal_uInt16 ImplGetMonthFromCalendarItem( OUString& rStr, const uno::Sequence< i18n::CalendarItem2 >& rMonths )
1213 const sal_uInt16 nMonths = rMonths.getLength();
1214 for (sal_uInt16 i=0; i < nMonths; ++i)
1216 // long month name?
1217 if ( ImplCutMonthName( rStr, rMonths[i].FullName ) )
1218 return i+1;
1220 // short month name?
1221 if ( ImplCutMonthName( rStr, rMonths[i].AbbrevName ) )
1222 return i+1;
1224 return 0;
1227 static sal_uInt16 ImplCutMonthFromString( OUString& rStr, OUString& rCalendarName,
1228 const LocaleDataWrapper& rLocaleData, const CalendarWrapper& rCalendarWrapper )
1230 const OUString aDefaultCalendarName( rCalendarWrapper.getUniqueID());
1231 rCalendarName = aDefaultCalendarName;
1233 // Search for a month name of the loaded default calendar.
1234 const uno::Sequence< i18n::CalendarItem2 > aMonths = rCalendarWrapper.getMonths();
1235 sal_uInt16 nMonth = ImplGetMonthFromCalendarItem( rStr, aMonths);
1236 if (nMonth > 0)
1237 return nMonth;
1239 // And also possessive genitive and partitive month names.
1240 const uno::Sequence< i18n::CalendarItem2 > aGenitiveMonths = rCalendarWrapper.getGenitiveMonths();
1241 if (aGenitiveMonths != aMonths)
1243 nMonth = ImplGetMonthFromCalendarItem( rStr, aGenitiveMonths);
1244 if (nMonth > 0)
1245 return nMonth;
1247 const uno::Sequence< i18n::CalendarItem2 > aPartitiveMonths = rCalendarWrapper.getPartitiveMonths();
1248 if (aPartitiveMonths != aMonths)
1250 nMonth = ImplGetMonthFromCalendarItem( rStr, aPartitiveMonths);
1251 if (nMonth > 0)
1252 return nMonth;
1255 // Check if there are more calendars and try them if so, as the long date
1256 // format is obtained from the number formatter this is possible (e.g.
1257 // ar_DZ "[~hijri] ...")
1258 const uno::Sequence< i18n::Calendar2 > aCalendars = rLocaleData.getAllCalendars();
1259 if (aCalendars.getLength() > 1)
1261 for (const auto& rCalendar : aCalendars)
1263 if (rCalendar.Name != aDefaultCalendarName)
1265 rCalendarName = rCalendar.Name;
1267 nMonth = ImplGetMonthFromCalendarItem( rStr, rCalendar.Months);
1268 if (nMonth > 0)
1269 return nMonth;
1271 if (rCalendar.Months != rCalendar.GenitiveMonths)
1273 nMonth = ImplGetMonthFromCalendarItem( rStr, rCalendar.GenitiveMonths);
1274 if (nMonth > 0)
1275 return nMonth;
1278 if (rCalendar.Months != rCalendar.PartitiveMonths)
1280 nMonth = ImplGetMonthFromCalendarItem( rStr, rCalendar.PartitiveMonths);
1281 if (nMonth > 0)
1282 return nMonth;
1285 rCalendarName = aDefaultCalendarName;
1290 return ImplCutNumberFromString( rStr );
1293 static OUString ImplGetDateSep( const LocaleDataWrapper& rLocaleDataWrapper, ExtDateFieldFormat eFormat )
1295 if ( ( eFormat == ExtDateFieldFormat::ShortYYMMDD_DIN5008 ) || ( eFormat == ExtDateFieldFormat::ShortYYYYMMDD_DIN5008 ) )
1296 return "-";
1297 else
1298 return rLocaleDataWrapper.getDateSep();
1301 static bool ImplDateProcessKeyInput( const KeyEvent& rKEvt, ExtDateFieldFormat eFormat,
1302 const LocaleDataWrapper& rLocaleDataWrapper )
1304 sal_Unicode cChar = rKEvt.GetCharCode();
1305 sal_uInt16 nGroup = rKEvt.GetKeyCode().GetGroup();
1306 return !((nGroup == KEYGROUP_FKEYS) ||
1307 (nGroup == KEYGROUP_CURSOR) ||
1308 (nGroup == KEYGROUP_MISC)||
1309 ((cChar >= '0') && (cChar <= '9')) ||
1310 (cChar == ImplGetDateSep( rLocaleDataWrapper, eFormat )[0]));
1313 bool DateFormatter::TextToDate(const OUString& rStr, Date& rDate, ExtDateFieldFormat eDateOrder,
1314 const LocaleDataWrapper& rLocaleDataWrapper, const CalendarWrapper& rCalendarWrapper)
1316 sal_uInt16 nDay = 0;
1317 sal_uInt16 nMonth = 0;
1318 sal_uInt16 nYear = 0;
1319 bool bError = false;
1320 OUString aStr( rStr );
1322 if ( eDateOrder == ExtDateFieldFormat::SystemLong )
1324 OUString aCalendarName;
1325 LongDateOrder eFormat = rLocaleDataWrapper.getLongDateOrder();
1326 switch( eFormat )
1328 case LongDateOrder::MDY:
1329 nMonth = ImplCutMonthFromString( aStr, aCalendarName, rLocaleDataWrapper, rCalendarWrapper );
1330 nDay = ImplCutNumberFromString( aStr );
1331 nYear = ImplCutNumberFromString( aStr );
1332 break;
1333 case LongDateOrder::DMY:
1334 nDay = ImplCutNumberFromString( aStr );
1335 nMonth = ImplCutMonthFromString( aStr, aCalendarName, rLocaleDataWrapper, rCalendarWrapper );
1336 nYear = ImplCutNumberFromString( aStr );
1337 break;
1338 case LongDateOrder::YDM:
1339 nYear = ImplCutNumberFromString( aStr );
1340 nDay = ImplCutNumberFromString( aStr );
1341 nMonth = ImplCutMonthFromString( aStr, aCalendarName, rLocaleDataWrapper, rCalendarWrapper );
1342 break;
1343 case LongDateOrder::YMD:
1344 default:
1345 nYear = ImplCutNumberFromString( aStr );
1346 nMonth = ImplCutMonthFromString( aStr, aCalendarName, rLocaleDataWrapper, rCalendarWrapper );
1347 nDay = ImplCutNumberFromString( aStr );
1348 break;
1350 if (aCalendarName != "gregorian")
1352 // Calendar widget is Gregorian, convert date.
1353 // Need full date.
1354 bError = !nDay || !nMonth || !nYear;
1355 if (!bError)
1357 CalendarWrapper aCW( rLocaleDataWrapper.getComponentContext());
1358 aCW.loadCalendar( aCalendarName, rLocaleDataWrapper.getLoadedLanguageTag().getLocale());
1359 aCW.setDateTime(0.5); // get rid of current time, set some day noon
1360 aCW.setValue( i18n::CalendarFieldIndex::DAY_OF_MONTH, nDay);
1361 aCW.setValue( i18n::CalendarFieldIndex::MONTH, nMonth - 1);
1362 aCW.setValue( i18n::CalendarFieldIndex::YEAR, nYear);
1363 bError = !aCW.isValid();
1364 if (!bError)
1366 Date aDate = aCW.getEpochStart() + aCW.getDateTime();
1367 nYear = aDate.GetYear();
1368 nMonth = aDate.GetMonth();
1369 nDay = aDate.GetDay();
1374 else
1376 bool bYear = true;
1378 // Check if year is present:
1379 OUString aDateSep = ImplGetDateSep( rLocaleDataWrapper, eDateOrder );
1380 sal_Int32 nSepPos = aStr.indexOf( aDateSep );
1381 if ( nSepPos < 0 )
1382 return false;
1383 nSepPos = aStr.indexOf( aDateSep, nSepPos+1 );
1384 if ( ( nSepPos < 0 ) || ( nSepPos == (aStr.getLength()-1) ) )
1386 bYear = false;
1387 nYear = Date( Date::SYSTEM ).GetYearUnsigned();
1390 const sal_Unicode* pBuf = aStr.getStr();
1391 ImplSkipDelimiters( pBuf );
1393 switch ( eDateOrder )
1395 case ExtDateFieldFormat::ShortDDMMYY:
1396 case ExtDateFieldFormat::ShortDDMMYYYY:
1398 nDay = ImplGetNum( pBuf, bError );
1399 ImplSkipDelimiters( pBuf );
1400 nMonth = ImplGetNum( pBuf, bError );
1401 ImplSkipDelimiters( pBuf );
1402 if ( bYear )
1403 nYear = ImplGetNum( pBuf, bError );
1405 break;
1406 case ExtDateFieldFormat::ShortMMDDYY:
1407 case ExtDateFieldFormat::ShortMMDDYYYY:
1409 nMonth = ImplGetNum( pBuf, bError );
1410 ImplSkipDelimiters( pBuf );
1411 nDay = ImplGetNum( pBuf, bError );
1412 ImplSkipDelimiters( pBuf );
1413 if ( bYear )
1414 nYear = ImplGetNum( pBuf, bError );
1416 break;
1417 case ExtDateFieldFormat::ShortYYMMDD:
1418 case ExtDateFieldFormat::ShortYYYYMMDD:
1419 case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
1420 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1422 if ( bYear )
1423 nYear = ImplGetNum( pBuf, bError );
1424 ImplSkipDelimiters( pBuf );
1425 nMonth = ImplGetNum( pBuf, bError );
1426 ImplSkipDelimiters( pBuf );
1427 nDay = ImplGetNum( pBuf, bError );
1429 break;
1431 default:
1433 OSL_FAIL( "DateOrder???" );
1438 if ( bError || !nDay || !nMonth )
1439 return false;
1441 Date aNewDate( nDay, nMonth, nYear );
1442 DateFormatter::ExpandCentury( aNewDate, officecfg::Office::Common::DateFormat::TwoDigitYear::get() );
1443 if ( aNewDate.IsValidDate() )
1445 rDate = aNewDate;
1446 return true;
1448 return false;
1451 void DateFormatter::ImplDateReformat( const OUString& rStr, OUString& rOutStr )
1453 Date aDate( Date::EMPTY );
1454 if (!TextToDate(rStr, aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper()))
1455 return;
1457 Date aTempDate = aDate;
1458 if ( aTempDate > GetMax() )
1459 aTempDate = GetMax();
1460 else if ( aTempDate < GetMin() )
1461 aTempDate = GetMin();
1463 rOutStr = ImplGetDateAsText( aTempDate );
1466 namespace
1468 ExtDateFieldFormat ResolveSystemFormat(ExtDateFieldFormat eDateFormat, const LocaleDataWrapper& rLocaleData)
1470 if (eDateFormat <= ExtDateFieldFormat::SystemShortYYYY)
1472 bool bShowCentury = (eDateFormat == ExtDateFieldFormat::SystemShortYYYY);
1473 switch (rLocaleData.getDateOrder())
1475 case DateOrder::DMY:
1476 eDateFormat = bShowCentury ? ExtDateFieldFormat::ShortDDMMYYYY : ExtDateFieldFormat::ShortDDMMYY;
1477 break;
1478 case DateOrder::MDY:
1479 eDateFormat = bShowCentury ? ExtDateFieldFormat::ShortMMDDYYYY : ExtDateFieldFormat::ShortMMDDYY;
1480 break;
1481 default:
1482 eDateFormat = bShowCentury ? ExtDateFieldFormat::ShortYYYYMMDD : ExtDateFieldFormat::ShortYYMMDD;
1485 return eDateFormat;
1489 OUString DateFormatter::FormatDate(const Date& rDate, ExtDateFieldFormat eExtFormat,
1490 const LocaleDataWrapper& rLocaleData,
1491 const Formatter::StaticFormatter& rStaticFormatter)
1493 bool bShowCentury = false;
1494 switch (eExtFormat)
1496 case ExtDateFieldFormat::SystemShortYYYY:
1497 case ExtDateFieldFormat::SystemLong:
1498 case ExtDateFieldFormat::ShortDDMMYYYY:
1499 case ExtDateFieldFormat::ShortMMDDYYYY:
1500 case ExtDateFieldFormat::ShortYYYYMMDD:
1501 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1503 bShowCentury = true;
1505 break;
1506 default:
1508 bShowCentury = false;
1512 if ( !bShowCentury )
1514 // Check if I have to use force showing the century
1515 sal_uInt16 nTwoDigitYearStart = officecfg::Office::Common::DateFormat::TwoDigitYear::get();
1516 sal_uInt16 nYear = rDate.GetYearUnsigned();
1518 // If year is not in double digit range
1519 if ( (nYear < nTwoDigitYearStart) || (nYear >= nTwoDigitYearStart+100) )
1520 bShowCentury = true;
1523 sal_Unicode aBuf[128];
1524 sal_Unicode* pBuf = aBuf;
1526 eExtFormat = ResolveSystemFormat(eExtFormat, rLocaleData);
1528 OUString aDateSep = ImplGetDateSep( rLocaleData, eExtFormat );
1529 sal_uInt16 nDay = rDate.GetDay();
1530 sal_uInt16 nMonth = rDate.GetMonth();
1531 sal_Int16 nYear = rDate.GetYear();
1532 sal_uInt16 nYearLen = bShowCentury ? 4 : 2;
1534 if ( !bShowCentury )
1535 nYear %= 100;
1537 switch (eExtFormat)
1539 case ExtDateFieldFormat::SystemLong:
1541 SvNumberFormatter* pFormatter = rStaticFormatter;
1542 const LanguageTag aFormatterLang( pFormatter->GetLanguageTag());
1543 const sal_uInt32 nIndex = pFormatter->GetFormatIndex( NF_DATE_SYSTEM_LONG,
1544 rLocaleData.getLanguageTag().getLanguageType(false));
1545 OUString aStr;
1546 const Color* pCol;
1547 pFormatter->GetOutputString( rDate - pFormatter->GetNullDate(), nIndex, aStr, &pCol);
1548 // Reset to what other uses may expect.
1549 pFormatter->ChangeIntl( aFormatterLang.getLanguageType(false));
1550 return aStr;
1552 case ExtDateFieldFormat::ShortDDMMYY:
1553 case ExtDateFieldFormat::ShortDDMMYYYY:
1555 pBuf = ImplAddNum( pBuf, nDay, 2 );
1556 pBuf = ImplAddString( pBuf, aDateSep );
1557 pBuf = ImplAddNum( pBuf, nMonth, 2 );
1558 pBuf = ImplAddString( pBuf, aDateSep );
1559 pBuf = ImplAddSNum( pBuf, nYear, nYearLen );
1561 break;
1562 case ExtDateFieldFormat::ShortMMDDYY:
1563 case ExtDateFieldFormat::ShortMMDDYYYY:
1565 pBuf = ImplAddNum( pBuf, nMonth, 2 );
1566 pBuf = ImplAddString( pBuf, aDateSep );
1567 pBuf = ImplAddNum( pBuf, nDay, 2 );
1568 pBuf = ImplAddString( pBuf, aDateSep );
1569 pBuf = ImplAddSNum( pBuf, nYear, nYearLen );
1571 break;
1572 case ExtDateFieldFormat::ShortYYMMDD:
1573 case ExtDateFieldFormat::ShortYYYYMMDD:
1574 case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
1575 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1577 pBuf = ImplAddSNum( pBuf, nYear, nYearLen );
1578 pBuf = ImplAddString( pBuf, aDateSep );
1579 pBuf = ImplAddNum( pBuf, nMonth, 2 );
1580 pBuf = ImplAddString( pBuf, aDateSep );
1581 pBuf = ImplAddNum( pBuf, nDay, 2 );
1583 break;
1584 default:
1586 OSL_FAIL( "DateOrder???" );
1590 return OUString(aBuf, pBuf-aBuf);
1593 OUString DateFormatter::ImplGetDateAsText( const Date& rDate ) const
1595 return DateFormatter::FormatDate(rDate, GetExtDateFormat(), ImplGetLocaleDataWrapper(), maStaticFormatter);
1598 static void ImplDateIncrementDay( Date& rDate, bool bUp )
1600 DateFormatter::ExpandCentury( rDate );
1601 rDate.AddDays( bUp ? 1 : -1 );
1604 static void ImplDateIncrementMonth( Date& rDate, bool bUp )
1606 DateFormatter::ExpandCentury( rDate );
1607 rDate.AddMonths( bUp ? 1 : -1 );
1610 static void ImplDateIncrementYear( Date& rDate, bool bUp )
1612 DateFormatter::ExpandCentury( rDate );
1613 rDate.AddYears( bUp ? 1 : -1 );
1616 bool DateFormatter::ImplAllowMalformedInput() const
1618 return !IsEnforceValidValue();
1621 int DateFormatter::GetDateArea(ExtDateFieldFormat& eFormat, std::u16string_view rText, int nCursor, const LocaleDataWrapper& rLocaleDataWrapper)
1623 sal_Int8 nDateArea = 0;
1625 if ( eFormat == ExtDateFieldFormat::SystemLong )
1627 eFormat = ImplGetExtFormat(rLocaleDataWrapper.getLongDateOrder());
1628 nDateArea = 1;
1630 else
1632 // search area
1633 size_t nPos = 0;
1634 OUString aDateSep = ImplGetDateSep(rLocaleDataWrapper, eFormat);
1635 for ( sal_Int8 i = 1; i <= 3; i++ )
1637 nPos = rText.find( aDateSep, nPos );
1638 if (nPos == std::u16string_view::npos || static_cast<sal_Int32>(nPos) >= nCursor)
1640 nDateArea = i;
1641 break;
1643 else
1644 nPos++;
1648 return nDateArea;
1651 void DateField::ImplDateSpinArea( bool bUp )
1653 // increment days if all is selected
1654 if ( !GetField() )
1655 return;
1657 Date aDate( GetDate() );
1658 Selection aSelection = GetField()->GetSelection();
1659 aSelection.Normalize();
1660 OUString aText( GetText() );
1661 if ( static_cast<sal_Int32>(aSelection.Len()) == aText.getLength() )
1662 ImplDateIncrementDay( aDate, bUp );
1663 else
1665 ExtDateFieldFormat eFormat = GetExtDateFormat( true );
1666 sal_Int8 nDateArea = GetDateArea(eFormat, aText, aSelection.Max(), ImplGetLocaleDataWrapper());
1668 switch( eFormat )
1670 case ExtDateFieldFormat::ShortMMDDYY:
1671 case ExtDateFieldFormat::ShortMMDDYYYY:
1672 switch( nDateArea )
1674 case 1: ImplDateIncrementMonth( aDate, bUp );
1675 break;
1676 case 2: ImplDateIncrementDay( aDate, bUp );
1677 break;
1678 case 3: ImplDateIncrementYear( aDate, bUp );
1679 break;
1681 break;
1682 case ExtDateFieldFormat::ShortDDMMYY:
1683 case ExtDateFieldFormat::ShortDDMMYYYY:
1684 switch( nDateArea )
1686 case 1: ImplDateIncrementDay( aDate, bUp );
1687 break;
1688 case 2: ImplDateIncrementMonth( aDate, bUp );
1689 break;
1690 case 3: ImplDateIncrementYear( aDate, bUp );
1691 break;
1693 break;
1694 case ExtDateFieldFormat::ShortYYMMDD:
1695 case ExtDateFieldFormat::ShortYYYYMMDD:
1696 case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
1697 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1698 switch( nDateArea )
1700 case 1: ImplDateIncrementYear( aDate, bUp );
1701 break;
1702 case 2: ImplDateIncrementMonth( aDate, bUp );
1703 break;
1704 case 3: ImplDateIncrementDay( aDate, bUp );
1705 break;
1707 break;
1708 default:
1709 OSL_FAIL( "invalid conversion" );
1710 break;
1714 ImplNewFieldValue( aDate );
1717 DateFormatter::DateFormatter(Edit* pEdit)
1718 : FormatterBase(pEdit)
1719 , maFieldDate(0)
1720 , maLastDate(0)
1721 , maMin(1, 1, 1900)
1722 , maMax(31, 12, 2200)
1723 , mbLongFormat(false)
1724 , mbShowDateCentury(true)
1725 , mnExtDateFormat(ExtDateFieldFormat::SystemShort)
1726 , mbEnforceValidValue(true)
1730 DateFormatter::~DateFormatter()
1734 CalendarWrapper& DateFormatter::GetCalendarWrapper() const
1736 if (!mxCalendarWrapper)
1738 const_cast<DateFormatter*>(this)->mxCalendarWrapper.reset( new CalendarWrapper( comphelper::getProcessComponentContext() ) );
1739 mxCalendarWrapper->loadDefaultCalendar( GetLocale() );
1742 return *mxCalendarWrapper;
1745 void DateFormatter::SetExtDateFormat( ExtDateFieldFormat eFormat )
1747 mnExtDateFormat = eFormat;
1748 ReformatAll();
1751 ExtDateFieldFormat DateFormatter::GetExtDateFormat( bool bResolveSystemFormat ) const
1753 ExtDateFieldFormat eDateFormat = mnExtDateFormat;
1755 if (bResolveSystemFormat)
1756 eDateFormat = ResolveSystemFormat(eDateFormat, ImplGetLocaleDataWrapper());
1758 return eDateFormat;
1761 void DateFormatter::ReformatAll()
1763 Reformat();
1766 void DateFormatter::SetMin( const Date& rNewMin )
1768 maMin = rNewMin;
1769 if ( !IsEmptyFieldValue() )
1770 ReformatAll();
1773 void DateFormatter::SetMax( const Date& rNewMax )
1775 maMax = rNewMax;
1776 if ( !IsEmptyFieldValue() )
1777 ReformatAll();
1780 void DateFormatter::SetLongFormat( bool bLong )
1782 mbLongFormat = bLong;
1784 // #91913# Remove LongFormat and DateShowCentury - redundant
1785 if ( bLong )
1787 SetExtDateFormat( ExtDateFieldFormat::SystemLong );
1789 else
1791 if( mnExtDateFormat == ExtDateFieldFormat::SystemLong )
1792 SetExtDateFormat( ExtDateFieldFormat::SystemShort );
1795 ReformatAll();
1798 namespace
1800 ExtDateFieldFormat ChangeDateCentury(ExtDateFieldFormat eExtDateFormat, bool bShowDateCentury)
1802 // #91913# Remove LongFormat and DateShowCentury - redundant
1803 if (bShowDateCentury)
1805 switch (eExtDateFormat)
1807 case ExtDateFieldFormat::SystemShort:
1808 case ExtDateFieldFormat::SystemShortYY:
1809 eExtDateFormat = ExtDateFieldFormat::SystemShortYYYY; break;
1810 case ExtDateFieldFormat::ShortDDMMYY:
1811 eExtDateFormat = ExtDateFieldFormat::ShortDDMMYYYY; break;
1812 case ExtDateFieldFormat::ShortMMDDYY:
1813 eExtDateFormat = ExtDateFieldFormat::ShortMMDDYYYY; break;
1814 case ExtDateFieldFormat::ShortYYMMDD:
1815 eExtDateFormat = ExtDateFieldFormat::ShortYYYYMMDD; break;
1816 case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
1817 eExtDateFormat = ExtDateFieldFormat::ShortYYYYMMDD_DIN5008; break;
1818 default:
1822 else
1824 switch (eExtDateFormat)
1826 case ExtDateFieldFormat::SystemShort:
1827 case ExtDateFieldFormat::SystemShortYYYY:
1828 eExtDateFormat = ExtDateFieldFormat::SystemShortYY; break;
1829 case ExtDateFieldFormat::ShortDDMMYYYY:
1830 eExtDateFormat = ExtDateFieldFormat::ShortDDMMYY; break;
1831 case ExtDateFieldFormat::ShortMMDDYYYY:
1832 eExtDateFormat = ExtDateFieldFormat::ShortMMDDYY; break;
1833 case ExtDateFieldFormat::ShortYYYYMMDD:
1834 eExtDateFormat = ExtDateFieldFormat::ShortYYMMDD; break;
1835 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1836 eExtDateFormat = ExtDateFieldFormat::ShortYYMMDD_DIN5008; break;
1837 default:
1842 return eExtDateFormat;
1846 void DateFormatter::SetShowDateCentury( bool bShowDateCentury )
1848 mbShowDateCentury = bShowDateCentury;
1850 SetExtDateFormat(ChangeDateCentury(GetExtDateFormat(), bShowDateCentury));
1852 ReformatAll();
1855 void DateFormatter::SetDate( const Date& rNewDate )
1857 ImplSetUserDate( rNewDate );
1858 maFieldDate = maLastDate;
1859 maLastDate = GetDate();
1862 void DateFormatter::ImplSetUserDate( const Date& rNewDate, Selection const * pNewSelection )
1864 Date aNewDate = rNewDate;
1865 if ( aNewDate > maMax )
1866 aNewDate = maMax;
1867 else if ( aNewDate < maMin )
1868 aNewDate = maMin;
1869 maLastDate = aNewDate;
1871 if ( GetField() )
1872 ImplSetText( ImplGetDateAsText( aNewDate ), pNewSelection );
1875 void DateFormatter::ImplNewFieldValue( const Date& rDate )
1877 if ( !GetField() )
1878 return;
1880 Selection aSelection = GetField()->GetSelection();
1881 aSelection.Normalize();
1882 OUString aText = GetField()->GetText();
1884 // If selected until the end then keep it that way
1885 if ( static_cast<sal_Int32>(aSelection.Max()) == aText.getLength() )
1887 if ( !aSelection.Len() )
1888 aSelection.Min() = SELECTION_MAX;
1889 aSelection.Max() = SELECTION_MAX;
1892 Date aOldLastDate = maLastDate;
1893 ImplSetUserDate( rDate, &aSelection );
1894 maLastDate = aOldLastDate;
1896 // Modify at Edit is only set at KeyInput
1897 if ( GetField()->GetText() != aText )
1899 GetField()->SetModifyFlag();
1900 GetField()->Modify();
1904 Date DateFormatter::GetDate() const
1906 Date aDate( Date::EMPTY );
1908 if ( GetField() )
1910 if (TextToDate(GetField()->GetText(), aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper()))
1912 if ( aDate > maMax )
1913 aDate = maMax;
1914 else if ( aDate < maMin )
1915 aDate = maMin;
1917 else
1919 // !!! We should find out why dates are treated differently than other fields (see
1920 // also bug: 52384)
1922 if ( !ImplAllowMalformedInput() )
1924 if ( maLastDate.GetDate() )
1925 aDate = maLastDate;
1926 else if ( !IsEmptyFieldValueEnabled() )
1927 aDate = Date( Date::SYSTEM );
1929 else
1930 aDate = Date( Date::EMPTY ); // set invalid date
1934 return aDate;
1937 void DateFormatter::SetEmptyDate()
1939 FormatterBase::SetEmptyFieldValue();
1942 bool DateFormatter::IsEmptyDate() const
1944 bool bEmpty = FormatterBase::IsEmptyFieldValue();
1946 if ( GetField() && MustBeReformatted() && IsEmptyFieldValueEnabled() )
1948 if ( GetField()->GetText().isEmpty() )
1950 bEmpty = true;
1952 else if ( !maLastDate.GetDate() )
1954 Date aDate( Date::EMPTY );
1955 bEmpty = !TextToDate(GetField()->GetText(), aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper());
1958 return bEmpty;
1961 void DateFormatter::Reformat()
1963 if ( !GetField() )
1964 return;
1966 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
1967 return;
1969 OUString aStr;
1970 ImplDateReformat( GetField()->GetText(), aStr );
1972 if ( !aStr.isEmpty() )
1974 ImplSetText( aStr );
1975 (void)TextToDate(aStr, maLastDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper());
1977 else
1979 if ( maLastDate.GetDate() )
1980 SetDate( maLastDate );
1981 else if ( !IsEmptyFieldValueEnabled() )
1982 SetDate( Date( Date::SYSTEM ) );
1983 else
1985 ImplSetText( OUString() );
1986 SetEmptyFieldValueData( true );
1991 void DateFormatter::ExpandCentury( Date& rDate )
1993 ExpandCentury(rDate, officecfg::Office::Common::DateFormat::TwoDigitYear::get());
1996 void DateFormatter::ExpandCentury( Date& rDate, sal_uInt16 nTwoDigitYearStart )
1998 sal_Int16 nDateYear = rDate.GetYear();
1999 if ( 0 <= nDateYear && nDateYear < 100 )
2001 sal_uInt16 nCentury = nTwoDigitYearStart / 100;
2002 if ( nDateYear < (nTwoDigitYearStart % 100) )
2003 nCentury++;
2004 rDate.SetYear( nDateYear + (nCentury*100) );
2008 DateField::DateField( vcl::Window* pParent, WinBits nWinStyle ) :
2009 SpinField( pParent, nWinStyle ),
2010 DateFormatter(this),
2011 maFirst( GetMin() ),
2012 maLast( GetMax() )
2014 SetText( ImplGetLocaleDataWrapper().getDate( ImplGetFieldDate() ) );
2015 Reformat();
2016 ResetLastDate();
2019 void DateField::dispose()
2021 ClearField();
2022 SpinField::dispose();
2025 bool DateField::PreNotify( NotifyEvent& rNEvt )
2027 if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && IsStrictFormat() &&
2028 ( GetExtDateFormat() != ExtDateFieldFormat::SystemLong ) &&
2029 !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
2031 if ( ImplDateProcessKeyInput( *rNEvt.GetKeyEvent(), GetExtDateFormat( true ), ImplGetLocaleDataWrapper() ) )
2032 return true;
2035 return SpinField::PreNotify( rNEvt );
2038 bool DateField::EventNotify( NotifyEvent& rNEvt )
2040 if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
2041 MarkToBeReformatted( false );
2042 else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
2044 if ( MustBeReformatted() )
2046 // !!! We should find out why dates are treated differently than other fields (see
2047 // also bug: 52384)
2049 bool bTextLen = !GetText().isEmpty();
2050 if ( bTextLen || !IsEmptyFieldValueEnabled() )
2052 if ( !ImplAllowMalformedInput() )
2053 Reformat();
2054 else
2056 Date aDate( 0, 0, 0 );
2057 if (TextToDate(GetText(), aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper()))
2058 // even with strict text analysis, our text is a valid date -> do a complete
2059 // reformat
2060 Reformat();
2063 else
2065 ResetLastDate();
2066 SetEmptyFieldValueData( true );
2071 return SpinField::EventNotify( rNEvt );
2074 void DateField::DataChanged( const DataChangedEvent& rDCEvt )
2076 SpinField::DataChanged( rDCEvt );
2078 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & (AllSettingsFlags::LOCALE|AllSettingsFlags::MISC)) )
2080 if (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE)
2081 ImplResetLocaleDataWrapper();
2082 ReformatAll();
2086 void DateField::Modify()
2088 MarkToBeReformatted( true );
2089 SpinField::Modify();
2092 void DateField::Up()
2094 ImplDateSpinArea( true );
2095 SpinField::Up();
2098 void DateField::Down()
2100 ImplDateSpinArea( false );
2101 SpinField::Down();
2104 void DateField::First()
2106 ImplNewFieldValue( maFirst );
2107 SpinField::First();
2110 void DateField::Last()
2112 ImplNewFieldValue( maLast );
2113 SpinField::Last();
2116 DateBox::DateBox(vcl::Window* pParent, WinBits nWinStyle)
2117 : ComboBox( pParent, nWinStyle )
2118 , DateFormatter(this)
2120 SetText( ImplGetLocaleDataWrapper().getDate( ImplGetFieldDate() ) );
2121 Reformat();
2124 void DateBox::dispose()
2126 ClearField();
2127 ComboBox::dispose();
2130 bool DateBox::PreNotify( NotifyEvent& rNEvt )
2132 if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && IsStrictFormat() &&
2133 ( GetExtDateFormat() != ExtDateFieldFormat::SystemLong ) &&
2134 !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
2136 if ( ImplDateProcessKeyInput( *rNEvt.GetKeyEvent(), GetExtDateFormat( true ), ImplGetLocaleDataWrapper() ) )
2137 return true;
2140 return ComboBox::PreNotify( rNEvt );
2143 void DateBox::DataChanged( const DataChangedEvent& rDCEvt )
2145 ComboBox::DataChanged( rDCEvt );
2147 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
2149 ImplResetLocaleDataWrapper();
2150 ReformatAll();
2154 bool DateBox::EventNotify( NotifyEvent& rNEvt )
2156 if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
2157 MarkToBeReformatted( false );
2158 else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
2160 if ( MustBeReformatted() )
2162 bool bTextLen = !GetText().isEmpty();
2163 if ( bTextLen || !IsEmptyFieldValueEnabled() )
2164 Reformat();
2165 else
2167 ResetLastDate();
2168 SetEmptyFieldValueData( true );
2173 return ComboBox::EventNotify( rNEvt );
2176 void DateBox::Modify()
2178 MarkToBeReformatted( true );
2179 ComboBox::Modify();
2182 void DateBox::ReformatAll()
2184 OUString aStr;
2185 SetUpdateMode( false );
2186 const sal_Int32 nEntryCount = GetEntryCount();
2187 for ( sal_Int32 i=0; i < nEntryCount; ++i )
2189 ImplDateReformat( GetEntry( i ), aStr );
2190 RemoveEntryAt(i);
2191 InsertEntry( aStr, i );
2193 DateFormatter::Reformat();
2194 SetUpdateMode( true );
2197 namespace weld
2199 CalendarWrapper& DateFormatter::GetCalendarWrapper() const
2201 if (!m_xCalendarWrapper)
2203 m_xCalendarWrapper.reset(new CalendarWrapper(comphelper::getProcessComponentContext()));
2204 m_xCalendarWrapper->loadDefaultCalendar(Application::GetSettings().GetLanguageTag().getLocale());
2206 return *m_xCalendarWrapper;
2209 void DateFormatter::SetShowDateCentury(bool bShowDateCentury)
2211 m_eFormat = ChangeDateCentury(m_eFormat, bShowDateCentury);
2213 ReFormat();
2216 void DateFormatter::SetDate(const Date& rDate)
2218 auto nDate = rDate.GetDate();
2219 bool bForceOutput = GetEntryText().isEmpty() && rDate == GetDate();
2220 if (bForceOutput)
2222 ImplSetValue(nDate, true);
2223 return;
2225 SetValue(nDate);
2228 Date DateFormatter::GetDate()
2230 return Date(GetValue());
2233 void DateFormatter::SetMin(const Date& rNewMin)
2235 SetMinValue(rNewMin.GetDate());
2238 void DateFormatter::SetMax(const Date& rNewMax)
2240 SetMaxValue(rNewMax.GetDate());
2243 OUString DateFormatter::FormatNumber(int nValue) const
2245 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
2246 return ::DateFormatter::FormatDate(Date(nValue), m_eFormat, rLocaleData, m_aStaticFormatter);
2249 IMPL_LINK_NOARG(DateFormatter, FormatOutputHdl, LinkParamNone*, bool)
2251 OUString sText = FormatNumber(GetValue());
2252 ImplSetTextImpl(sText, nullptr);
2253 return true;
2256 IMPL_LINK(DateFormatter, ParseInputHdl, sal_Int64*, result, TriState)
2258 const LocaleDataWrapper& rLocaleDataWrapper = Application::GetSettings().GetLocaleDataWrapper();
2260 Date aResult(Date::EMPTY);
2261 bool bRet = ::DateFormatter::TextToDate(GetEntryText(), aResult, ResolveSystemFormat(m_eFormat, rLocaleDataWrapper),
2262 rLocaleDataWrapper, GetCalendarWrapper());
2263 if (bRet)
2264 *result = aResult.GetDate();
2266 return bRet ? TRISTATE_TRUE : TRISTATE_FALSE;
2270 static bool ImplTimeProcessKeyInput( const KeyEvent& rKEvt,
2271 bool bStrictFormat, bool bDuration,
2272 TimeFieldFormat eFormat,
2273 const LocaleDataWrapper& rLocaleDataWrapper )
2275 sal_Unicode cChar = rKEvt.GetCharCode();
2277 if ( !bStrictFormat )
2278 return false;
2279 else
2281 sal_uInt16 nGroup = rKEvt.GetKeyCode().GetGroup();
2282 if ( (nGroup == KEYGROUP_FKEYS) || (nGroup == KEYGROUP_CURSOR) ||
2283 (nGroup == KEYGROUP_MISC) ||
2284 ((cChar >= '0') && (cChar <= '9')) ||
2285 rLocaleDataWrapper.getTimeSep() == OUStringChar(cChar) ||
2286 (rLocaleDataWrapper.getTimeAM().indexOf(cChar) != -1) ||
2287 (rLocaleDataWrapper.getTimePM().indexOf(cChar) != -1) ||
2288 // Accept AM/PM:
2289 (cChar == 'a') || (cChar == 'A') || (cChar == 'm') || (cChar == 'M') || (cChar == 'p') || (cChar == 'P') ||
2290 ((eFormat == TimeFieldFormat::F_SEC_CS) && rLocaleDataWrapper.getTime100SecSep() == OUStringChar(cChar)) ||
2291 (bDuration && (cChar == '-')) )
2292 return false;
2293 else
2294 return true;
2298 static bool ImplIsOnlyDigits( const OUString& _rStr )
2300 const sal_Unicode* _pChr = _rStr.getStr();
2301 for ( sal_Int32 i = 0; i < _rStr.getLength(); ++i, ++_pChr )
2303 if ( *_pChr < '0' || *_pChr > '9' )
2304 return false;
2306 return true;
2309 static bool ImplIsValidTimePortion( bool _bSkipInvalidCharacters, const OUString& _rStr )
2311 if ( !_bSkipInvalidCharacters )
2313 if ( ( _rStr.getLength() > 2 ) || _rStr.isEmpty() || !ImplIsOnlyDigits( _rStr ) )
2314 return false;
2316 return true;
2319 static bool ImplCutTimePortion( OUStringBuffer& _rStr, sal_Int32 _nSepPos, bool _bSkipInvalidCharacters, short* _pPortion )
2321 OUString sPortion(_rStr.subView(0, _nSepPos));
2323 if (_nSepPos < _rStr.getLength())
2324 _rStr.remove(0, _nSepPos + 1);
2325 else
2326 _rStr.truncate();
2328 if ( !ImplIsValidTimePortion( _bSkipInvalidCharacters, sPortion ) )
2329 return false;
2330 *_pPortion = static_cast<short>(sPortion.toInt32());
2331 return true;
2334 bool TimeFormatter::TextToTime(std::u16string_view rStr, tools::Time& rTime,
2335 TimeFieldFormat eFormat,
2336 bool bDuration, const LocaleDataWrapper& rLocaleDataWrapper, bool _bSkipInvalidCharacters)
2338 OUStringBuffer aStr(rStr);
2339 short nHour = 0;
2340 short nMinute = 0;
2341 short nSecond = 0;
2342 sal_Int64 nNanoSec = 0;
2343 tools::Time aTime( 0, 0, 0 );
2345 if ( rStr.empty() )
2346 return false;
2348 // Search for separators
2349 if (!rLocaleDataWrapper.getTimeSep().isEmpty())
2351 OUStringBuffer aSepStr(",.;:/");
2352 if ( !bDuration )
2353 aSepStr.append('-');
2355 // Replace characters above by the separator character
2356 for (sal_Int32 i = 0; i < aSepStr.getLength(); ++i)
2358 if (rLocaleDataWrapper.getTimeSep() == OUStringChar(aSepStr[i]))
2359 continue;
2360 for ( sal_Int32 j = 0; j < aStr.getLength(); j++ )
2362 if (aStr[j] == aSepStr[i])
2363 aStr[j] = rLocaleDataWrapper.getTimeSep()[0];
2368 bool bNegative = false;
2369 sal_Int32 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2370 if ( aStr[0] == '-' )
2371 bNegative = true;
2372 if ( eFormat != TimeFieldFormat::F_SEC_CS )
2374 if ( nSepPos < 0 )
2375 nSepPos = aStr.getLength();
2376 if ( !ImplCutTimePortion( aStr, nSepPos, _bSkipInvalidCharacters, &nHour ) )
2377 return false;
2379 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2380 if ( !aStr.isEmpty() && aStr[0] == '-' )
2381 bNegative = true;
2382 if ( nSepPos >= 0 )
2384 if ( !ImplCutTimePortion( aStr, nSepPos, _bSkipInvalidCharacters, &nMinute ) )
2385 return false;
2387 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2388 if ( !aStr.isEmpty() && aStr[0] == '-' )
2389 bNegative = true;
2390 if ( nSepPos >= 0 )
2392 if ( !ImplCutTimePortion( aStr, nSepPos, _bSkipInvalidCharacters, &nSecond ) )
2393 return false;
2394 if ( !aStr.isEmpty() && aStr[0] == '-' )
2395 bNegative = true;
2396 nNanoSec = o3tl::toInt64(aStr);
2398 else
2399 nSecond = static_cast<short>(o3tl::toInt32(aStr));
2401 else
2402 nMinute = static_cast<short>(o3tl::toInt32(aStr));
2404 else if ( nSepPos < 0 )
2406 nSecond = static_cast<short>(o3tl::toInt32(aStr));
2407 nMinute += nSecond / 60;
2408 nSecond %= 60;
2409 nHour += nMinute / 60;
2410 nMinute %= 60;
2412 else
2414 nSecond = static_cast<short>(o3tl::toInt32(aStr.subView( 0, nSepPos )));
2415 aStr.remove( 0, nSepPos+1 );
2417 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2418 if ( !aStr.isEmpty() && aStr[0] == '-' )
2419 bNegative = true;
2420 if ( nSepPos >= 0 )
2422 nMinute = nSecond;
2423 nSecond = static_cast<short>(o3tl::toInt32(aStr.subView( 0, nSepPos )));
2424 aStr.remove( 0, nSepPos+1 );
2426 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2427 if ( !aStr.isEmpty() && aStr[0] == '-' )
2428 bNegative = true;
2429 if ( nSepPos >= 0 )
2431 nHour = nMinute;
2432 nMinute = nSecond;
2433 nSecond = static_cast<short>(o3tl::toInt32(aStr.subView( 0, nSepPos )));
2434 aStr.remove( 0, nSepPos+1 );
2436 else
2438 nHour += nMinute / 60;
2439 nMinute %= 60;
2442 else
2444 nMinute += nSecond / 60;
2445 nSecond %= 60;
2446 nHour += nMinute / 60;
2447 nMinute %= 60;
2449 nNanoSec = o3tl::toInt64(aStr);
2452 if ( nNanoSec )
2454 assert(aStr.getLength() >= 1);
2456 sal_Int32 nLen = 1; // at least one digit, otherwise nNanoSec==0
2458 while ( aStr.getLength() > nLen && aStr[nLen] >= '0' && aStr[nLen] <= '9' )
2459 nLen++;
2461 while ( nLen < 9)
2463 nNanoSec *= 10;
2464 ++nLen;
2466 while ( nLen > 9 )
2468 // round if negative?
2469 nNanoSec = (nNanoSec + 5) / 10;
2470 --nLen;
2474 assert(nNanoSec > -1000000000 && nNanoSec < 1000000000);
2475 if ( (nMinute > 59) || (nSecond > 59) || (nNanoSec > 1000000000) )
2476 return false;
2478 if ( eFormat == TimeFieldFormat::F_NONE )
2479 nSecond = nNanoSec = 0;
2480 else if ( eFormat == TimeFieldFormat::F_SEC )
2481 nNanoSec = 0;
2483 if ( !bDuration )
2485 if ( bNegative || (nHour < 0) || (nMinute < 0) ||
2486 (nSecond < 0) || (nNanoSec < 0) )
2487 return false;
2489 OUString aUpperCaseStr = aStr.toString().toAsciiUpperCase();
2490 OUString aAMlocalised(rLocaleDataWrapper.getTimeAM().toAsciiUpperCase());
2491 OUString aPMlocalised(rLocaleDataWrapper.getTimePM().toAsciiUpperCase());
2493 if ( (nHour < 12) && ( ( aUpperCaseStr.indexOf( "PM" ) >= 0 ) || ( aUpperCaseStr.indexOf( aPMlocalised ) >= 0 ) ) )
2494 nHour += 12;
2496 if ( (nHour == 12) && ( ( aUpperCaseStr.indexOf( "AM" ) >= 0 ) || ( aUpperCaseStr.indexOf( aAMlocalised ) >= 0 ) ) )
2497 nHour = 0;
2499 aTime = tools::Time( static_cast<sal_uInt16>(nHour), static_cast<sal_uInt16>(nMinute), static_cast<sal_uInt16>(nSecond),
2500 static_cast<sal_uInt32>(nNanoSec) );
2502 else
2504 assert( !bNegative || (nHour < 0) || (nMinute < 0) ||
2505 (nSecond < 0) || (nNanoSec < 0) );
2506 if ( bNegative || (nHour < 0) || (nMinute < 0) ||
2507 (nSecond < 0) || (nNanoSec < 0) )
2509 // LEM TODO: this looks weird... I think buggy when parsing "05:-02:18"
2510 bNegative = true;
2511 nHour = nHour < 0 ? -nHour : nHour;
2512 nMinute = nMinute < 0 ? -nMinute : nMinute;
2513 nSecond = nSecond < 0 ? -nSecond : nSecond;
2514 nNanoSec = nNanoSec < 0 ? -nNanoSec : nNanoSec;
2517 aTime = tools::Time( static_cast<sal_uInt16>(nHour), static_cast<sal_uInt16>(nMinute), static_cast<sal_uInt16>(nSecond),
2518 static_cast<sal_uInt32>(nNanoSec) );
2519 if ( bNegative )
2520 aTime = -aTime;
2523 rTime = aTime;
2525 return true;
2528 void TimeFormatter::ImplTimeReformat( std::u16string_view rStr, OUString& rOutStr )
2530 tools::Time aTime( 0, 0, 0 );
2531 if ( !TextToTime( rStr, aTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper() ) )
2532 return;
2534 tools::Time aTempTime = aTime;
2535 if ( aTempTime > GetMax() )
2536 aTempTime = GetMax() ;
2537 else if ( aTempTime < GetMin() )
2538 aTempTime = GetMin();
2540 bool bSecond = false;
2541 bool b100Sec = false;
2542 if ( meFormat != TimeFieldFormat::F_NONE )
2543 bSecond = true;
2545 if ( meFormat == TimeFieldFormat::F_SEC_CS )
2547 sal_uLong n = aTempTime.GetHour() * 3600L;
2548 n += aTempTime.GetMin() * 60L;
2549 n += aTempTime.GetSec();
2550 rOutStr = OUString::number( n );
2551 rOutStr += ImplGetLocaleDataWrapper().getTime100SecSep();
2552 std::ostringstream ostr;
2553 ostr.fill('0');
2554 ostr.width(9);
2555 ostr << aTempTime.GetNanoSec();
2556 rOutStr += OUString::createFromAscii(ostr.str());
2558 else if ( mbDuration )
2559 rOutStr = ImplGetLocaleDataWrapper().getDuration( aTempTime, bSecond, b100Sec );
2560 else
2562 rOutStr = ImplGetLocaleDataWrapper().getTime( aTempTime, bSecond, b100Sec );
2563 if ( GetTimeFormat() == TimeFormat::Hour12 )
2565 if ( aTempTime.GetHour() > 12 )
2567 tools::Time aT( aTempTime );
2568 aT.SetHour( aT.GetHour() % 12 );
2569 rOutStr = ImplGetLocaleDataWrapper().getTime( aT, bSecond, b100Sec );
2571 // Don't use LocaleDataWrapper, we want AM/PM
2572 if ( aTempTime.GetHour() < 12 )
2573 rOutStr += "AM"; // ImplGetLocaleDataWrapper().getTimeAM();
2574 else
2575 rOutStr += "PM"; // ImplGetLocaleDataWrapper().getTimePM();
2580 bool TimeFormatter::ImplAllowMalformedInput() const
2582 return !IsEnforceValidValue();
2585 int TimeFormatter::GetTimeArea(TimeFieldFormat eFormat, std::u16string_view rText, int nCursor,
2586 const LocaleDataWrapper& rLocaleDataWrapper)
2588 int nTimeArea = 0;
2590 // Area search
2591 if (eFormat != TimeFieldFormat::F_SEC_CS)
2593 //Which area is the cursor in of HH:MM:SS.TT
2594 for ( size_t i = 1, nPos = 0; i <= 4; i++ )
2596 size_t nPos1 = rText.find(rLocaleDataWrapper.getTimeSep(), nPos);
2597 size_t nPos2 = rText.find(rLocaleDataWrapper.getTime100SecSep(), nPos);
2598 //which ever comes first, bearing in mind that one might not be there
2599 if (nPos1 != std::u16string_view::npos && nPos2 != std::u16string_view::npos)
2600 nPos = std::min(nPos1, nPos2);
2601 else if (nPos1 != std::u16string_view::npos)
2602 nPos = nPos1;
2603 else
2604 nPos = nPos2;
2605 if (nPos == std::u16string_view::npos || static_cast<sal_Int32>(nPos) >= nCursor)
2607 nTimeArea = i;
2608 break;
2610 else
2611 nPos++;
2614 else
2616 size_t nPos = rText.find(rLocaleDataWrapper.getTime100SecSep());
2617 if (nPos == std::u16string_view::npos || static_cast<sal_Int32>(nPos) >= nCursor)
2618 nTimeArea = 3;
2619 else
2620 nTimeArea = 4;
2623 return nTimeArea;
2626 tools::Time TimeFormatter::SpinTime(bool bUp, const tools::Time& rTime, TimeFieldFormat eFormat,
2627 bool bDuration, std::u16string_view rText, int nCursor,
2628 const LocaleDataWrapper& rLocaleDataWrapper)
2630 tools::Time aTime(rTime);
2632 int nTimeArea = GetTimeArea(eFormat, rText, nCursor, rLocaleDataWrapper);
2634 if ( nTimeArea )
2636 tools::Time aAddTime( 0, 0, 0 );
2637 if ( nTimeArea == 1 )
2638 aAddTime = tools::Time( 1, 0 );
2639 else if ( nTimeArea == 2 )
2640 aAddTime = tools::Time( 0, 1 );
2641 else if ( nTimeArea == 3 )
2642 aAddTime = tools::Time( 0, 0, 1 );
2643 else if ( nTimeArea == 4 )
2644 aAddTime = tools::Time( 0, 0, 0, 1 );
2646 if ( !bUp )
2647 aAddTime = -aAddTime;
2649 aTime += aAddTime;
2650 if (!bDuration)
2652 tools::Time aAbsMaxTime( 23, 59, 59, 999999999 );
2653 if ( aTime > aAbsMaxTime )
2654 aTime = aAbsMaxTime;
2655 tools::Time aAbsMinTime( 0, 0 );
2656 if ( aTime < aAbsMinTime )
2657 aTime = aAbsMinTime;
2661 return aTime;
2664 void TimeField::ImplTimeSpinArea( bool bUp )
2666 if ( GetField() )
2668 tools::Time aTime( GetTime() );
2669 OUString aText( GetText() );
2670 Selection aSelection( GetField()->GetSelection() );
2672 aTime = TimeFormatter::SpinTime(bUp, aTime, GetFormat(), IsDuration(), aText, aSelection.Max(), ImplGetLocaleDataWrapper());
2674 ImplNewFieldValue( aTime );
2678 TimeFormatter::TimeFormatter(Edit* pEdit)
2679 : FormatterBase(pEdit)
2680 , maLastTime(0, 0)
2681 , maMin(0, 0)
2682 , maMax(23, 59, 59, 999999999)
2683 , meFormat(TimeFieldFormat::F_NONE)
2684 , mnTimeFormat(TimeFormat::Hour24) // Should become an ExtTimeFieldFormat in next implementation, merge with mbDuration and meFormat
2685 , mbDuration(false)
2686 , mbEnforceValidValue(true)
2687 , maFieldTime(0, 0)
2691 TimeFormatter::~TimeFormatter()
2695 void TimeFormatter::ReformatAll()
2697 Reformat();
2700 void TimeFormatter::SetMin( const tools::Time& rNewMin )
2702 maMin = rNewMin;
2703 if ( !IsEmptyFieldValue() )
2704 ReformatAll();
2707 void TimeFormatter::SetMax( const tools::Time& rNewMax )
2709 maMax = rNewMax;
2710 if ( !IsEmptyFieldValue() )
2711 ReformatAll();
2714 void TimeFormatter::SetTimeFormat( TimeFormat eNewFormat )
2716 mnTimeFormat = eNewFormat;
2720 void TimeFormatter::SetFormat( TimeFieldFormat eNewFormat )
2722 meFormat = eNewFormat;
2723 ReformatAll();
2726 void TimeFormatter::SetDuration( bool bNewDuration )
2728 mbDuration = bNewDuration;
2729 ReformatAll();
2732 void TimeFormatter::SetTime( const tools::Time& rNewTime )
2734 SetUserTime( rNewTime );
2735 maFieldTime = maLastTime;
2736 SetEmptyFieldValueData( false );
2739 void TimeFormatter::ImplNewFieldValue( const tools::Time& rTime )
2741 if ( !GetField() )
2742 return;
2744 Selection aSelection = GetField()->GetSelection();
2745 aSelection.Normalize();
2746 OUString aText = GetField()->GetText();
2748 // If selected until the end then keep it that way
2749 if ( static_cast<sal_Int32>(aSelection.Max()) == aText.getLength() )
2751 if ( !aSelection.Len() )
2752 aSelection.Min() = SELECTION_MAX;
2753 aSelection.Max() = SELECTION_MAX;
2756 tools::Time aOldLastTime = maLastTime;
2757 ImplSetUserTime( rTime, &aSelection );
2758 maLastTime = aOldLastTime;
2760 // Modify at Edit is only set at KeyInput
2761 if ( GetField()->GetText() != aText )
2763 GetField()->SetModifyFlag();
2764 GetField()->Modify();
2768 OUString TimeFormatter::FormatTime(const tools::Time& rNewTime, TimeFieldFormat eFormat, TimeFormat eHourFormat, bool bDuration, const LocaleDataWrapper& rLocaleData)
2770 OUString aStr;
2771 bool bSec = false;
2772 bool b100Sec = false;
2773 if ( eFormat != TimeFieldFormat::F_NONE )
2774 bSec = true;
2775 if ( eFormat == TimeFieldFormat::F_SEC_CS )
2776 b100Sec = true;
2777 if ( eFormat == TimeFieldFormat::F_SEC_CS )
2779 sal_uLong n = rNewTime.GetHour() * 3600L;
2780 n += rNewTime.GetMin() * 60L;
2781 n += rNewTime.GetSec();
2782 aStr = OUString::number( n ) + rLocaleData.getTime100SecSep();
2783 std::ostringstream ostr;
2784 ostr.fill('0');
2785 ostr.width(9);
2786 ostr << rNewTime.GetNanoSec();
2787 aStr += OUString::createFromAscii(ostr.str());
2789 else if ( bDuration )
2791 aStr = rLocaleData.getDuration( rNewTime, bSec, b100Sec );
2793 else
2795 aStr = rLocaleData.getTime( rNewTime, bSec, b100Sec );
2796 if ( eHourFormat == TimeFormat::Hour12 )
2798 if ( rNewTime.GetHour() > 12 )
2800 tools::Time aT( rNewTime );
2801 aT.SetHour( aT.GetHour() % 12 );
2802 aStr = rLocaleData.getTime( aT, bSec, b100Sec );
2804 // Don't use LocaleDataWrapper, we want AM/PM
2805 if ( rNewTime.GetHour() < 12 )
2806 aStr += "AM"; // rLocaleData.getTimeAM();
2807 else
2808 aStr += "PM"; // rLocaleData.getTimePM();
2812 return aStr;
2815 void TimeFormatter::ImplSetUserTime( const tools::Time& rNewTime, Selection const * pNewSelection )
2817 tools::Time aNewTime = rNewTime;
2818 if ( aNewTime > GetMax() )
2819 aNewTime = GetMax();
2820 else if ( aNewTime < GetMin() )
2821 aNewTime = GetMin();
2822 maLastTime = aNewTime;
2824 if ( GetField() )
2826 OUString aStr = TimeFormatter::FormatTime(aNewTime, meFormat, GetTimeFormat(), mbDuration, ImplGetLocaleDataWrapper());
2827 ImplSetText( aStr, pNewSelection );
2831 void TimeFormatter::SetUserTime( const tools::Time& rNewTime )
2833 ImplSetUserTime( rNewTime );
2836 tools::Time TimeFormatter::GetTime() const
2838 tools::Time aTime( 0, 0, 0 );
2840 if ( GetField() )
2842 bool bAllowMalformed = ImplAllowMalformedInput();
2843 if ( TextToTime( GetField()->GetText(), aTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper(), !bAllowMalformed ) )
2845 if ( aTime > GetMax() )
2846 aTime = GetMax();
2847 else if ( aTime < GetMin() )
2848 aTime = GetMin();
2850 else
2852 if ( bAllowMalformed )
2853 aTime = tools::Time( 99, 99, 99 ); // set invalid time
2854 else
2855 aTime = maLastTime;
2859 return aTime;
2862 void TimeFormatter::Reformat()
2864 if ( !GetField() )
2865 return;
2867 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
2868 return;
2870 OUString aStr;
2871 ImplTimeReformat( GetField()->GetText(), aStr );
2873 if ( !aStr.isEmpty() )
2875 ImplSetText( aStr );
2876 (void)TextToTime(aStr, maLastTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper());
2878 else
2879 SetTime( maLastTime );
2882 TimeField::TimeField( vcl::Window* pParent, WinBits nWinStyle ) :
2883 SpinField( pParent, nWinStyle ),
2884 TimeFormatter(this),
2885 maFirst( GetMin() ),
2886 maLast( GetMax() )
2888 SetText( ImplGetLocaleDataWrapper().getTime( maFieldTime, false ) );
2889 Reformat();
2892 void TimeField::dispose()
2894 ClearField();
2895 SpinField::dispose();
2898 bool TimeField::PreNotify( NotifyEvent& rNEvt )
2900 if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
2902 if ( ImplTimeProcessKeyInput( *rNEvt.GetKeyEvent(), IsStrictFormat(), IsDuration(), GetFormat(), ImplGetLocaleDataWrapper() ) )
2903 return true;
2906 return SpinField::PreNotify( rNEvt );
2909 bool TimeField::EventNotify( NotifyEvent& rNEvt )
2911 if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
2912 MarkToBeReformatted( false );
2913 else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
2915 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
2917 if ( !ImplAllowMalformedInput() )
2918 Reformat();
2919 else
2921 tools::Time aTime( 0, 0, 0 );
2922 if ( TextToTime( GetText(), aTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper(), false ) )
2923 // even with strict text analysis, our text is a valid time -> do a complete
2924 // reformat
2925 Reformat();
2930 return SpinField::EventNotify( rNEvt );
2933 void TimeField::DataChanged( const DataChangedEvent& rDCEvt )
2935 SpinField::DataChanged( rDCEvt );
2937 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
2939 ImplResetLocaleDataWrapper();
2940 ReformatAll();
2944 void TimeField::Modify()
2946 MarkToBeReformatted( true );
2947 SpinField::Modify();
2950 void TimeField::Up()
2952 ImplTimeSpinArea( true );
2953 SpinField::Up();
2956 void TimeField::Down()
2958 ImplTimeSpinArea( false );
2959 SpinField::Down();
2962 void TimeField::First()
2964 ImplNewFieldValue( maFirst );
2965 SpinField::First();
2968 void TimeField::Last()
2970 ImplNewFieldValue( maLast );
2971 SpinField::Last();
2974 void TimeField::SetExtFormat( ExtTimeFieldFormat eFormat )
2976 switch ( eFormat )
2978 case ExtTimeFieldFormat::Short24H:
2980 SetTimeFormat( TimeFormat::Hour24 );
2981 SetDuration( false );
2982 SetFormat( TimeFieldFormat::F_NONE );
2984 break;
2985 case ExtTimeFieldFormat::Long24H:
2987 SetTimeFormat( TimeFormat::Hour24 );
2988 SetDuration( false );
2989 SetFormat( TimeFieldFormat::F_SEC );
2991 break;
2992 case ExtTimeFieldFormat::Short12H:
2994 SetTimeFormat( TimeFormat::Hour12 );
2995 SetDuration( false );
2996 SetFormat( TimeFieldFormat::F_NONE );
2998 break;
2999 case ExtTimeFieldFormat::Long12H:
3001 SetTimeFormat( TimeFormat::Hour12 );
3002 SetDuration( false );
3003 SetFormat( TimeFieldFormat::F_SEC );
3005 break;
3006 case ExtTimeFieldFormat::ShortDuration:
3008 SetDuration( true );
3009 SetFormat( TimeFieldFormat::F_NONE );
3011 break;
3012 case ExtTimeFieldFormat::LongDuration:
3014 SetDuration( true );
3015 SetFormat( TimeFieldFormat::F_SEC );
3017 break;
3018 default: OSL_FAIL( "ExtTimeFieldFormat unknown!" );
3021 if ( GetField() && !GetField()->GetText().isEmpty() )
3022 SetUserTime( GetTime() );
3023 ReformatAll();
3026 TimeBox::TimeBox(vcl::Window* pParent, WinBits nWinStyle)
3027 : ComboBox(pParent, nWinStyle)
3028 , TimeFormatter(this)
3030 SetText( ImplGetLocaleDataWrapper().getTime( maFieldTime, false ) );
3031 Reformat();
3034 void TimeBox::dispose()
3036 ClearField();
3037 ComboBox::dispose();
3040 bool TimeBox::PreNotify( NotifyEvent& rNEvt )
3042 if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
3044 if ( ImplTimeProcessKeyInput( *rNEvt.GetKeyEvent(), IsStrictFormat(), IsDuration(), GetFormat(), ImplGetLocaleDataWrapper() ) )
3045 return true;
3048 return ComboBox::PreNotify( rNEvt );
3051 bool TimeBox::EventNotify( NotifyEvent& rNEvt )
3053 if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
3054 MarkToBeReformatted( false );
3055 else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
3057 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
3058 Reformat();
3061 return ComboBox::EventNotify( rNEvt );
3064 void TimeBox::DataChanged( const DataChangedEvent& rDCEvt )
3066 ComboBox::DataChanged( rDCEvt );
3068 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
3070 ImplResetLocaleDataWrapper();
3071 ReformatAll();
3075 void TimeBox::Modify()
3077 MarkToBeReformatted( true );
3078 ComboBox::Modify();
3081 void TimeBox::ReformatAll()
3083 OUString aStr;
3084 SetUpdateMode( false );
3085 const sal_Int32 nEntryCount = GetEntryCount();
3086 for ( sal_Int32 i=0; i < nEntryCount; ++i )
3088 ImplTimeReformat( GetEntry( i ), aStr );
3089 RemoveEntryAt(i);
3090 InsertEntry( aStr, i );
3092 TimeFormatter::Reformat();
3093 SetUpdateMode( true );
3096 namespace weld
3098 tools::Time TimeFormatter::ConvertValue(int nValue)
3100 tools::Time aTime(0);
3101 aTime.MakeTimeFromMS(nValue);
3102 return aTime;
3105 int TimeFormatter::ConvertValue(const tools::Time& rTime)
3107 return rTime.GetMSFromTime();
3110 void TimeFormatter::SetTime(const tools::Time& rTime)
3112 auto nTime = ConvertValue(rTime);
3113 bool bForceOutput = GetEntryText().isEmpty() && rTime == GetTime();
3114 if (bForceOutput)
3116 ImplSetValue(nTime, true);
3117 return;
3119 SetValue(nTime);
3122 tools::Time TimeFormatter::GetTime()
3124 return ConvertValue(GetValue());
3127 void TimeFormatter::SetMin(const tools::Time& rNewMin)
3129 SetMinValue(ConvertValue(rNewMin));
3132 void TimeFormatter::SetMax(const tools::Time& rNewMax)
3134 SetMaxValue(ConvertValue(rNewMax));
3137 OUString TimeFormatter::FormatNumber(int nValue) const
3139 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
3140 return ::TimeFormatter::FormatTime(ConvertValue(nValue), m_eFormat, m_eTimeFormat, m_bDuration, rLocaleData);
3143 IMPL_LINK_NOARG(TimeFormatter, FormatOutputHdl, LinkParamNone*, bool)
3145 OUString sText = FormatNumber(GetValue());
3146 ImplSetTextImpl(sText, nullptr);
3147 return true;
3150 IMPL_LINK(TimeFormatter, ParseInputHdl, sal_Int64*, result, TriState)
3152 const LocaleDataWrapper& rLocaleDataWrapper = Application::GetSettings().GetLocaleDataWrapper();
3154 tools::Time aResult(0);
3155 bool bRet = ::TimeFormatter::TextToTime(GetEntryText(), aResult, m_eFormat, m_bDuration, rLocaleDataWrapper);
3156 if (bRet)
3157 *result = ConvertValue(aResult);
3159 return bRet ? TRISTATE_TRUE : TRISTATE_FALSE;
3162 IMPL_LINK(TimeFormatter, CursorChangedHdl, weld::Entry&, rEntry, void)
3164 int nStartPos, nEndPos;
3165 rEntry.get_selection_bounds(nStartPos, nEndPos);
3167 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
3168 const int nTimeArea = ::TimeFormatter::GetTimeArea(m_eFormat, GetEntryText(), nEndPos, rLocaleData);
3170 int nIncrements = 1;
3172 if (nTimeArea == 1)
3173 nIncrements = 1000 * 60 * 60;
3174 else if (nTimeArea == 2)
3175 nIncrements = 1000 * 60;
3176 else if (nTimeArea == 3)
3177 nIncrements = 1000;
3179 SetSpinSize(nIncrements);
3183 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */