Simplify using designated initializers
[LibreOffice.git] / vcl / source / control / field2.cxx
blobf082b08f798da76128d9083dd5dc7fc6c5f24689
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>
47 #include <tools/duration.hxx>
49 using namespace ::com::sun::star;
50 using namespace ::comphelper;
52 #define EDITMASK_LITERAL 'L'
53 #define EDITMASK_ALPHA 'a'
54 #define EDITMASK_UPPERALPHA 'A'
55 #define EDITMASK_ALPHANUM 'c'
56 #define EDITMASK_UPPERALPHANUM 'C'
57 #define EDITMASK_NUM 'N'
58 #define EDITMASK_NUMSPACE 'n'
59 #define EDITMASK_ALLCHAR 'x'
60 #define EDITMASK_UPPERALLCHAR 'X'
62 uno::Reference< i18n::XCharacterClassification > const & ImplGetCharClass()
64 ImplSVData *const pSVData = ImplGetSVData();
65 assert(pSVData);
67 if (!pSVData->m_xCharClass.is())
69 pSVData->m_xCharClass = vcl::unohelper::CreateCharacterClassification();
72 return pSVData->m_xCharClass;
75 static sal_Unicode* ImplAddString( sal_Unicode* pBuf, const OUString& rStr )
77 memcpy( pBuf, rStr.getStr(), rStr.getLength() * sizeof(sal_Unicode) );
78 pBuf += rStr.getLength();
79 return pBuf;
82 static sal_Unicode* ImplAddNum( sal_Unicode* pBuf, sal_uLong nNumber, int nMinLen )
84 // fill temp buffer with digits
85 sal_Unicode aTempBuf[30];
86 sal_Unicode* pTempBuf = aTempBuf;
89 *pTempBuf = static_cast<sal_Unicode>(nNumber % 10) + '0';
90 pTempBuf++;
91 nNumber /= 10;
92 if ( nMinLen )
93 nMinLen--;
95 while ( nNumber );
97 // fill with zeros up to the minimal length
98 while ( nMinLen > 0 )
100 *pBuf = '0';
101 pBuf++;
102 nMinLen--;
105 // copy temp buffer to real buffer
108 pTempBuf--;
109 *pBuf = *pTempBuf;
110 pBuf++;
112 while ( pTempBuf != aTempBuf );
114 return pBuf;
117 static sal_Unicode* ImplAddSNum( sal_Unicode* pBuf, sal_Int32 nNumber, int nMinLen )
119 if (nNumber < 0)
121 *pBuf++ = '-';
122 nNumber = -nNumber;
124 return ImplAddNum( pBuf, nNumber, nMinLen);
127 static sal_uInt16 ImplGetNum( const sal_Unicode*& rpBuf, bool& rbError )
129 if ( !*rpBuf )
131 rbError = true;
132 return 0;
135 sal_uInt16 nNumber = 0;
136 while( ( *rpBuf >= '0' ) && ( *rpBuf <= '9' ) )
138 nNumber *= 10;
139 nNumber += *rpBuf - '0';
140 rpBuf++;
143 return nNumber;
146 static void ImplSkipDelimiters( const sal_Unicode*& rpBuf )
148 while( ( *rpBuf == ',' ) || ( *rpBuf == '.' ) || ( *rpBuf == ';' ) ||
149 ( *rpBuf == ':' ) || ( *rpBuf == '-' ) || ( *rpBuf == '/' ) )
151 rpBuf++;
155 static bool ImplIsPatternChar( sal_Unicode cChar, char cEditMask )
157 sal_Int32 nType = 0;
161 OUString aCharStr(cChar);
162 nType = ImplGetCharClass()->getCharacterType( aCharStr, 0,
163 Application::GetSettings().GetLanguageTag().getLocale() );
165 catch (const css::uno::Exception&)
167 DBG_UNHANDLED_EXCEPTION("vcl.control");
168 return false;
171 if ( (cEditMask == EDITMASK_ALPHA) || (cEditMask == EDITMASK_UPPERALPHA) )
173 if( !CharClass::isLetterType( nType ) )
174 return false;
176 else if ( cEditMask == EDITMASK_NUM )
178 if( !CharClass::isNumericType( nType ) )
179 return false;
181 else if ( (cEditMask == EDITMASK_ALPHANUM) || (cEditMask == EDITMASK_UPPERALPHANUM) )
183 if( !CharClass::isLetterNumericType( nType ) )
184 return false;
186 else if ( (cEditMask == EDITMASK_ALLCHAR) || (cEditMask == EDITMASK_UPPERALLCHAR) )
188 if ( cChar < 32 )
189 return false;
191 else if ( cEditMask == EDITMASK_NUMSPACE )
193 if ( !CharClass::isNumericType( nType ) && ( cChar != ' ' ) )
194 return false;
196 else
197 return false;
199 return true;
202 static sal_Unicode ImplPatternChar( sal_Unicode cChar, char cEditMask )
204 if ( ImplIsPatternChar( cChar, cEditMask ) )
206 if ( (cEditMask == EDITMASK_UPPERALPHA) ||
207 (cEditMask == EDITMASK_UPPERALPHANUM) ||
208 ( cEditMask == EDITMASK_UPPERALLCHAR ) )
210 cChar = ImplGetCharClass()->toUpper(OUString(cChar), 0, 1,
211 Application::GetSettings().GetLanguageTag().getLocale())[0];
213 return cChar;
215 else
216 return 0;
219 static bool ImplCommaPointCharEqual( sal_Unicode c1, sal_Unicode c2 )
221 if ( c1 == c2 )
222 return true;
223 else if ( ((c1 == '.') || (c1 == ',')) &&
224 ((c2 == '.') || (c2 == ',')) )
225 return true;
226 else
227 return false;
230 static OUString ImplPatternReformat( const OUString& rStr,
231 const OString& rEditMask,
232 std::u16string_view rLiteralMask,
233 sal_uInt16 nFormatFlags )
235 if (rEditMask.isEmpty())
236 return rStr;
238 OUStringBuffer aOutStr(rLiteralMask);
239 sal_Unicode cTempChar;
240 sal_Unicode cChar;
241 sal_Unicode cLiteral;
242 char cMask;
243 sal_Int32 nStrIndex = 0;
244 sal_Int32 i = 0;
245 sal_Int32 n;
247 while ( i < rEditMask.getLength() )
249 if ( nStrIndex >= rStr.getLength() )
250 break;
252 cChar = rStr[nStrIndex];
253 cLiteral = rLiteralMask[i];
254 cMask = rEditMask[i];
256 // current position is a literal
257 if ( cMask == EDITMASK_LITERAL )
259 // if it is a literal copy otherwise ignore because it might be the next valid
260 // character of the string
261 if ( ImplCommaPointCharEqual( cChar, cLiteral ) )
262 nStrIndex++;
263 else
265 // Otherwise we check if it is an invalid character. This is the case if it does not
266 // fit in the pattern of the next non-literal character.
267 n = i+1;
268 while ( n < rEditMask.getLength() )
270 if ( rEditMask[n] != EDITMASK_LITERAL )
272 if ( !ImplIsPatternChar( cChar, rEditMask[n] ) )
273 nStrIndex++;
274 break;
277 n++;
281 else
283 // valid character at this position
284 cTempChar = ImplPatternChar( cChar, cMask );
285 if ( cTempChar )
287 // use this character
288 aOutStr[i] = cTempChar;
289 nStrIndex++;
291 else
293 // copy if it is a literal character
294 if ( cLiteral == cChar )
295 nStrIndex++;
296 else
298 // If the invalid character might be the next literal character then we jump
299 // ahead to it, otherwise we ignore it. Do only if empty literals are allowed.
300 if ( nFormatFlags & PATTERN_FORMAT_EMPTYLITERALS )
302 n = i;
303 while ( n < rEditMask.getLength() )
305 if ( rEditMask[n] == EDITMASK_LITERAL )
307 if ( ImplCommaPointCharEqual( cChar, rLiteralMask[n] ) )
308 i = n+1;
310 break;
313 n++;
317 nStrIndex++;
318 continue;
323 i++;
326 return aOutStr.makeStringAndClear();
329 static void ImplPatternMaxPos( std::u16string_view rStr, const OString& rEditMask,
330 sal_uInt16 nFormatFlags, bool bSameMask,
331 sal_Int32 nCursorPos, sal_Int32& rPos )
334 // last position must not be longer than the contained string
335 sal_Int32 nMaxPos = rStr.size();
337 // if non empty literals are allowed ignore blanks at the end as well
338 if ( bSameMask && !(nFormatFlags & PATTERN_FORMAT_EMPTYLITERALS) )
340 while ( nMaxPos )
342 if ( (rEditMask[nMaxPos-1] != EDITMASK_LITERAL) &&
343 (rStr[nMaxPos-1] != ' ') )
344 break;
345 nMaxPos--;
348 // if we are in front of a literal, continue search until first character after the literal
349 sal_Int32 nTempPos = nMaxPos;
350 while ( nTempPos < rEditMask.getLength() )
352 if ( rEditMask[nTempPos] != EDITMASK_LITERAL )
354 nMaxPos = nTempPos;
355 break;
357 nTempPos++;
361 if ( rPos > nMaxPos )
362 rPos = nMaxPos;
364 // character should not move left
365 if ( rPos < nCursorPos )
366 rPos = nCursorPos;
369 static OUString ImplPatternProcessStrictModify(const OUString& rText,
370 const OString& rEditMask,
371 std::u16string_view rLiteralMask,
372 bool bSameMask)
374 OUString aText(rText);
376 // remove leading blanks
377 if (bSameMask && !rEditMask.isEmpty())
379 sal_Int32 i = 0;
380 sal_Int32 nMaxLen = aText.getLength();
381 while ( i < nMaxLen )
383 if ( (rEditMask[i] != EDITMASK_LITERAL) &&
384 (aText[i] != ' ') )
385 break;
387 i++;
389 // keep all literal characters
390 while ( i && (rEditMask[i] == EDITMASK_LITERAL) )
391 i--;
392 aText = aText.copy( i );
395 return ImplPatternReformat(aText, rEditMask, rLiteralMask, 0);
398 static void ImplPatternProcessStrictModify( Edit* pEdit,
399 const OString& rEditMask,
400 std::u16string_view rLiteralMask,
401 bool bSameMask )
403 OUString aText = pEdit->GetText();
404 OUString aNewText = ImplPatternProcessStrictModify(aText,
405 rEditMask,
406 rLiteralMask,
407 bSameMask);
409 if ( aNewText == aText )
410 return;
412 // adjust selection such that it remains at the end if it was there before
413 Selection aSel = pEdit->GetSelection();
414 sal_Int64 nMaxSel = std::max( aSel.Min(), aSel.Max() );
415 if ( nMaxSel >= aText.getLength() )
417 sal_Int32 nMaxPos = aNewText.getLength();
418 ImplPatternMaxPos(aNewText, rEditMask, 0, bSameMask, nMaxSel, nMaxPos);
419 if ( aSel.Min() == aSel.Max() )
421 aSel.Min() = nMaxPos;
422 aSel.Max() = aSel.Min();
424 else if ( aSel.Min() > aSel.Max() )
425 aSel.Min() = nMaxPos;
426 else
427 aSel.Max() = nMaxPos;
429 pEdit->SetText( aNewText, aSel );
432 static void ImplPatternProcessStrictModify( weld::Entry& rEntry,
433 const OString& rEditMask,
434 std::u16string_view rLiteralMask,
435 bool bSameMask )
437 OUString aText = rEntry.get_text();
438 OUString aNewText = ImplPatternProcessStrictModify(aText,
439 rEditMask,
440 rLiteralMask,
441 bSameMask);
443 if (aNewText == aText)
444 return;
446 // adjust selection such that it remains at the end if it was there before
447 int nStartPos, nEndPos;
448 rEntry.get_selection_bounds(nStartPos, nEndPos);
450 int nMaxSel = std::max(nStartPos, nEndPos);
451 if (nMaxSel >= aText.getLength())
453 sal_Int32 nMaxPos = aNewText.getLength();
454 ImplPatternMaxPos(aNewText, rEditMask, 0, bSameMask, nMaxSel, nMaxPos);
455 if (nStartPos == nEndPos)
457 nStartPos = nMaxPos;
458 nEndPos = nMaxPos;
460 else if (nStartPos > nMaxPos)
461 nStartPos = nMaxPos;
462 else
463 nEndPos = nMaxPos;
465 rEntry.set_text(aNewText);
466 rEntry.select_region(nStartPos, nEndPos);
469 static sal_Int32 ImplPatternLeftPos(std::string_view rEditMask, sal_Int32 nCursorPos)
471 // search non-literal predecessor
472 sal_Int32 nNewPos = nCursorPos;
473 sal_Int32 nTempPos = nNewPos;
474 while ( nTempPos )
476 if ( rEditMask[nTempPos-1] != EDITMASK_LITERAL )
478 nNewPos = nTempPos-1;
479 break;
481 nTempPos--;
483 return nNewPos;
486 static sal_Int32 ImplPatternRightPos( std::u16string_view rStr, const OString& rEditMask,
487 sal_uInt16 nFormatFlags, bool bSameMask,
488 sal_Int32 nCursorPos )
490 // search non-literal successor
491 sal_Int32 nNewPos = nCursorPos;
493 for(sal_Int32 nTempPos = nNewPos+1; nTempPos < rEditMask.getLength(); ++nTempPos )
495 if ( rEditMask[nTempPos] != EDITMASK_LITERAL )
497 nNewPos = nTempPos;
498 break;
501 ImplPatternMaxPos( rStr, rEditMask, nFormatFlags, bSameMask, nCursorPos, nNewPos );
502 return nNewPos;
505 namespace
507 class IEditImplementation
509 public:
510 virtual ~IEditImplementation() {}
512 virtual OUString GetText() const = 0;
513 virtual void SetText(const OUString& rStr, const Selection& rSelection) = 0;
515 virtual Selection GetSelection() const = 0;
516 virtual void SetSelection(const Selection& rSelection) = 0;
518 virtual bool IsInsertMode() const = 0;
520 virtual void SetModified() = 0;
524 static bool ImplPatternProcessKeyInput( IEditImplementation& rEdit, const KeyEvent& rKEvt,
525 const OString& rEditMask,
526 std::u16string_view rLiteralMask,
527 bool bStrictFormat,
528 bool bSameMask,
529 bool& rbInKeyInput )
531 if ( rEditMask.isEmpty() || !bStrictFormat )
532 return false;
534 sal_uInt16 nFormatFlags = 0;
535 Selection aOldSel = rEdit.GetSelection();
536 vcl::KeyCode aCode = rKEvt.GetKeyCode();
537 sal_Unicode cChar = rKEvt.GetCharCode();
538 sal_uInt16 nKeyCode = aCode.GetCode();
539 bool bShift = aCode.IsShift();
540 sal_Int32 nCursorPos = static_cast<sal_Int32>(aOldSel.Max());
541 sal_Int32 nNewPos;
542 sal_Int32 nTempPos;
544 if ( nKeyCode && !aCode.IsMod1() && !aCode.IsMod2() )
546 if ( nKeyCode == KEY_LEFT )
548 Selection aSel( ImplPatternLeftPos( rEditMask, nCursorPos ) );
549 if ( bShift )
550 aSel.Min() = aOldSel.Min();
551 rEdit.SetSelection( aSel );
552 return true;
554 else if ( nKeyCode == KEY_RIGHT )
556 // Use the start of selection as minimum; even a small position is allowed in case that
557 // all was selected by the focus
558 Selection aSel( aOldSel );
559 aSel.Normalize();
560 nCursorPos = aSel.Min();
561 aSel.Max() = ImplPatternRightPos( rEdit.GetText(), rEditMask, nFormatFlags, bSameMask, nCursorPos );
562 if ( bShift )
563 aSel.Min() = aOldSel.Min();
564 else
565 aSel.Min() = aSel.Max();
566 rEdit.SetSelection( aSel );
567 return true;
569 else if ( nKeyCode == KEY_HOME )
571 // Home is the position of the first non-literal character
572 nNewPos = 0;
573 while ( (nNewPos < rEditMask.getLength()) &&
574 (rEditMask[nNewPos] == EDITMASK_LITERAL) )
575 nNewPos++;
577 // Home should not move to the right
578 if ( nCursorPos < nNewPos )
579 nNewPos = nCursorPos;
580 Selection aSel( nNewPos );
581 if ( bShift )
582 aSel.Min() = aOldSel.Min();
583 rEdit.SetSelection( aSel );
584 return true;
586 else if ( nKeyCode == KEY_END )
588 // End is position of last non-literal character
589 nNewPos = rEditMask.getLength();
590 while ( nNewPos &&
591 (rEditMask[nNewPos-1] == EDITMASK_LITERAL) )
592 nNewPos--;
593 // Use the start of selection as minimum; even a small position is allowed in case that
594 // all was selected by the focus
595 Selection aSel( aOldSel );
596 aSel.Normalize();
597 nCursorPos = static_cast<sal_Int32>(aSel.Min());
598 ImplPatternMaxPos( rEdit.GetText(), rEditMask, nFormatFlags, bSameMask, nCursorPos, nNewPos );
599 aSel.Max() = nNewPos;
600 if ( bShift )
601 aSel.Min() = aOldSel.Min();
602 else
603 aSel.Min() = aSel.Max();
604 rEdit.SetSelection( aSel );
605 return true;
607 else if ( (nKeyCode == KEY_BACKSPACE) || (nKeyCode == KEY_DELETE) )
609 OUString aOldStr( rEdit.GetText() );
610 OUStringBuffer aStr( aOldStr );
611 Selection aSel = aOldSel;
613 aSel.Normalize();
614 nNewPos = static_cast<sal_Int32>(aSel.Min());
616 // if selection then delete it
617 if ( aSel.Len() )
619 if ( bSameMask )
620 aStr.remove( static_cast<sal_Int32>(aSel.Min()), static_cast<sal_Int32>(aSel.Len()) );
621 else
623 std::u16string_view aRep = rLiteralMask.substr( static_cast<sal_Int32>(aSel.Min()), static_cast<sal_Int32>(aSel.Len()) );
624 aStr.remove( aSel.Min(), aRep.size() );
625 aStr.insert( aSel.Min(), aRep );
628 else
630 if ( nKeyCode == KEY_BACKSPACE )
632 nTempPos = nNewPos;
633 nNewPos = ImplPatternLeftPos( rEditMask, nTempPos );
635 else
636 nTempPos = ImplPatternRightPos( aStr, rEditMask, nFormatFlags, bSameMask, nNewPos );
638 if ( nNewPos != nTempPos )
640 if ( bSameMask )
642 if ( rEditMask[nNewPos] != EDITMASK_LITERAL )
643 aStr.remove( nNewPos, 1 );
645 else
647 aStr[nNewPos] = rLiteralMask[nNewPos];
652 OUString sStr = aStr.makeStringAndClear();
653 if ( aOldStr != sStr )
655 if ( bSameMask )
656 sStr = ImplPatternReformat( sStr, rEditMask, rLiteralMask, nFormatFlags );
657 rbInKeyInput = true;
658 rEdit.SetText( sStr, Selection( nNewPos ) );
659 rEdit.SetModified();
660 rbInKeyInput = false;
662 else
663 rEdit.SetSelection( Selection( nNewPos ) );
665 return true;
667 else if ( nKeyCode == KEY_INSERT )
669 // you can only set InsertMode for a PatternField if the
670 // mask is equal at all input positions
671 if ( !bSameMask )
673 return true;
678 if ( rKEvt.GetKeyCode().IsMod2() || (cChar < 32) || (cChar == 127) )
679 return false;
681 Selection aSel = aOldSel;
682 aSel.Normalize();
683 nNewPos = aSel.Min();
685 if ( nNewPos < rEditMask.getLength() )
687 sal_Unicode cPattChar = ImplPatternChar( cChar, rEditMask[nNewPos] );
688 if ( cPattChar )
689 cChar = cPattChar;
690 else
692 // If no valid character, check if the user wanted to jump to next literal. We do this
693 // only if we're after a character, so that literals that were skipped automatically
694 // do not influence the position anymore.
695 if ( nNewPos &&
696 (rEditMask[nNewPos-1] != EDITMASK_LITERAL) &&
697 !aSel.Len() )
699 // search for next character not being a literal
700 nTempPos = nNewPos;
701 while ( nTempPos < rEditMask.getLength() )
703 if ( rEditMask[nTempPos] == EDITMASK_LITERAL )
705 // only valid if no literal present
706 if ( (rEditMask[nTempPos+1] != EDITMASK_LITERAL ) &&
707 ImplCommaPointCharEqual( cChar, rLiteralMask[nTempPos] ) )
709 nTempPos++;
710 ImplPatternMaxPos( rEdit.GetText(), rEditMask, nFormatFlags, bSameMask, nNewPos, nTempPos );
711 if ( nTempPos > nNewPos )
713 rEdit.SetSelection( Selection( nTempPos ) );
714 return true;
717 break;
719 nTempPos++;
723 cChar = 0;
726 else
727 cChar = 0;
728 if ( cChar )
730 OUStringBuffer aStr(rEdit.GetText());
731 bool bError = false;
732 if ( bSameMask && rEdit.IsInsertMode() )
734 // crop spaces and literals at the end until current position
735 sal_Int32 n = aStr.getLength();
736 while ( n && (n > nNewPos) )
738 if ( (aStr[n-1] != ' ') &&
739 ((n > rEditMask.getLength()) || (rEditMask[n-1] != EDITMASK_LITERAL)) )
740 break;
742 n--;
744 aStr.truncate( n );
746 if ( aSel.Len() )
747 aStr.remove( aSel.Min(), aSel.Len() );
749 if ( aStr.getLength() < rEditMask.getLength() )
751 // possibly extend string until cursor position
752 if ( aStr.getLength() < nNewPos )
753 aStr.append( rLiteralMask.substr(aStr.getLength(), nNewPos-aStr.getLength()) );
754 if ( nNewPos < aStr.getLength() )
755 aStr.insert( cChar, nNewPos );
756 else if ( nNewPos < rEditMask.getLength() )
757 aStr.append(cChar);
758 aStr = ImplPatternReformat( aStr.toString(), rEditMask, rLiteralMask, nFormatFlags );
760 else
761 bError = true;
763 else
765 if ( aSel.Len() )
767 // delete selection
768 std::u16string_view aRep = rLiteralMask.substr( aSel.Min(), aSel.Len() );
769 aStr.remove( aSel.Min(), aRep.size() );
770 aStr.insert( aSel.Min(), aRep );
773 if ( nNewPos < aStr.getLength() )
774 aStr[nNewPos] = cChar;
775 else if ( nNewPos < rEditMask.getLength() )
776 aStr.append(cChar);
779 if ( !bError )
781 rbInKeyInput = true;
782 const OUString sStr = aStr.makeStringAndClear();
783 Selection aNewSel( ImplPatternRightPos( sStr, rEditMask, nFormatFlags, bSameMask, nNewPos ) );
784 rEdit.SetText( sStr, aNewSel );
785 rEdit.SetModified();
786 rbInKeyInput = false;
790 return true;
793 namespace
795 bool ImplSetMask(const OString& rEditMask, OUString& rLiteralMask)
797 bool bSameMask = true;
799 if (rEditMask.getLength() != rLiteralMask.getLength())
801 OUStringBuffer aBuf(rLiteralMask);
802 if (rEditMask.getLength() < aBuf.getLength())
803 aBuf.setLength(rEditMask.getLength());
804 else
805 comphelper::string::padToLength(aBuf, rEditMask.getLength(), ' ');
806 rLiteralMask = aBuf.makeStringAndClear();
809 // Strict mode allows only the input mode if only equal characters are allowed as mask and if
810 // only spaces are specified which are not allowed by the mask
811 sal_Int32 i = 0;
812 char c = 0;
813 while ( i < rEditMask.getLength() )
815 char cTemp = rEditMask[i];
816 if ( cTemp != EDITMASK_LITERAL )
818 if ( (cTemp == EDITMASK_ALLCHAR) ||
819 (cTemp == EDITMASK_UPPERALLCHAR) ||
820 (cTemp == EDITMASK_NUMSPACE) )
822 bSameMask = false;
823 break;
825 if ( i < rLiteralMask.getLength() )
827 if ( rLiteralMask[i] != ' ' )
829 bSameMask = false;
830 break;
833 if ( !c )
834 c = cTemp;
835 if ( cTemp != c )
837 bSameMask = false;
838 break;
841 i++;
844 return bSameMask;
848 PatternFormatter::PatternFormatter(Edit* pEdit)
849 : FormatterBase(pEdit)
851 mbSameMask = true;
852 mbInPattKeyInput = false;
855 PatternFormatter::~PatternFormatter()
859 void PatternFormatter::SetMask( const OString& rEditMask,
860 const OUString& rLiteralMask )
862 m_aEditMask = rEditMask;
863 maLiteralMask = rLiteralMask;
864 mbSameMask = ImplSetMask(m_aEditMask, maLiteralMask);
865 ReformatAll();
868 namespace
870 class EntryImplementation : public IEditImplementation
872 public:
873 EntryImplementation(weld::PatternFormatter& rFormatter)
874 : m_rFormatter(rFormatter)
875 , m_rEntry(rFormatter.get_widget())
879 virtual OUString GetText() const override
881 return m_rEntry.get_text();
884 virtual void SetText(const OUString& rStr, const Selection& rSelection) override
886 m_rEntry.set_text(rStr);
887 SetSelection(rSelection);
890 virtual Selection GetSelection() const override
892 int nStartPos, nEndPos;
893 m_rEntry.get_selection_bounds(nStartPos, nEndPos);
894 return Selection(nStartPos, nEndPos);
897 virtual void SetSelection(const Selection& rSelection) override
899 auto nMin = rSelection.Min();
900 auto nMax = rSelection.Max();
901 m_rEntry.select_region(nMin < 0 ? 0 : nMin, nMax == SELECTION_MAX ? -1 : nMax);
904 virtual bool IsInsertMode() const override
906 return !m_rEntry.get_overwrite_mode();
909 virtual void SetModified() override
911 m_rFormatter.Modify();
914 private:
915 weld::PatternFormatter& m_rFormatter;
916 weld::Entry& m_rEntry;
920 namespace weld
922 void PatternFormatter::SetStrictFormat(bool bStrict)
924 if (bStrict != m_bStrictFormat)
926 m_bStrictFormat = bStrict;
927 if (m_bStrictFormat)
928 ReformatAll();
932 void PatternFormatter::SetMask(const OString& rEditMask,
933 const OUString& rLiteralMask)
935 m_aEditMask = rEditMask;
936 m_aLiteralMask = rLiteralMask;
937 m_bSameMask = ImplSetMask(m_aEditMask, m_aLiteralMask);
938 ReformatAll();
941 void PatternFormatter::ReformatAll()
943 m_rEntry.set_text(ImplPatternReformat(m_rEntry.get_text(), m_aEditMask, m_aLiteralMask, 0/*nFormatFlags*/));
944 if (!m_bSameMask && m_bStrictFormat && m_rEntry.get_editable())
945 m_rEntry.set_overwrite_mode(true);
948 void PatternFormatter::EntryGainFocus()
950 m_bReformat = false;
953 void PatternFormatter::EntryLostFocus()
955 if (m_bReformat)
956 ReformatAll();
959 void PatternFormatter::Modify()
961 if (!m_bInPattKeyInput)
963 if (m_bStrictFormat)
964 ImplPatternProcessStrictModify(m_rEntry, m_aEditMask, m_aLiteralMask, m_bSameMask);
965 else
966 m_bReformat = true;
968 m_aModifyHdl.Call(m_rEntry);
971 IMPL_LINK(PatternFormatter, KeyInputHdl, const KeyEvent&, rKEvt, bool)
973 if (m_aKeyPressHdl.Call(rKEvt))
974 return true;
975 if (rKEvt.GetKeyCode().IsMod2())
976 return false;
977 EntryImplementation aAdapt(*this);
978 return ImplPatternProcessKeyInput(aAdapt, rKEvt, m_aEditMask, m_aLiteralMask,
979 m_bStrictFormat,
980 m_bSameMask, m_bInPattKeyInput);
984 void PatternFormatter::SetString( const OUString& rStr )
986 if ( GetField() )
988 GetField()->SetText( rStr );
989 MarkToBeReformatted( false );
993 OUString PatternFormatter::GetString() const
995 if ( !GetField() )
996 return OUString();
997 else
998 return ImplPatternReformat( GetField()->GetText(), m_aEditMask, maLiteralMask, 0/*nFormatFlags*/ );
1001 void PatternFormatter::Reformat()
1003 if ( GetField() )
1005 ImplSetText( ImplPatternReformat( GetField()->GetText(), m_aEditMask, maLiteralMask, 0/*nFormatFlags*/ ) );
1006 if ( !mbSameMask && IsStrictFormat() && !GetField()->IsReadOnly() )
1007 GetField()->SetInsertMode( false );
1011 PatternField::PatternField(vcl::Window* pParent, WinBits nWinStyle)
1012 : SpinField(pParent, nWinStyle)
1013 , PatternFormatter(this)
1015 Reformat();
1018 void PatternField::dispose()
1020 ClearField();
1021 SpinField::dispose();
1024 namespace
1026 class EditImplementation : public IEditImplementation
1028 public:
1029 EditImplementation(Edit& rEdit)
1030 : m_rEdit(rEdit)
1034 virtual OUString GetText() const override
1036 return m_rEdit.GetText();
1039 virtual void SetText(const OUString& rStr, const Selection& rSelection) override
1041 m_rEdit.SetText(rStr, rSelection);
1044 virtual Selection GetSelection() const override
1046 return m_rEdit.GetSelection();
1049 virtual void SetSelection(const Selection& rSelection) override
1051 m_rEdit.SetSelection(rSelection);
1054 virtual bool IsInsertMode() const override
1056 return m_rEdit.IsInsertMode();
1059 virtual void SetModified() override
1061 m_rEdit.SetModifyFlag();
1062 m_rEdit.Modify();
1065 private:
1066 Edit& m_rEdit;
1070 bool PatternField::PreNotify( NotifyEvent& rNEvt )
1072 if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
1074 EditImplementation aAdapt(*GetField());
1075 if ( ImplPatternProcessKeyInput( aAdapt, *rNEvt.GetKeyEvent(), GetEditMask(), GetLiteralMask(),
1076 IsStrictFormat(),
1077 ImplIsSameMask(), ImplGetInPattKeyInput() ) )
1078 return true;
1081 return SpinField::PreNotify( rNEvt );
1084 bool PatternField::EventNotify( NotifyEvent& rNEvt )
1086 if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
1087 MarkToBeReformatted( false );
1088 else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
1090 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1091 Reformat();
1094 return SpinField::EventNotify( rNEvt );
1097 void PatternField::Modify()
1099 if ( !ImplGetInPattKeyInput() )
1101 if ( IsStrictFormat() )
1102 ImplPatternProcessStrictModify( GetField(), GetEditMask(), GetLiteralMask(), ImplIsSameMask() );
1103 else
1104 MarkToBeReformatted( true );
1107 SpinField::Modify();
1110 PatternBox::PatternBox(vcl::Window* pParent, WinBits nWinStyle)
1111 : ComboBox( pParent, nWinStyle )
1112 , PatternFormatter(this)
1114 Reformat();
1117 void PatternBox::dispose()
1119 ClearField();
1120 ComboBox::dispose();
1123 bool PatternBox::PreNotify( NotifyEvent& rNEvt )
1125 if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
1127 EditImplementation aAdapt(*GetField());
1128 if ( ImplPatternProcessKeyInput( aAdapt, *rNEvt.GetKeyEvent(), GetEditMask(), GetLiteralMask(),
1129 IsStrictFormat(),
1130 ImplIsSameMask(), ImplGetInPattKeyInput() ) )
1131 return true;
1134 return ComboBox::PreNotify( rNEvt );
1137 bool PatternBox::EventNotify( NotifyEvent& rNEvt )
1139 if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
1140 MarkToBeReformatted( false );
1141 else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
1143 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1144 Reformat();
1147 return ComboBox::EventNotify( rNEvt );
1150 void PatternBox::Modify()
1152 if ( !ImplGetInPattKeyInput() )
1154 if ( IsStrictFormat() )
1155 ImplPatternProcessStrictModify( GetField(), GetEditMask(), GetLiteralMask(), ImplIsSameMask() );
1156 else
1157 MarkToBeReformatted( true );
1160 ComboBox::Modify();
1163 void PatternBox::ReformatAll()
1165 OUString aStr;
1166 SetUpdateMode( false );
1167 const sal_Int32 nEntryCount = GetEntryCount();
1168 for ( sal_Int32 i=0; i < nEntryCount; ++i )
1170 aStr = ImplPatternReformat( GetEntry( i ), GetEditMask(), GetLiteralMask(), 0/*nFormatFlags*/ );
1171 RemoveEntryAt(i);
1172 InsertEntry( aStr, i );
1174 PatternFormatter::Reformat();
1175 SetUpdateMode( true );
1178 static ExtDateFieldFormat ImplGetExtFormat( LongDateOrder eOld )
1180 switch( eOld )
1182 case LongDateOrder::YDM:
1183 case LongDateOrder::DMY: return ExtDateFieldFormat::ShortDDMMYY;
1184 case LongDateOrder::MDY: return ExtDateFieldFormat::ShortMMDDYY;
1185 case LongDateOrder::YMD:
1186 default: return ExtDateFieldFormat::ShortYYMMDD;
1190 static sal_uInt16 ImplCutNumberFromString( OUString& rStr )
1192 sal_Int32 i1 = 0;
1193 while (i1 != rStr.getLength() && (rStr[i1] < '0' || rStr[i1] > '9')) {
1194 ++i1;
1196 sal_Int32 i2 = i1;
1197 while (i2 != rStr.getLength() && rStr[i2] >= '0' && rStr[i2] <= '9') {
1198 ++i2;
1200 sal_Int32 nValue = o3tl::toInt32(rStr.subView(i1, i2-i1));
1201 rStr = rStr.copy(std::min(i2+1, rStr.getLength()));
1202 return nValue;
1205 static bool ImplCutMonthName( OUString& rStr, std::u16string_view _rLookupMonthName )
1207 sal_Int32 index = 0;
1208 rStr = rStr.replaceFirst(_rLookupMonthName, "", &index);
1209 return index >= 0;
1212 static sal_uInt16 ImplGetMonthFromCalendarItem( OUString& rStr, const uno::Sequence< i18n::CalendarItem2 >& rMonths )
1214 const sal_uInt16 nMonths = rMonths.getLength();
1215 for (sal_uInt16 i=0; i < nMonths; ++i)
1217 // long month name?
1218 if ( ImplCutMonthName( rStr, rMonths[i].FullName ) )
1219 return i+1;
1221 // short month name?
1222 if ( ImplCutMonthName( rStr, rMonths[i].AbbrevName ) )
1223 return i+1;
1225 return 0;
1228 static sal_uInt16 ImplCutMonthFromString( OUString& rStr, OUString& rCalendarName,
1229 const LocaleDataWrapper& rLocaleData, const CalendarWrapper& rCalendarWrapper )
1231 const OUString aDefaultCalendarName( rCalendarWrapper.getUniqueID());
1232 rCalendarName = aDefaultCalendarName;
1234 // Search for a month name of the loaded default calendar.
1235 const uno::Sequence< i18n::CalendarItem2 > aMonths = rCalendarWrapper.getMonths();
1236 sal_uInt16 nMonth = ImplGetMonthFromCalendarItem( rStr, aMonths);
1237 if (nMonth > 0)
1238 return nMonth;
1240 // And also possessive genitive and partitive month names.
1241 const uno::Sequence< i18n::CalendarItem2 > aGenitiveMonths = rCalendarWrapper.getGenitiveMonths();
1242 if (aGenitiveMonths != aMonths)
1244 nMonth = ImplGetMonthFromCalendarItem( rStr, aGenitiveMonths);
1245 if (nMonth > 0)
1246 return nMonth;
1248 const uno::Sequence< i18n::CalendarItem2 > aPartitiveMonths = rCalendarWrapper.getPartitiveMonths();
1249 if (aPartitiveMonths != aMonths)
1251 nMonth = ImplGetMonthFromCalendarItem( rStr, aPartitiveMonths);
1252 if (nMonth > 0)
1253 return nMonth;
1256 // Check if there are more calendars and try them if so, as the long date
1257 // format is obtained from the number formatter this is possible (e.g.
1258 // ar_DZ "[~hijri] ...")
1259 const uno::Sequence< i18n::Calendar2 > aCalendars = rLocaleData.getAllCalendars();
1260 if (aCalendars.getLength() > 1)
1262 for (const auto& rCalendar : aCalendars)
1264 if (rCalendar.Name != aDefaultCalendarName)
1266 rCalendarName = rCalendar.Name;
1268 nMonth = ImplGetMonthFromCalendarItem( rStr, rCalendar.Months);
1269 if (nMonth > 0)
1270 return nMonth;
1272 if (rCalendar.Months != rCalendar.GenitiveMonths)
1274 nMonth = ImplGetMonthFromCalendarItem( rStr, rCalendar.GenitiveMonths);
1275 if (nMonth > 0)
1276 return nMonth;
1279 if (rCalendar.Months != rCalendar.PartitiveMonths)
1281 nMonth = ImplGetMonthFromCalendarItem( rStr, rCalendar.PartitiveMonths);
1282 if (nMonth > 0)
1283 return nMonth;
1286 rCalendarName = aDefaultCalendarName;
1291 return ImplCutNumberFromString( rStr );
1294 static OUString ImplGetDateSep( const LocaleDataWrapper& rLocaleDataWrapper, ExtDateFieldFormat eFormat )
1296 if ( ( eFormat == ExtDateFieldFormat::ShortYYMMDD_DIN5008 ) || ( eFormat == ExtDateFieldFormat::ShortYYYYMMDD_DIN5008 ) )
1297 return u"-"_ustr;
1298 else
1299 return rLocaleDataWrapper.getDateSep();
1302 static bool ImplDateProcessKeyInput( const KeyEvent& rKEvt, ExtDateFieldFormat eFormat,
1303 const LocaleDataWrapper& rLocaleDataWrapper )
1305 sal_Unicode cChar = rKEvt.GetCharCode();
1306 sal_uInt16 nGroup = rKEvt.GetKeyCode().GetGroup();
1307 return !((nGroup == KEYGROUP_FKEYS) ||
1308 (nGroup == KEYGROUP_CURSOR) ||
1309 (nGroup == KEYGROUP_MISC)||
1310 ((cChar >= '0') && (cChar <= '9')) ||
1311 (cChar == ImplGetDateSep( rLocaleDataWrapper, eFormat )[0]));
1314 bool DateFormatter::TextToDate(const OUString& rStr, Date& rDate, ExtDateFieldFormat eDateOrder,
1315 const LocaleDataWrapper& rLocaleDataWrapper, const CalendarWrapper& rCalendarWrapper)
1317 sal_uInt16 nDay = 0;
1318 sal_uInt16 nMonth = 0;
1319 sal_uInt16 nYear = 0;
1320 bool bError = false;
1321 OUString aStr( rStr );
1323 if ( eDateOrder == ExtDateFieldFormat::SystemLong )
1325 OUString aCalendarName;
1326 LongDateOrder eFormat = rLocaleDataWrapper.getLongDateOrder();
1327 switch( eFormat )
1329 case LongDateOrder::MDY:
1330 nMonth = ImplCutMonthFromString( aStr, aCalendarName, rLocaleDataWrapper, rCalendarWrapper );
1331 nDay = ImplCutNumberFromString( aStr );
1332 nYear = ImplCutNumberFromString( aStr );
1333 break;
1334 case LongDateOrder::DMY:
1335 nDay = ImplCutNumberFromString( aStr );
1336 nMonth = ImplCutMonthFromString( aStr, aCalendarName, rLocaleDataWrapper, rCalendarWrapper );
1337 nYear = ImplCutNumberFromString( aStr );
1338 break;
1339 case LongDateOrder::YDM:
1340 nYear = ImplCutNumberFromString( aStr );
1341 nDay = ImplCutNumberFromString( aStr );
1342 nMonth = ImplCutMonthFromString( aStr, aCalendarName, rLocaleDataWrapper, rCalendarWrapper );
1343 break;
1344 case LongDateOrder::YMD:
1345 default:
1346 nYear = ImplCutNumberFromString( aStr );
1347 nMonth = ImplCutMonthFromString( aStr, aCalendarName, rLocaleDataWrapper, rCalendarWrapper );
1348 nDay = ImplCutNumberFromString( aStr );
1349 break;
1351 if (aCalendarName != "gregorian")
1353 // Calendar widget is Gregorian, convert date.
1354 // Need full date.
1355 bError = !nDay || !nMonth || !nYear;
1356 if (!bError)
1358 CalendarWrapper aCW( rLocaleDataWrapper.getComponentContext());
1359 aCW.loadCalendar( aCalendarName, rLocaleDataWrapper.getLoadedLanguageTag().getLocale());
1360 aCW.setDateTime(0.5); // get rid of current time, set some day noon
1361 aCW.setValue( i18n::CalendarFieldIndex::DAY_OF_MONTH, nDay);
1362 aCW.setValue( i18n::CalendarFieldIndex::MONTH, nMonth - 1);
1363 aCW.setValue( i18n::CalendarFieldIndex::YEAR, nYear);
1364 bError = !aCW.isValid();
1365 if (!bError)
1367 Date aDate = aCW.getEpochStart() + aCW.getDateTime();
1368 nYear = aDate.GetYear();
1369 nMonth = aDate.GetMonth();
1370 nDay = aDate.GetDay();
1375 else
1377 bool bYear = true;
1379 // Check if year is present:
1380 OUString aDateSep = ImplGetDateSep( rLocaleDataWrapper, eDateOrder );
1381 sal_Int32 nSepPos = aStr.indexOf( aDateSep );
1382 if ( nSepPos < 0 )
1383 return false;
1384 nSepPos = aStr.indexOf( aDateSep, nSepPos+1 );
1385 if ( ( nSepPos < 0 ) || ( nSepPos == (aStr.getLength()-1) ) )
1387 bYear = false;
1388 nYear = Date( Date::SYSTEM ).GetYearUnsigned();
1391 const sal_Unicode* pBuf = aStr.getStr();
1392 ImplSkipDelimiters( pBuf );
1394 switch ( eDateOrder )
1396 case ExtDateFieldFormat::ShortDDMMYY:
1397 case ExtDateFieldFormat::ShortDDMMYYYY:
1399 nDay = ImplGetNum( pBuf, bError );
1400 ImplSkipDelimiters( pBuf );
1401 nMonth = ImplGetNum( pBuf, bError );
1402 ImplSkipDelimiters( pBuf );
1403 if ( bYear )
1404 nYear = ImplGetNum( pBuf, bError );
1406 break;
1407 case ExtDateFieldFormat::ShortMMDDYY:
1408 case ExtDateFieldFormat::ShortMMDDYYYY:
1410 nMonth = ImplGetNum( pBuf, bError );
1411 ImplSkipDelimiters( pBuf );
1412 nDay = ImplGetNum( pBuf, bError );
1413 ImplSkipDelimiters( pBuf );
1414 if ( bYear )
1415 nYear = ImplGetNum( pBuf, bError );
1417 break;
1418 case ExtDateFieldFormat::ShortYYMMDD:
1419 case ExtDateFieldFormat::ShortYYYYMMDD:
1420 case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
1421 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1423 if ( bYear )
1424 nYear = ImplGetNum( pBuf, bError );
1425 ImplSkipDelimiters( pBuf );
1426 nMonth = ImplGetNum( pBuf, bError );
1427 ImplSkipDelimiters( pBuf );
1428 nDay = ImplGetNum( pBuf, bError );
1430 break;
1432 default:
1434 OSL_FAIL( "DateOrder???" );
1439 if ( bError || !nDay || !nMonth )
1440 return false;
1442 Date aNewDate( nDay, nMonth, nYear );
1443 DateFormatter::ExpandCentury( aNewDate, officecfg::Office::Common::DateFormat::TwoDigitYear::get() );
1444 if ( aNewDate.IsValidDate() )
1446 rDate = aNewDate;
1447 return true;
1449 return false;
1452 void DateFormatter::ImplDateReformat( const OUString& rStr, OUString& rOutStr )
1454 Date aDate( Date::EMPTY );
1455 if (!TextToDate(rStr, aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper()))
1456 return;
1458 Date aTempDate = aDate;
1459 if ( aTempDate > GetMax() )
1460 aTempDate = GetMax();
1461 else if ( aTempDate < GetMin() )
1462 aTempDate = GetMin();
1464 rOutStr = ImplGetDateAsText( aTempDate );
1467 namespace
1469 ExtDateFieldFormat ResolveSystemFormat(ExtDateFieldFormat eDateFormat, const LocaleDataWrapper& rLocaleData)
1471 if (eDateFormat <= ExtDateFieldFormat::SystemShortYYYY)
1473 bool bShowCentury = (eDateFormat == ExtDateFieldFormat::SystemShortYYYY);
1474 switch (rLocaleData.getDateOrder())
1476 case DateOrder::DMY:
1477 eDateFormat = bShowCentury ? ExtDateFieldFormat::ShortDDMMYYYY : ExtDateFieldFormat::ShortDDMMYY;
1478 break;
1479 case DateOrder::MDY:
1480 eDateFormat = bShowCentury ? ExtDateFieldFormat::ShortMMDDYYYY : ExtDateFieldFormat::ShortMMDDYY;
1481 break;
1482 default:
1483 eDateFormat = bShowCentury ? ExtDateFieldFormat::ShortYYYYMMDD : ExtDateFieldFormat::ShortYYMMDD;
1486 return eDateFormat;
1490 OUString DateFormatter::FormatDate(const Date& rDate, ExtDateFieldFormat eExtFormat,
1491 const LocaleDataWrapper& rLocaleData,
1492 const Formatter::StaticFormatter& rStaticFormatter)
1494 bool bShowCentury = false;
1495 switch (eExtFormat)
1497 case ExtDateFieldFormat::SystemShortYYYY:
1498 case ExtDateFieldFormat::SystemLong:
1499 case ExtDateFieldFormat::ShortDDMMYYYY:
1500 case ExtDateFieldFormat::ShortMMDDYYYY:
1501 case ExtDateFieldFormat::ShortYYYYMMDD:
1502 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1504 bShowCentury = true;
1506 break;
1507 default:
1509 bShowCentury = false;
1513 if ( !bShowCentury )
1515 // Check if I have to use force showing the century
1516 sal_uInt16 nTwoDigitYearStart = officecfg::Office::Common::DateFormat::TwoDigitYear::get();
1517 sal_uInt16 nYear = rDate.GetYearUnsigned();
1519 // If year is not in double digit range
1520 if ( (nYear < nTwoDigitYearStart) || (nYear >= nTwoDigitYearStart+100) )
1521 bShowCentury = true;
1524 sal_Unicode aBuf[128];
1525 sal_Unicode* pBuf = aBuf;
1527 eExtFormat = ResolveSystemFormat(eExtFormat, rLocaleData);
1529 OUString aDateSep = ImplGetDateSep( rLocaleData, eExtFormat );
1530 sal_uInt16 nDay = rDate.GetDay();
1531 sal_uInt16 nMonth = rDate.GetMonth();
1532 sal_Int16 nYear = rDate.GetYear();
1533 sal_uInt16 nYearLen = bShowCentury ? 4 : 2;
1535 if ( !bShowCentury )
1536 nYear %= 100;
1538 switch (eExtFormat)
1540 case ExtDateFieldFormat::SystemLong:
1542 SvNumberFormatter* pFormatter = rStaticFormatter;
1543 const LanguageTag aFormatterLang( pFormatter->GetLanguageTag());
1544 const sal_uInt32 nIndex = pFormatter->GetFormatIndex( NF_DATE_SYSTEM_LONG,
1545 rLocaleData.getLanguageTag().getLanguageType(false));
1546 OUString aStr;
1547 const Color* pCol;
1548 pFormatter->GetOutputString( rDate - pFormatter->GetNullDate(), nIndex, aStr, &pCol);
1549 // Reset to what other uses may expect.
1550 pFormatter->ChangeIntl( aFormatterLang.getLanguageType(false));
1551 return aStr;
1553 case ExtDateFieldFormat::ShortDDMMYY:
1554 case ExtDateFieldFormat::ShortDDMMYYYY:
1556 pBuf = ImplAddNum( pBuf, nDay, 2 );
1557 pBuf = ImplAddString( pBuf, aDateSep );
1558 pBuf = ImplAddNum( pBuf, nMonth, 2 );
1559 pBuf = ImplAddString( pBuf, aDateSep );
1560 pBuf = ImplAddSNum( pBuf, nYear, nYearLen );
1562 break;
1563 case ExtDateFieldFormat::ShortMMDDYY:
1564 case ExtDateFieldFormat::ShortMMDDYYYY:
1566 pBuf = ImplAddNum( pBuf, nMonth, 2 );
1567 pBuf = ImplAddString( pBuf, aDateSep );
1568 pBuf = ImplAddNum( pBuf, nDay, 2 );
1569 pBuf = ImplAddString( pBuf, aDateSep );
1570 pBuf = ImplAddSNum( pBuf, nYear, nYearLen );
1572 break;
1573 case ExtDateFieldFormat::ShortYYMMDD:
1574 case ExtDateFieldFormat::ShortYYYYMMDD:
1575 case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
1576 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1578 pBuf = ImplAddSNum( pBuf, nYear, nYearLen );
1579 pBuf = ImplAddString( pBuf, aDateSep );
1580 pBuf = ImplAddNum( pBuf, nMonth, 2 );
1581 pBuf = ImplAddString( pBuf, aDateSep );
1582 pBuf = ImplAddNum( pBuf, nDay, 2 );
1584 break;
1585 default:
1587 OSL_FAIL( "DateOrder???" );
1591 return OUString(aBuf, pBuf-aBuf);
1594 OUString DateFormatter::ImplGetDateAsText( const Date& rDate ) const
1596 return DateFormatter::FormatDate(rDate, GetExtDateFormat(), ImplGetLocaleDataWrapper(), maStaticFormatter);
1599 static void ImplDateIncrementDay( Date& rDate, bool bUp )
1601 DateFormatter::ExpandCentury( rDate );
1602 rDate.AddDays( bUp ? 1 : -1 );
1605 static void ImplDateIncrementMonth( Date& rDate, bool bUp )
1607 DateFormatter::ExpandCentury( rDate );
1608 rDate.AddMonths( bUp ? 1 : -1 );
1611 static void ImplDateIncrementYear( Date& rDate, bool bUp )
1613 DateFormatter::ExpandCentury( rDate );
1614 rDate.AddYears( bUp ? 1 : -1 );
1617 bool DateFormatter::ImplAllowMalformedInput() const
1619 return !IsEnforceValidValue();
1622 int DateFormatter::GetDateArea(ExtDateFieldFormat& eFormat, std::u16string_view rText, int nCursor, const LocaleDataWrapper& rLocaleDataWrapper)
1624 sal_Int8 nDateArea = 0;
1626 if ( eFormat == ExtDateFieldFormat::SystemLong )
1628 eFormat = ImplGetExtFormat(rLocaleDataWrapper.getLongDateOrder());
1629 nDateArea = 1;
1631 else
1633 // search area
1634 size_t nPos = 0;
1635 OUString aDateSep = ImplGetDateSep(rLocaleDataWrapper, eFormat);
1636 for ( sal_Int8 i = 1; i <= 3; i++ )
1638 nPos = rText.find( aDateSep, nPos );
1639 if (nPos == std::u16string_view::npos || static_cast<sal_Int32>(nPos) >= nCursor)
1641 nDateArea = i;
1642 break;
1644 else
1645 nPos++;
1649 return nDateArea;
1652 void DateField::ImplDateSpinArea( bool bUp )
1654 // increment days if all is selected
1655 if ( !GetField() )
1656 return;
1658 Date aDate( GetDate() );
1659 Selection aSelection = GetField()->GetSelection();
1660 aSelection.Normalize();
1661 OUString aText( GetText() );
1662 if ( static_cast<sal_Int32>(aSelection.Len()) == aText.getLength() )
1663 ImplDateIncrementDay( aDate, bUp );
1664 else
1666 ExtDateFieldFormat eFormat = GetExtDateFormat( true );
1667 sal_Int8 nDateArea = GetDateArea(eFormat, aText, aSelection.Max(), ImplGetLocaleDataWrapper());
1669 switch( eFormat )
1671 case ExtDateFieldFormat::ShortMMDDYY:
1672 case ExtDateFieldFormat::ShortMMDDYYYY:
1673 switch( nDateArea )
1675 case 1: ImplDateIncrementMonth( aDate, bUp );
1676 break;
1677 case 2: ImplDateIncrementDay( aDate, bUp );
1678 break;
1679 case 3: ImplDateIncrementYear( aDate, bUp );
1680 break;
1682 break;
1683 case ExtDateFieldFormat::ShortDDMMYY:
1684 case ExtDateFieldFormat::ShortDDMMYYYY:
1685 switch( nDateArea )
1687 case 1: ImplDateIncrementDay( aDate, bUp );
1688 break;
1689 case 2: ImplDateIncrementMonth( aDate, bUp );
1690 break;
1691 case 3: ImplDateIncrementYear( aDate, bUp );
1692 break;
1694 break;
1695 case ExtDateFieldFormat::ShortYYMMDD:
1696 case ExtDateFieldFormat::ShortYYYYMMDD:
1697 case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
1698 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1699 switch( nDateArea )
1701 case 1: ImplDateIncrementYear( aDate, bUp );
1702 break;
1703 case 2: ImplDateIncrementMonth( aDate, bUp );
1704 break;
1705 case 3: ImplDateIncrementDay( aDate, bUp );
1706 break;
1708 break;
1709 default:
1710 OSL_FAIL( "invalid conversion" );
1711 break;
1715 ImplNewFieldValue( aDate );
1718 DateFormatter::DateFormatter(Edit* pEdit)
1719 : FormatterBase(pEdit)
1720 , maFieldDate(0)
1721 , maLastDate(0)
1722 , maMin(1, 1, 1900)
1723 , maMax(31, 12, 2200)
1724 , mbLongFormat(false)
1725 , mbShowDateCentury(true)
1726 , mnExtDateFormat(ExtDateFieldFormat::SystemShort)
1727 , mbEnforceValidValue(true)
1731 DateFormatter::~DateFormatter()
1735 CalendarWrapper& DateFormatter::GetCalendarWrapper() const
1737 if (!mxCalendarWrapper)
1739 const_cast<DateFormatter*>(this)->mxCalendarWrapper.reset( new CalendarWrapper( comphelper::getProcessComponentContext() ) );
1740 mxCalendarWrapper->loadDefaultCalendar( GetLocale() );
1743 return *mxCalendarWrapper;
1746 void DateFormatter::SetExtDateFormat( ExtDateFieldFormat eFormat )
1748 mnExtDateFormat = eFormat;
1749 ReformatAll();
1752 ExtDateFieldFormat DateFormatter::GetExtDateFormat( bool bResolveSystemFormat ) const
1754 ExtDateFieldFormat eDateFormat = mnExtDateFormat;
1756 if (bResolveSystemFormat)
1757 eDateFormat = ResolveSystemFormat(eDateFormat, ImplGetLocaleDataWrapper());
1759 return eDateFormat;
1762 void DateFormatter::ReformatAll()
1764 Reformat();
1767 void DateFormatter::SetMin( const Date& rNewMin )
1769 maMin = rNewMin;
1770 if ( !IsEmptyFieldValue() )
1771 ReformatAll();
1774 void DateFormatter::SetMax( const Date& rNewMax )
1776 maMax = rNewMax;
1777 if ( !IsEmptyFieldValue() )
1778 ReformatAll();
1781 void DateFormatter::SetLongFormat( bool bLong )
1783 mbLongFormat = bLong;
1785 // #91913# Remove LongFormat and DateShowCentury - redundant
1786 if ( bLong )
1788 SetExtDateFormat( ExtDateFieldFormat::SystemLong );
1790 else
1792 if( mnExtDateFormat == ExtDateFieldFormat::SystemLong )
1793 SetExtDateFormat( ExtDateFieldFormat::SystemShort );
1796 ReformatAll();
1799 namespace
1801 ExtDateFieldFormat ChangeDateCentury(ExtDateFieldFormat eExtDateFormat, bool bShowDateCentury)
1803 // #91913# Remove LongFormat and DateShowCentury - redundant
1804 if (bShowDateCentury)
1806 switch (eExtDateFormat)
1808 case ExtDateFieldFormat::SystemShort:
1809 case ExtDateFieldFormat::SystemShortYY:
1810 eExtDateFormat = ExtDateFieldFormat::SystemShortYYYY; break;
1811 case ExtDateFieldFormat::ShortDDMMYY:
1812 eExtDateFormat = ExtDateFieldFormat::ShortDDMMYYYY; break;
1813 case ExtDateFieldFormat::ShortMMDDYY:
1814 eExtDateFormat = ExtDateFieldFormat::ShortMMDDYYYY; break;
1815 case ExtDateFieldFormat::ShortYYMMDD:
1816 eExtDateFormat = ExtDateFieldFormat::ShortYYYYMMDD; break;
1817 case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
1818 eExtDateFormat = ExtDateFieldFormat::ShortYYYYMMDD_DIN5008; break;
1819 default:
1823 else
1825 switch (eExtDateFormat)
1827 case ExtDateFieldFormat::SystemShort:
1828 case ExtDateFieldFormat::SystemShortYYYY:
1829 eExtDateFormat = ExtDateFieldFormat::SystemShortYY; break;
1830 case ExtDateFieldFormat::ShortDDMMYYYY:
1831 eExtDateFormat = ExtDateFieldFormat::ShortDDMMYY; break;
1832 case ExtDateFieldFormat::ShortMMDDYYYY:
1833 eExtDateFormat = ExtDateFieldFormat::ShortMMDDYY; break;
1834 case ExtDateFieldFormat::ShortYYYYMMDD:
1835 eExtDateFormat = ExtDateFieldFormat::ShortYYMMDD; break;
1836 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1837 eExtDateFormat = ExtDateFieldFormat::ShortYYMMDD_DIN5008; break;
1838 default:
1843 return eExtDateFormat;
1847 void DateFormatter::SetShowDateCentury( bool bShowDateCentury )
1849 mbShowDateCentury = bShowDateCentury;
1851 SetExtDateFormat(ChangeDateCentury(GetExtDateFormat(), bShowDateCentury));
1853 ReformatAll();
1856 void DateFormatter::SetDate( const Date& rNewDate )
1858 ImplSetUserDate( rNewDate );
1859 maFieldDate = maLastDate;
1860 maLastDate = GetDate();
1863 void DateFormatter::ImplSetUserDate( const Date& rNewDate, Selection const * pNewSelection )
1865 Date aNewDate = rNewDate;
1866 if ( aNewDate > maMax )
1867 aNewDate = maMax;
1868 else if ( aNewDate < maMin )
1869 aNewDate = maMin;
1870 maLastDate = aNewDate;
1872 if ( GetField() )
1873 ImplSetText( ImplGetDateAsText( aNewDate ), pNewSelection );
1876 void DateFormatter::ImplNewFieldValue( const Date& rDate )
1878 if ( !GetField() )
1879 return;
1881 Selection aSelection = GetField()->GetSelection();
1882 aSelection.Normalize();
1883 OUString aText = GetField()->GetText();
1885 // If selected until the end then keep it that way
1886 if ( static_cast<sal_Int32>(aSelection.Max()) == aText.getLength() )
1888 if ( !aSelection.Len() )
1889 aSelection.Min() = SELECTION_MAX;
1890 aSelection.Max() = SELECTION_MAX;
1893 Date aOldLastDate = maLastDate;
1894 ImplSetUserDate( rDate, &aSelection );
1895 maLastDate = aOldLastDate;
1897 // Modify at Edit is only set at KeyInput
1898 if ( GetField()->GetText() != aText )
1900 GetField()->SetModifyFlag();
1901 GetField()->Modify();
1905 Date DateFormatter::GetDate() const
1907 Date aDate( Date::EMPTY );
1909 if ( GetField() )
1911 if (TextToDate(GetField()->GetText(), aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper()))
1913 if ( aDate > maMax )
1914 aDate = maMax;
1915 else if ( aDate < maMin )
1916 aDate = maMin;
1918 else
1920 // !!! We should find out why dates are treated differently than other fields (see
1921 // also bug: 52384)
1923 if ( !ImplAllowMalformedInput() )
1925 if ( maLastDate.GetDate() )
1926 aDate = maLastDate;
1927 else if ( !IsEmptyFieldValueEnabled() )
1928 aDate = Date( Date::SYSTEM );
1930 else
1931 aDate = Date( Date::EMPTY ); // set invalid date
1935 return aDate;
1938 void DateFormatter::SetEmptyDate()
1940 FormatterBase::SetEmptyFieldValue();
1943 bool DateFormatter::IsEmptyDate() const
1945 bool bEmpty = FormatterBase::IsEmptyFieldValue();
1947 if ( GetField() && MustBeReformatted() && IsEmptyFieldValueEnabled() )
1949 if ( GetField()->GetText().isEmpty() )
1951 bEmpty = true;
1953 else if ( !maLastDate.GetDate() )
1955 Date aDate( Date::EMPTY );
1956 bEmpty = !TextToDate(GetField()->GetText(), aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper());
1959 return bEmpty;
1962 void DateFormatter::Reformat()
1964 if ( !GetField() )
1965 return;
1967 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
1968 return;
1970 OUString aStr;
1971 ImplDateReformat( GetField()->GetText(), aStr );
1973 if ( !aStr.isEmpty() )
1975 ImplSetText( aStr );
1976 (void)TextToDate(aStr, maLastDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper());
1978 else
1980 if ( maLastDate.GetDate() )
1981 SetDate( maLastDate );
1982 else if ( !IsEmptyFieldValueEnabled() )
1983 SetDate( Date( Date::SYSTEM ) );
1984 else
1986 ImplSetText( OUString() );
1987 SetEmptyFieldValueData( true );
1992 void DateFormatter::ExpandCentury( Date& rDate )
1994 ExpandCentury(rDate, officecfg::Office::Common::DateFormat::TwoDigitYear::get());
1997 void DateFormatter::ExpandCentury( Date& rDate, sal_uInt16 nTwoDigitYearStart )
1999 sal_Int16 nDateYear = rDate.GetYear();
2000 if ( 0 <= nDateYear && nDateYear < 100 )
2002 sal_uInt16 nCentury = nTwoDigitYearStart / 100;
2003 if ( nDateYear < (nTwoDigitYearStart % 100) )
2004 nCentury++;
2005 rDate.SetYear( nDateYear + (nCentury*100) );
2009 DateField::DateField( vcl::Window* pParent, WinBits nWinStyle ) :
2010 SpinField( pParent, nWinStyle ),
2011 DateFormatter(this),
2012 maFirst( GetMin() ),
2013 maLast( GetMax() )
2015 SetText( ImplGetLocaleDataWrapper().getDate( ImplGetFieldDate() ) );
2016 Reformat();
2017 ResetLastDate();
2020 void DateField::dispose()
2022 ClearField();
2023 SpinField::dispose();
2026 bool DateField::PreNotify( NotifyEvent& rNEvt )
2028 if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && IsStrictFormat() &&
2029 ( GetExtDateFormat() != ExtDateFieldFormat::SystemLong ) &&
2030 !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
2032 if ( ImplDateProcessKeyInput( *rNEvt.GetKeyEvent(), GetExtDateFormat( true ), ImplGetLocaleDataWrapper() ) )
2033 return true;
2036 return SpinField::PreNotify( rNEvt );
2039 bool DateField::EventNotify( NotifyEvent& rNEvt )
2041 if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
2042 MarkToBeReformatted( false );
2043 else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
2045 if ( MustBeReformatted() )
2047 // !!! We should find out why dates are treated differently than other fields (see
2048 // also bug: 52384)
2050 bool bTextLen = !GetText().isEmpty();
2051 if ( bTextLen || !IsEmptyFieldValueEnabled() )
2053 if ( !ImplAllowMalformedInput() )
2054 Reformat();
2055 else
2057 Date aDate( 0, 0, 0 );
2058 if (TextToDate(GetText(), aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper()))
2059 // even with strict text analysis, our text is a valid date -> do a complete
2060 // reformat
2061 Reformat();
2064 else
2066 ResetLastDate();
2067 SetEmptyFieldValueData( true );
2072 return SpinField::EventNotify( rNEvt );
2075 void DateField::DataChanged( const DataChangedEvent& rDCEvt )
2077 SpinField::DataChanged( rDCEvt );
2079 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & (AllSettingsFlags::LOCALE|AllSettingsFlags::MISC)) )
2081 if (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE)
2082 ImplResetLocaleDataWrapper();
2083 ReformatAll();
2087 void DateField::Modify()
2089 MarkToBeReformatted( true );
2090 SpinField::Modify();
2093 void DateField::Up()
2095 ImplDateSpinArea( true );
2096 SpinField::Up();
2099 void DateField::Down()
2101 ImplDateSpinArea( false );
2102 SpinField::Down();
2105 void DateField::First()
2107 ImplNewFieldValue( maFirst );
2108 SpinField::First();
2111 void DateField::Last()
2113 ImplNewFieldValue( maLast );
2114 SpinField::Last();
2117 DateBox::DateBox(vcl::Window* pParent, WinBits nWinStyle)
2118 : ComboBox( pParent, nWinStyle )
2119 , DateFormatter(this)
2121 SetText( ImplGetLocaleDataWrapper().getDate( ImplGetFieldDate() ) );
2122 Reformat();
2125 void DateBox::dispose()
2127 ClearField();
2128 ComboBox::dispose();
2131 bool DateBox::PreNotify( NotifyEvent& rNEvt )
2133 if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && IsStrictFormat() &&
2134 ( GetExtDateFormat() != ExtDateFieldFormat::SystemLong ) &&
2135 !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
2137 if ( ImplDateProcessKeyInput( *rNEvt.GetKeyEvent(), GetExtDateFormat( true ), ImplGetLocaleDataWrapper() ) )
2138 return true;
2141 return ComboBox::PreNotify( rNEvt );
2144 void DateBox::DataChanged( const DataChangedEvent& rDCEvt )
2146 ComboBox::DataChanged( rDCEvt );
2148 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
2150 ImplResetLocaleDataWrapper();
2151 ReformatAll();
2155 bool DateBox::EventNotify( NotifyEvent& rNEvt )
2157 if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
2158 MarkToBeReformatted( false );
2159 else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
2161 if ( MustBeReformatted() )
2163 bool bTextLen = !GetText().isEmpty();
2164 if ( bTextLen || !IsEmptyFieldValueEnabled() )
2165 Reformat();
2166 else
2168 ResetLastDate();
2169 SetEmptyFieldValueData( true );
2174 return ComboBox::EventNotify( rNEvt );
2177 void DateBox::Modify()
2179 MarkToBeReformatted( true );
2180 ComboBox::Modify();
2183 void DateBox::ReformatAll()
2185 OUString aStr;
2186 SetUpdateMode( false );
2187 const sal_Int32 nEntryCount = GetEntryCount();
2188 for ( sal_Int32 i=0; i < nEntryCount; ++i )
2190 ImplDateReformat( GetEntry( i ), aStr );
2191 RemoveEntryAt(i);
2192 InsertEntry( aStr, i );
2194 DateFormatter::Reformat();
2195 SetUpdateMode( true );
2198 namespace weld
2200 CalendarWrapper& DateFormatter::GetCalendarWrapper() const
2202 if (!m_xCalendarWrapper)
2204 m_xCalendarWrapper.reset(new CalendarWrapper(comphelper::getProcessComponentContext()));
2205 m_xCalendarWrapper->loadDefaultCalendar(Application::GetSettings().GetLanguageTag().getLocale());
2207 return *m_xCalendarWrapper;
2210 void DateFormatter::SetShowDateCentury(bool bShowDateCentury)
2212 m_eFormat = ChangeDateCentury(m_eFormat, bShowDateCentury);
2214 ReFormat();
2217 void DateFormatter::SetDate(const Date& rDate)
2219 auto nDate = rDate.GetDate();
2220 bool bForceOutput = GetEntryText().isEmpty() && rDate == GetDate();
2221 if (bForceOutput)
2223 ImplSetValue(nDate, true);
2224 return;
2226 SetValue(nDate);
2229 Date DateFormatter::GetDate()
2231 return Date(GetValue());
2234 void DateFormatter::SetMin(const Date& rNewMin)
2236 SetMinValue(rNewMin.GetDate());
2239 void DateFormatter::SetMax(const Date& rNewMax)
2241 SetMaxValue(rNewMax.GetDate());
2244 OUString DateFormatter::FormatNumber(int nValue) const
2246 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
2247 return ::DateFormatter::FormatDate(Date(nValue), m_eFormat, rLocaleData, m_aStaticFormatter);
2250 IMPL_LINK_NOARG(DateFormatter, FormatOutputHdl, LinkParamNone*, bool)
2252 OUString sText = FormatNumber(GetValue());
2253 ImplSetTextImpl(sText, nullptr);
2254 return true;
2257 IMPL_LINK(DateFormatter, ParseInputHdl, sal_Int64*, result, TriState)
2259 const LocaleDataWrapper& rLocaleDataWrapper = Application::GetSettings().GetLocaleDataWrapper();
2261 Date aResult(Date::EMPTY);
2262 bool bRet = ::DateFormatter::TextToDate(GetEntryText(), aResult, ResolveSystemFormat(m_eFormat, rLocaleDataWrapper),
2263 rLocaleDataWrapper, GetCalendarWrapper());
2264 if (bRet)
2265 *result = aResult.GetDate();
2267 return bRet ? TRISTATE_TRUE : TRISTATE_FALSE;
2271 static bool ImplTimeProcessKeyInput( const KeyEvent& rKEvt,
2272 bool bStrictFormat, bool bDuration,
2273 TimeFieldFormat eFormat,
2274 const LocaleDataWrapper& rLocaleDataWrapper )
2276 sal_Unicode cChar = rKEvt.GetCharCode();
2278 if ( !bStrictFormat )
2279 return false;
2280 else
2282 sal_uInt16 nGroup = rKEvt.GetKeyCode().GetGroup();
2283 if ( (nGroup == KEYGROUP_FKEYS) || (nGroup == KEYGROUP_CURSOR) ||
2284 (nGroup == KEYGROUP_MISC) ||
2285 ((cChar >= '0') && (cChar <= '9')) ||
2286 rLocaleDataWrapper.getTimeSep() == OUStringChar(cChar) ||
2287 (rLocaleDataWrapper.getTimeAM().indexOf(cChar) != -1) ||
2288 (rLocaleDataWrapper.getTimePM().indexOf(cChar) != -1) ||
2289 // Accept AM/PM:
2290 (cChar == 'a') || (cChar == 'A') || (cChar == 'm') || (cChar == 'M') || (cChar == 'p') || (cChar == 'P') ||
2291 ((eFormat == TimeFieldFormat::F_SEC_CS) && rLocaleDataWrapper.getTime100SecSep() == OUStringChar(cChar)) ||
2292 (bDuration && (cChar == '-')) )
2293 return false;
2294 else
2295 return true;
2299 static bool ImplIsOnlyDigits( const OUString& _rStr )
2301 const sal_Unicode* _pChr = _rStr.getStr();
2302 for ( sal_Int32 i = 0; i < _rStr.getLength(); ++i, ++_pChr )
2304 if ( *_pChr < '0' || *_pChr > '9' )
2305 return false;
2307 return true;
2310 static bool ImplIsValidTimePortion( bool _bSkipInvalidCharacters, const OUString& _rStr )
2312 if ( !_bSkipInvalidCharacters )
2314 if ( ( _rStr.getLength() > 2 ) || _rStr.isEmpty() || !ImplIsOnlyDigits( _rStr ) )
2315 return false;
2317 return true;
2320 static bool ImplCutTimePortion( OUStringBuffer& _rStr, sal_Int32 _nSepPos, bool _bSkipInvalidCharacters, short* _pPortion )
2322 OUString sPortion(_rStr.subView(0, _nSepPos));
2324 if (_nSepPos < _rStr.getLength())
2325 _rStr.remove(0, _nSepPos + 1);
2326 else
2327 _rStr.truncate();
2329 if ( !ImplIsValidTimePortion( _bSkipInvalidCharacters, sPortion ) )
2330 return false;
2331 *_pPortion = static_cast<short>(sPortion.toInt32());
2332 return true;
2335 bool TimeFormatter::TextToTime(std::u16string_view rStr, tools::Time& rTime,
2336 TimeFieldFormat eFormat,
2337 bool bDuration, const LocaleDataWrapper& rLocaleDataWrapper, bool _bSkipInvalidCharacters)
2339 OUStringBuffer aStr(rStr);
2340 short nHour = 0;
2341 short nMinute = 0;
2342 short nSecond = 0;
2343 sal_Int64 nNanoSec = 0;
2344 tools::Time aTime( 0, 0, 0 );
2346 if ( rStr.empty() )
2347 return false;
2349 // Search for separators
2350 if (!rLocaleDataWrapper.getTimeSep().isEmpty())
2352 OUStringBuffer aSepStr(",.;:/");
2353 if ( !bDuration )
2354 aSepStr.append('-');
2356 // Replace characters above by the separator character
2357 for (sal_Int32 i = 0; i < aSepStr.getLength(); ++i)
2359 if (rLocaleDataWrapper.getTimeSep() == OUStringChar(aSepStr[i]))
2360 continue;
2361 for ( sal_Int32 j = 0; j < aStr.getLength(); j++ )
2363 if (aStr[j] == aSepStr[i])
2364 aStr[j] = rLocaleDataWrapper.getTimeSep()[0];
2369 bool bNegative = false;
2370 sal_Int32 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2371 if ( aStr[0] == '-' )
2372 bNegative = true;
2373 if ( eFormat != TimeFieldFormat::F_SEC_CS )
2375 if ( nSepPos < 0 )
2376 nSepPos = aStr.getLength();
2377 if ( !ImplCutTimePortion( aStr, nSepPos, _bSkipInvalidCharacters, &nHour ) )
2378 return false;
2380 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2381 if ( !aStr.isEmpty() && aStr[0] == '-' )
2382 bNegative = true;
2383 if ( nSepPos >= 0 )
2385 if ( !ImplCutTimePortion( aStr, nSepPos, _bSkipInvalidCharacters, &nMinute ) )
2386 return false;
2388 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2389 if ( !aStr.isEmpty() && aStr[0] == '-' )
2390 bNegative = true;
2391 if ( nSepPos >= 0 )
2393 if ( !ImplCutTimePortion( aStr, nSepPos, _bSkipInvalidCharacters, &nSecond ) )
2394 return false;
2395 if ( !aStr.isEmpty() && aStr[0] == '-' )
2396 bNegative = true;
2397 nNanoSec = o3tl::toInt64(aStr);
2399 else
2400 nSecond = static_cast<short>(o3tl::toInt32(aStr));
2402 else
2403 nMinute = static_cast<short>(o3tl::toInt32(aStr));
2405 else if ( nSepPos < 0 )
2407 nSecond = static_cast<short>(o3tl::toInt32(aStr));
2408 nMinute += nSecond / 60;
2409 nSecond %= 60;
2410 nHour += nMinute / 60;
2411 nMinute %= 60;
2413 else
2415 nSecond = static_cast<short>(o3tl::toInt32(aStr.subView( 0, nSepPos )));
2416 aStr.remove( 0, nSepPos+1 );
2418 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2419 if ( !aStr.isEmpty() && aStr[0] == '-' )
2420 bNegative = true;
2421 if ( nSepPos >= 0 )
2423 nMinute = nSecond;
2424 nSecond = static_cast<short>(o3tl::toInt32(aStr.subView( 0, nSepPos )));
2425 aStr.remove( 0, nSepPos+1 );
2427 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2428 if ( !aStr.isEmpty() && aStr[0] == '-' )
2429 bNegative = true;
2430 if ( nSepPos >= 0 )
2432 nHour = nMinute;
2433 nMinute = nSecond;
2434 nSecond = static_cast<short>(o3tl::toInt32(aStr.subView( 0, nSepPos )));
2435 aStr.remove( 0, nSepPos+1 );
2437 else
2439 nHour += nMinute / 60;
2440 nMinute %= 60;
2443 else
2445 nMinute += nSecond / 60;
2446 nSecond %= 60;
2447 nHour += nMinute / 60;
2448 nMinute %= 60;
2450 nNanoSec = o3tl::toInt64(aStr);
2453 if ( nNanoSec )
2455 assert(aStr.getLength() >= 1);
2457 sal_Int32 nLen = 1; // at least one digit, otherwise nNanoSec==0
2459 while ( aStr.getLength() > nLen && aStr[nLen] >= '0' && aStr[nLen] <= '9' )
2460 nLen++;
2462 while ( nLen < 9)
2464 nNanoSec *= 10;
2465 ++nLen;
2467 while ( nLen > 9 )
2469 // round if negative?
2470 nNanoSec = (nNanoSec + 5) / 10;
2471 --nLen;
2475 assert(nNanoSec > -1000000000 && nNanoSec < 1000000000);
2476 if ( (nMinute > 59) || (nSecond > 59) || (nNanoSec > 1000000000) )
2477 return false;
2479 if ( eFormat == TimeFieldFormat::F_NONE )
2480 nSecond = nNanoSec = 0;
2481 else if ( eFormat == TimeFieldFormat::F_SEC )
2482 nNanoSec = 0;
2484 if ( !bDuration )
2486 if ( bNegative || (nHour < 0) || (nMinute < 0) ||
2487 (nSecond < 0) || (nNanoSec < 0) )
2488 return false;
2490 OUString aUpperCaseStr = aStr.toString().toAsciiUpperCase();
2491 OUString aAMlocalised(rLocaleDataWrapper.getTimeAM().toAsciiUpperCase());
2492 OUString aPMlocalised(rLocaleDataWrapper.getTimePM().toAsciiUpperCase());
2494 if ( (nHour < 12) && ( ( aUpperCaseStr.indexOf( "PM" ) >= 0 ) || ( aUpperCaseStr.indexOf( aPMlocalised ) >= 0 ) ) )
2495 nHour += 12;
2497 if ( (nHour == 12) && ( ( aUpperCaseStr.indexOf( "AM" ) >= 0 ) || ( aUpperCaseStr.indexOf( aAMlocalised ) >= 0 ) ) )
2498 nHour = 0;
2500 aTime = tools::Time( static_cast<sal_uInt16>(nHour), static_cast<sal_uInt16>(nMinute), static_cast<sal_uInt16>(nSecond),
2501 static_cast<sal_uInt32>(nNanoSec) );
2503 else
2505 assert( !bNegative || (nHour < 0) || (nMinute < 0) ||
2506 (nSecond < 0) || (nNanoSec < 0) );
2507 if ( bNegative || (nHour < 0) || (nMinute < 0) ||
2508 (nSecond < 0) || (nNanoSec < 0) )
2510 // LEM TODO: this looks weird... I think buggy when parsing "05:-02:18"
2511 bNegative = true;
2512 nHour = nHour < 0 ? -nHour : nHour;
2513 nMinute = nMinute < 0 ? -nMinute : nMinute;
2514 nSecond = nSecond < 0 ? -nSecond : nSecond;
2515 nNanoSec = nNanoSec < 0 ? -nNanoSec : nNanoSec;
2518 aTime = tools::Time( static_cast<sal_uInt16>(nHour), static_cast<sal_uInt16>(nMinute), static_cast<sal_uInt16>(nSecond),
2519 static_cast<sal_uInt32>(nNanoSec) );
2520 if ( bNegative )
2521 aTime = -aTime;
2524 rTime = aTime;
2526 return true;
2529 void TimeFormatter::ImplTimeReformat( std::u16string_view rStr, OUString& rOutStr )
2531 tools::Time aTime( 0, 0, 0 );
2532 if ( !TextToTime( rStr, aTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper() ) )
2533 return;
2535 tools::Time aTempTime = aTime;
2536 if ( aTempTime > GetMax() )
2537 aTempTime = GetMax() ;
2538 else if ( aTempTime < GetMin() )
2539 aTempTime = GetMin();
2541 bool bSecond = false;
2542 bool b100Sec = false;
2543 if ( meFormat != TimeFieldFormat::F_NONE )
2544 bSecond = true;
2546 if ( meFormat == TimeFieldFormat::F_SEC_CS )
2548 sal_uLong n = aTempTime.GetHour() * 3600L;
2549 n += aTempTime.GetMin() * 60L;
2550 n += aTempTime.GetSec();
2551 rOutStr = OUString::number( n );
2552 rOutStr += ImplGetLocaleDataWrapper().getTime100SecSep();
2553 std::ostringstream ostr;
2554 ostr.fill('0');
2555 ostr.width(9);
2556 ostr << aTempTime.GetNanoSec();
2557 rOutStr += OUString::createFromAscii(ostr.str());
2559 else if ( mbDuration )
2561 tools::Duration aDuration( 0, aTempTime);
2562 rOutStr = ImplGetLocaleDataWrapper().getDuration( aDuration, bSecond, b100Sec );
2564 else
2566 rOutStr = ImplGetLocaleDataWrapper().getTime( aTempTime, bSecond, b100Sec );
2567 if ( GetTimeFormat() == TimeFormat::Hour12 )
2569 if ( aTempTime.GetHour() > 12 )
2571 tools::Time aT( aTempTime );
2572 aT.SetHour( aT.GetHour() % 12 );
2573 rOutStr = ImplGetLocaleDataWrapper().getTime( aT, bSecond, b100Sec );
2575 // Don't use LocaleDataWrapper, we want AM/PM
2576 if ( aTempTime.GetHour() < 12 )
2577 rOutStr += "AM"; // ImplGetLocaleDataWrapper().getTimeAM();
2578 else
2579 rOutStr += "PM"; // ImplGetLocaleDataWrapper().getTimePM();
2584 bool TimeFormatter::ImplAllowMalformedInput() const
2586 return !IsEnforceValidValue();
2589 int TimeFormatter::GetTimeArea(TimeFieldFormat eFormat, std::u16string_view rText, int nCursor,
2590 const LocaleDataWrapper& rLocaleDataWrapper)
2592 int nTimeArea = 0;
2594 // Area search
2595 if (eFormat != TimeFieldFormat::F_SEC_CS)
2597 //Which area is the cursor in of HH:MM:SS.TT
2598 for ( size_t i = 1, nPos = 0; i <= 4; i++ )
2600 size_t nPos1 = rText.find(rLocaleDataWrapper.getTimeSep(), nPos);
2601 size_t nPos2 = rText.find(rLocaleDataWrapper.getTime100SecSep(), nPos);
2602 //which ever comes first, bearing in mind that one might not be there
2603 if (nPos1 != std::u16string_view::npos && nPos2 != std::u16string_view::npos)
2604 nPos = std::min(nPos1, nPos2);
2605 else if (nPos1 != std::u16string_view::npos)
2606 nPos = nPos1;
2607 else
2608 nPos = nPos2;
2609 if (nPos == std::u16string_view::npos || static_cast<sal_Int32>(nPos) >= nCursor)
2611 nTimeArea = i;
2612 break;
2614 else
2615 nPos++;
2618 else
2620 size_t nPos = rText.find(rLocaleDataWrapper.getTime100SecSep());
2621 if (nPos == std::u16string_view::npos || static_cast<sal_Int32>(nPos) >= nCursor)
2622 nTimeArea = 3;
2623 else
2624 nTimeArea = 4;
2627 return nTimeArea;
2630 tools::Time TimeFormatter::SpinTime(bool bUp, const tools::Time& rTime, TimeFieldFormat eFormat,
2631 bool bDuration, std::u16string_view rText, int nCursor,
2632 const LocaleDataWrapper& rLocaleDataWrapper)
2634 tools::Time aTime(rTime);
2636 int nTimeArea = GetTimeArea(eFormat, rText, nCursor, rLocaleDataWrapper);
2638 if ( nTimeArea )
2640 tools::Time aAddTime( 0, 0, 0 );
2641 if ( nTimeArea == 1 )
2642 aAddTime = tools::Time( 1, 0 );
2643 else if ( nTimeArea == 2 )
2644 aAddTime = tools::Time( 0, 1 );
2645 else if ( nTimeArea == 3 )
2646 aAddTime = tools::Time( 0, 0, 1 );
2647 else if ( nTimeArea == 4 )
2648 aAddTime = tools::Time( 0, 0, 0, 1 );
2650 if ( !bUp )
2651 aAddTime = -aAddTime;
2653 aTime += aAddTime;
2654 if (!bDuration)
2656 tools::Time aAbsMaxTime( 23, 59, 59, 999999999 );
2657 if ( aTime > aAbsMaxTime )
2658 aTime = aAbsMaxTime;
2659 tools::Time aAbsMinTime( 0, 0 );
2660 if ( aTime < aAbsMinTime )
2661 aTime = aAbsMinTime;
2665 return aTime;
2668 void TimeField::ImplTimeSpinArea( bool bUp )
2670 if ( GetField() )
2672 tools::Time aTime( GetTime() );
2673 OUString aText( GetText() );
2674 Selection aSelection( GetField()->GetSelection() );
2676 aTime = TimeFormatter::SpinTime(bUp, aTime, GetFormat(), IsDuration(), aText, aSelection.Max(), ImplGetLocaleDataWrapper());
2678 ImplNewFieldValue( aTime );
2682 TimeFormatter::TimeFormatter(Edit* pEdit)
2683 : FormatterBase(pEdit)
2684 , maLastTime(0, 0)
2685 , maMin(0, 0)
2686 , maMax(23, 59, 59, 999999999)
2687 , meFormat(TimeFieldFormat::F_NONE)
2688 , mnTimeFormat(TimeFormat::Hour24) // Should become an ExtTimeFieldFormat in next implementation, merge with mbDuration and meFormat
2689 , mbDuration(false)
2690 , mbEnforceValidValue(true)
2691 , maFieldTime(0, 0)
2695 TimeFormatter::~TimeFormatter()
2699 void TimeFormatter::ReformatAll()
2701 Reformat();
2704 void TimeFormatter::SetMin( const tools::Time& rNewMin )
2706 maMin = rNewMin;
2707 if ( !IsEmptyFieldValue() )
2708 ReformatAll();
2711 void TimeFormatter::SetMax( const tools::Time& rNewMax )
2713 maMax = rNewMax;
2714 if ( !IsEmptyFieldValue() )
2715 ReformatAll();
2718 void TimeFormatter::SetTimeFormat( TimeFormat eNewFormat )
2720 mnTimeFormat = eNewFormat;
2724 void TimeFormatter::SetFormat( TimeFieldFormat eNewFormat )
2726 meFormat = eNewFormat;
2727 ReformatAll();
2730 void TimeFormatter::SetDuration( bool bNewDuration )
2732 mbDuration = bNewDuration;
2733 ReformatAll();
2736 void TimeFormatter::SetTime( const tools::Time& rNewTime )
2738 SetUserTime( rNewTime );
2739 maFieldTime = maLastTime;
2740 SetEmptyFieldValueData( false );
2743 void TimeFormatter::ImplNewFieldValue( const tools::Time& rTime )
2745 if ( !GetField() )
2746 return;
2748 Selection aSelection = GetField()->GetSelection();
2749 aSelection.Normalize();
2750 OUString aText = GetField()->GetText();
2752 // If selected until the end then keep it that way
2753 if ( static_cast<sal_Int32>(aSelection.Max()) == aText.getLength() )
2755 if ( !aSelection.Len() )
2756 aSelection.Min() = SELECTION_MAX;
2757 aSelection.Max() = SELECTION_MAX;
2760 tools::Time aOldLastTime = maLastTime;
2761 ImplSetUserTime( rTime, &aSelection );
2762 maLastTime = aOldLastTime;
2764 // Modify at Edit is only set at KeyInput
2765 if ( GetField()->GetText() != aText )
2767 GetField()->SetModifyFlag();
2768 GetField()->Modify();
2772 OUString TimeFormatter::FormatTime(const tools::Time& rNewTime, TimeFieldFormat eFormat, TimeFormat eHourFormat, bool bDuration, const LocaleDataWrapper& rLocaleData)
2774 OUString aStr;
2775 bool bSec = false;
2776 bool b100Sec = false;
2777 if ( eFormat != TimeFieldFormat::F_NONE )
2778 bSec = true;
2779 if ( eFormat == TimeFieldFormat::F_SEC_CS )
2780 b100Sec = true;
2781 if ( eFormat == TimeFieldFormat::F_SEC_CS )
2783 sal_uLong n = rNewTime.GetHour() * 3600L;
2784 n += rNewTime.GetMin() * 60L;
2785 n += rNewTime.GetSec();
2786 aStr = OUString::number( n ) + rLocaleData.getTime100SecSep();
2787 std::ostringstream ostr;
2788 ostr.fill('0');
2789 ostr.width(9);
2790 ostr << rNewTime.GetNanoSec();
2791 aStr += OUString::createFromAscii(ostr.str());
2793 else if ( bDuration )
2795 tools::Duration aDuration( 0, rNewTime);
2796 aStr = rLocaleData.getDuration( aDuration, bSec, b100Sec );
2798 else
2800 aStr = rLocaleData.getTime( rNewTime, bSec, b100Sec );
2801 if ( eHourFormat == TimeFormat::Hour12 )
2803 if ( rNewTime.GetHour() > 12 )
2805 tools::Time aT( rNewTime );
2806 aT.SetHour( aT.GetHour() % 12 );
2807 aStr = rLocaleData.getTime( aT, bSec, b100Sec );
2809 // Don't use LocaleDataWrapper, we want AM/PM
2810 if ( rNewTime.GetHour() < 12 )
2811 aStr += "AM"; // rLocaleData.getTimeAM();
2812 else
2813 aStr += "PM"; // rLocaleData.getTimePM();
2817 return aStr;
2820 void TimeFormatter::ImplSetUserTime( const tools::Time& rNewTime, Selection const * pNewSelection )
2822 tools::Time aNewTime = rNewTime;
2823 if ( aNewTime > GetMax() )
2824 aNewTime = GetMax();
2825 else if ( aNewTime < GetMin() )
2826 aNewTime = GetMin();
2827 maLastTime = aNewTime;
2829 if ( GetField() )
2831 OUString aStr = TimeFormatter::FormatTime(aNewTime, meFormat, GetTimeFormat(), mbDuration, ImplGetLocaleDataWrapper());
2832 ImplSetText( aStr, pNewSelection );
2836 void TimeFormatter::SetUserTime( const tools::Time& rNewTime )
2838 ImplSetUserTime( rNewTime );
2841 tools::Time TimeFormatter::GetTime() const
2843 tools::Time aTime( 0, 0, 0 );
2845 if ( GetField() )
2847 bool bAllowMalformed = ImplAllowMalformedInput();
2848 if ( TextToTime( GetField()->GetText(), aTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper(), !bAllowMalformed ) )
2850 if ( aTime > GetMax() )
2851 aTime = GetMax();
2852 else if ( aTime < GetMin() )
2853 aTime = GetMin();
2855 else
2857 if ( bAllowMalformed )
2858 aTime = tools::Time( 99, 99, 99 ); // set invalid time
2859 else
2860 aTime = maLastTime;
2864 return aTime;
2867 void TimeFormatter::Reformat()
2869 if ( !GetField() )
2870 return;
2872 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
2873 return;
2875 OUString aStr;
2876 ImplTimeReformat( GetField()->GetText(), aStr );
2878 if ( !aStr.isEmpty() )
2880 ImplSetText( aStr );
2881 (void)TextToTime(aStr, maLastTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper());
2883 else
2884 SetTime( maLastTime );
2887 TimeField::TimeField( vcl::Window* pParent, WinBits nWinStyle ) :
2888 SpinField( pParent, nWinStyle ),
2889 TimeFormatter(this),
2890 maFirst( GetMin() ),
2891 maLast( GetMax() )
2893 SetText( ImplGetLocaleDataWrapper().getTime( maFieldTime, false ) );
2894 Reformat();
2897 void TimeField::dispose()
2899 ClearField();
2900 SpinField::dispose();
2903 bool TimeField::PreNotify( NotifyEvent& rNEvt )
2905 if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
2907 if ( ImplTimeProcessKeyInput( *rNEvt.GetKeyEvent(), IsStrictFormat(), IsDuration(), GetFormat(), ImplGetLocaleDataWrapper() ) )
2908 return true;
2911 return SpinField::PreNotify( rNEvt );
2914 bool TimeField::EventNotify( NotifyEvent& rNEvt )
2916 if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
2917 MarkToBeReformatted( false );
2918 else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
2920 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
2922 if ( !ImplAllowMalformedInput() )
2923 Reformat();
2924 else
2926 tools::Time aTime( 0, 0, 0 );
2927 if ( TextToTime( GetText(), aTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper(), false ) )
2928 // even with strict text analysis, our text is a valid time -> do a complete
2929 // reformat
2930 Reformat();
2935 return SpinField::EventNotify( rNEvt );
2938 void TimeField::DataChanged( const DataChangedEvent& rDCEvt )
2940 SpinField::DataChanged( rDCEvt );
2942 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
2944 ImplResetLocaleDataWrapper();
2945 ReformatAll();
2949 void TimeField::Modify()
2951 MarkToBeReformatted( true );
2952 SpinField::Modify();
2955 void TimeField::Up()
2957 ImplTimeSpinArea( true );
2958 SpinField::Up();
2961 void TimeField::Down()
2963 ImplTimeSpinArea( false );
2964 SpinField::Down();
2967 void TimeField::First()
2969 ImplNewFieldValue( maFirst );
2970 SpinField::First();
2973 void TimeField::Last()
2975 ImplNewFieldValue( maLast );
2976 SpinField::Last();
2979 void TimeField::SetExtFormat( ExtTimeFieldFormat eFormat )
2981 switch ( eFormat )
2983 case ExtTimeFieldFormat::Short24H:
2985 SetTimeFormat( TimeFormat::Hour24 );
2986 SetDuration( false );
2987 SetFormat( TimeFieldFormat::F_NONE );
2989 break;
2990 case ExtTimeFieldFormat::Long24H:
2992 SetTimeFormat( TimeFormat::Hour24 );
2993 SetDuration( false );
2994 SetFormat( TimeFieldFormat::F_SEC );
2996 break;
2997 case ExtTimeFieldFormat::Short12H:
2999 SetTimeFormat( TimeFormat::Hour12 );
3000 SetDuration( false );
3001 SetFormat( TimeFieldFormat::F_NONE );
3003 break;
3004 case ExtTimeFieldFormat::Long12H:
3006 SetTimeFormat( TimeFormat::Hour12 );
3007 SetDuration( false );
3008 SetFormat( TimeFieldFormat::F_SEC );
3010 break;
3011 case ExtTimeFieldFormat::ShortDuration:
3013 SetDuration( true );
3014 SetFormat( TimeFieldFormat::F_NONE );
3016 break;
3017 case ExtTimeFieldFormat::LongDuration:
3019 SetDuration( true );
3020 SetFormat( TimeFieldFormat::F_SEC );
3022 break;
3023 default: OSL_FAIL( "ExtTimeFieldFormat unknown!" );
3026 if ( GetField() && !GetField()->GetText().isEmpty() )
3027 SetUserTime( GetTime() );
3028 ReformatAll();
3031 TimeBox::TimeBox(vcl::Window* pParent, WinBits nWinStyle)
3032 : ComboBox(pParent, nWinStyle)
3033 , TimeFormatter(this)
3035 SetText( ImplGetLocaleDataWrapper().getTime( maFieldTime, false ) );
3036 Reformat();
3039 void TimeBox::dispose()
3041 ClearField();
3042 ComboBox::dispose();
3045 bool TimeBox::PreNotify( NotifyEvent& rNEvt )
3047 if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
3049 if ( ImplTimeProcessKeyInput( *rNEvt.GetKeyEvent(), IsStrictFormat(), IsDuration(), GetFormat(), ImplGetLocaleDataWrapper() ) )
3050 return true;
3053 return ComboBox::PreNotify( rNEvt );
3056 bool TimeBox::EventNotify( NotifyEvent& rNEvt )
3058 if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
3059 MarkToBeReformatted( false );
3060 else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
3062 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
3063 Reformat();
3066 return ComboBox::EventNotify( rNEvt );
3069 void TimeBox::DataChanged( const DataChangedEvent& rDCEvt )
3071 ComboBox::DataChanged( rDCEvt );
3073 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
3075 ImplResetLocaleDataWrapper();
3076 ReformatAll();
3080 void TimeBox::Modify()
3082 MarkToBeReformatted( true );
3083 ComboBox::Modify();
3086 void TimeBox::ReformatAll()
3088 OUString aStr;
3089 SetUpdateMode( false );
3090 const sal_Int32 nEntryCount = GetEntryCount();
3091 for ( sal_Int32 i=0; i < nEntryCount; ++i )
3093 ImplTimeReformat( GetEntry( i ), aStr );
3094 RemoveEntryAt(i);
3095 InsertEntry( aStr, i );
3097 TimeFormatter::Reformat();
3098 SetUpdateMode( true );
3101 namespace weld
3103 tools::Time TimeFormatter::ConvertValue(int nValue)
3105 tools::Time aTime(tools::Time::EMPTY);
3106 aTime.MakeTimeFromMS(nValue);
3107 return aTime;
3110 int TimeFormatter::ConvertValue(const tools::Time& rTime)
3112 return rTime.GetMSFromTime();
3115 void TimeFormatter::SetTime(const tools::Time& rTime)
3117 auto nTime = ConvertValue(rTime);
3118 bool bForceOutput = GetEntryText().isEmpty() && rTime == GetTime();
3119 if (bForceOutput)
3121 ImplSetValue(nTime, true);
3122 return;
3124 SetValue(nTime);
3127 tools::Time TimeFormatter::GetTime()
3129 return ConvertValue(GetValue());
3132 void TimeFormatter::SetMin(const tools::Time& rNewMin)
3134 SetMinValue(ConvertValue(rNewMin));
3137 void TimeFormatter::SetMax(const tools::Time& rNewMax)
3139 SetMaxValue(ConvertValue(rNewMax));
3142 OUString TimeFormatter::FormatNumber(int nValue) const
3144 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
3145 return ::TimeFormatter::FormatTime(ConvertValue(nValue), m_eFormat, m_eTimeFormat, m_bDuration, rLocaleData);
3148 IMPL_LINK_NOARG(TimeFormatter, FormatOutputHdl, LinkParamNone*, bool)
3150 OUString sText = FormatNumber(GetValue());
3151 ImplSetTextImpl(sText, nullptr);
3152 return true;
3155 IMPL_LINK(TimeFormatter, ParseInputHdl, sal_Int64*, result, TriState)
3157 const LocaleDataWrapper& rLocaleDataWrapper = Application::GetSettings().GetLocaleDataWrapper();
3159 tools::Time aResult(tools::Time::EMPTY);
3160 bool bRet = ::TimeFormatter::TextToTime(GetEntryText(), aResult, m_eFormat, m_bDuration, rLocaleDataWrapper);
3161 if (bRet)
3162 *result = ConvertValue(aResult);
3164 return bRet ? TRISTATE_TRUE : TRISTATE_FALSE;
3167 IMPL_LINK(TimeFormatter, CursorChangedHdl, weld::Entry&, rEntry, void)
3169 int nStartPos, nEndPos;
3170 rEntry.get_selection_bounds(nStartPos, nEndPos);
3172 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
3173 const int nTimeArea = ::TimeFormatter::GetTimeArea(m_eFormat, GetEntryText(), nEndPos, rLocaleData);
3175 int nIncrements = 1;
3177 if (nTimeArea == 1)
3178 nIncrements = 1000 * 60 * 60;
3179 else if (nTimeArea == 2)
3180 nIncrements = 1000 * 60;
3181 else if (nTimeArea == 3)
3182 nIncrements = 1000;
3184 SetSpinSize(nIncrements);
3188 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */