1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
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();
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();
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';
97 // fill with zeros up to the minimal length
105 // copy temp buffer to real buffer
112 while ( pTempBuf
!= aTempBuf
);
117 static sal_Unicode
* ImplAddSNum( sal_Unicode
* pBuf
, sal_Int32 nNumber
, int nMinLen
)
124 return ImplAddNum( pBuf
, nNumber
, nMinLen
);
127 static sal_uInt16
ImplGetNum( const sal_Unicode
*& rpBuf
, bool& rbError
)
135 sal_uInt16 nNumber
= 0;
136 while( ( *rpBuf
>= '0' ) && ( *rpBuf
<= '9' ) )
139 nNumber
+= *rpBuf
- '0';
146 static void ImplSkipDelimiters( const sal_Unicode
*& rpBuf
)
148 while( ( *rpBuf
== ',' ) || ( *rpBuf
== '.' ) || ( *rpBuf
== ';' ) ||
149 ( *rpBuf
== ':' ) || ( *rpBuf
== '-' ) || ( *rpBuf
== '/' ) )
155 static bool ImplIsPatternChar( sal_Unicode cChar
, char cEditMask
)
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");
171 if ( (cEditMask
== EDITMASK_ALPHA
) || (cEditMask
== EDITMASK_UPPERALPHA
) )
173 if( !CharClass::isLetterType( nType
) )
176 else if ( cEditMask
== EDITMASK_NUM
)
178 if( !CharClass::isNumericType( nType
) )
181 else if ( (cEditMask
== EDITMASK_ALPHANUM
) || (cEditMask
== EDITMASK_UPPERALPHANUM
) )
183 if( !CharClass::isLetterNumericType( nType
) )
186 else if ( (cEditMask
== EDITMASK_ALLCHAR
) || (cEditMask
== EDITMASK_UPPERALLCHAR
) )
191 else if ( cEditMask
== EDITMASK_NUMSPACE
)
193 if ( !CharClass::isNumericType( nType
) && ( cChar
!= ' ' ) )
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];
219 static bool ImplCommaPointCharEqual( sal_Unicode c1
, sal_Unicode c2
)
223 else if ( ((c1
== '.') || (c1
== ',')) &&
224 ((c2
== '.') || (c2
== ',')) )
230 static OUString
ImplPatternReformat( const OUString
& rStr
,
231 const OString
& rEditMask
,
232 std::u16string_view rLiteralMask
,
233 sal_uInt16 nFormatFlags
)
235 if (rEditMask
.isEmpty())
238 OUStringBuffer
aOutStr(rLiteralMask
);
239 sal_Unicode cTempChar
;
241 sal_Unicode cLiteral
;
243 sal_Int32 nStrIndex
= 0;
247 while ( i
< rEditMask
.getLength() )
249 if ( nStrIndex
>= rStr
.getLength() )
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
) )
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.
268 while ( n
< rEditMask
.getLength() )
270 if ( rEditMask
[n
] != EDITMASK_LITERAL
)
272 if ( !ImplIsPatternChar( cChar
, rEditMask
[n
] ) )
283 // valid character at this position
284 cTempChar
= ImplPatternChar( cChar
, cMask
);
287 // use this character
288 aOutStr
[i
] = cTempChar
;
293 // copy if it is a literal character
294 if ( cLiteral
== cChar
)
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
)
303 while ( n
< rEditMask
.getLength() )
305 if ( rEditMask
[n
] == EDITMASK_LITERAL
)
307 if ( ImplCommaPointCharEqual( cChar
, rLiteralMask
[n
] ) )
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
) )
342 if ( (rEditMask
[nMaxPos
-1] != EDITMASK_LITERAL
) &&
343 (rStr
[nMaxPos
-1] != ' ') )
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
)
361 if ( rPos
> nMaxPos
)
364 // character should not move left
365 if ( rPos
< nCursorPos
)
369 static OUString
ImplPatternProcessStrictModify(const OUString
& rText
,
370 const OString
& rEditMask
,
371 std::u16string_view rLiteralMask
,
374 OUString
aText(rText
);
376 // remove leading blanks
377 if (bSameMask
&& !rEditMask
.isEmpty())
380 sal_Int32 nMaxLen
= aText
.getLength();
381 while ( i
< nMaxLen
)
383 if ( (rEditMask
[i
] != EDITMASK_LITERAL
) &&
389 // keep all literal characters
390 while ( i
&& (rEditMask
[i
] == EDITMASK_LITERAL
) )
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
,
403 OUString aText
= pEdit
->GetText();
404 OUString aNewText
= ImplPatternProcessStrictModify(aText
,
409 if ( aNewText
== aText
)
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
;
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
,
437 OUString aText
= rEntry
.get_text();
438 OUString aNewText
= ImplPatternProcessStrictModify(aText
,
443 if (aNewText
== aText
)
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
)
460 else if (nStartPos
> 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
;
476 if ( rEditMask
[nTempPos
-1] != EDITMASK_LITERAL
)
478 nNewPos
= nTempPos
-1;
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
)
501 ImplPatternMaxPos( rStr
, rEditMask
, nFormatFlags
, bSameMask
, nCursorPos
, nNewPos
);
507 class IEditImplementation
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
,
531 if ( rEditMask
.isEmpty() || !bStrictFormat
)
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());
544 if ( nKeyCode
&& !aCode
.IsMod1() && !aCode
.IsMod2() )
546 if ( nKeyCode
== KEY_LEFT
)
548 Selection
aSel( ImplPatternLeftPos( rEditMask
, nCursorPos
) );
550 aSel
.Min() = aOldSel
.Min();
551 rEdit
.SetSelection( aSel
);
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
);
560 nCursorPos
= aSel
.Min();
561 aSel
.Max() = ImplPatternRightPos( rEdit
.GetText(), rEditMask
, nFormatFlags
, bSameMask
, nCursorPos
);
563 aSel
.Min() = aOldSel
.Min();
565 aSel
.Min() = aSel
.Max();
566 rEdit
.SetSelection( aSel
);
569 else if ( nKeyCode
== KEY_HOME
)
571 // Home is the position of the first non-literal character
573 while ( (nNewPos
< rEditMask
.getLength()) &&
574 (rEditMask
[nNewPos
] == EDITMASK_LITERAL
) )
577 // Home should not move to the right
578 if ( nCursorPos
< nNewPos
)
579 nNewPos
= nCursorPos
;
580 Selection
aSel( nNewPos
);
582 aSel
.Min() = aOldSel
.Min();
583 rEdit
.SetSelection( aSel
);
586 else if ( nKeyCode
== KEY_END
)
588 // End is position of last non-literal character
589 nNewPos
= rEditMask
.getLength();
591 (rEditMask
[nNewPos
-1] == EDITMASK_LITERAL
) )
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
);
597 nCursorPos
= static_cast<sal_Int32
>(aSel
.Min());
598 ImplPatternMaxPos( rEdit
.GetText(), rEditMask
, nFormatFlags
, bSameMask
, nCursorPos
, nNewPos
);
599 aSel
.Max() = nNewPos
;
601 aSel
.Min() = aOldSel
.Min();
603 aSel
.Min() = aSel
.Max();
604 rEdit
.SetSelection( aSel
);
607 else if ( (nKeyCode
== KEY_BACKSPACE
) || (nKeyCode
== KEY_DELETE
) )
609 OUString
aOldStr( rEdit
.GetText() );
610 OUStringBuffer
aStr( aOldStr
);
611 Selection aSel
= aOldSel
;
614 nNewPos
= static_cast<sal_Int32
>(aSel
.Min());
616 // if selection then delete it
620 aStr
.remove( static_cast<sal_Int32
>(aSel
.Min()), static_cast<sal_Int32
>(aSel
.Len()) );
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
);
630 if ( nKeyCode
== KEY_BACKSPACE
)
633 nNewPos
= ImplPatternLeftPos( rEditMask
, nTempPos
);
636 nTempPos
= ImplPatternRightPos( aStr
, rEditMask
, nFormatFlags
, bSameMask
, nNewPos
);
638 if ( nNewPos
!= nTempPos
)
642 if ( rEditMask
[nNewPos
] != EDITMASK_LITERAL
)
643 aStr
.remove( nNewPos
, 1 );
647 aStr
[nNewPos
] = rLiteralMask
[nNewPos
];
652 OUString sStr
= aStr
.makeStringAndClear();
653 if ( aOldStr
!= sStr
)
656 sStr
= ImplPatternReformat( sStr
, rEditMask
, rLiteralMask
, nFormatFlags
);
658 rEdit
.SetText( sStr
, Selection( nNewPos
) );
660 rbInKeyInput
= false;
663 rEdit
.SetSelection( Selection( nNewPos
) );
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
678 if ( rKEvt
.GetKeyCode().IsMod2() || (cChar
< 32) || (cChar
== 127) )
681 Selection aSel
= aOldSel
;
683 nNewPos
= aSel
.Min();
685 if ( nNewPos
< rEditMask
.getLength() )
687 sal_Unicode cPattChar
= ImplPatternChar( cChar
, rEditMask
[nNewPos
] );
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.
696 (rEditMask
[nNewPos
-1] != EDITMASK_LITERAL
) &&
699 // search for next character not being a literal
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
] ) )
710 ImplPatternMaxPos( rEdit
.GetText(), rEditMask
, nFormatFlags
, bSameMask
, nNewPos
, nTempPos
);
711 if ( nTempPos
> nNewPos
)
713 rEdit
.SetSelection( Selection( nTempPos
) );
730 OUStringBuffer
aStr(rEdit
.GetText());
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
)) )
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() )
758 aStr
= ImplPatternReformat( aStr
.toString(), rEditMask
, rLiteralMask
, nFormatFlags
);
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() )
782 const OUString sStr
= aStr
.makeStringAndClear();
783 Selection
aNewSel( ImplPatternRightPos( sStr
, rEditMask
, nFormatFlags
, bSameMask
, nNewPos
) );
784 rEdit
.SetText( sStr
, aNewSel
);
786 rbInKeyInput
= false;
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());
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
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
) )
825 if ( i
< rLiteralMask
.getLength() )
827 if ( rLiteralMask
[i
] != ' ' )
848 PatternFormatter::PatternFormatter(Edit
* pEdit
)
849 : FormatterBase(pEdit
)
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
);
870 class EntryImplementation
: public IEditImplementation
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();
915 weld::PatternFormatter
& m_rFormatter
;
916 weld::Entry
& m_rEntry
;
922 void PatternFormatter::SetStrictFormat(bool bStrict
)
924 if (bStrict
!= m_bStrictFormat
)
926 m_bStrictFormat
= bStrict
;
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
);
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()
953 void PatternFormatter::EntryLostFocus()
959 void PatternFormatter::Modify()
961 if (!m_bInPattKeyInput
)
964 ImplPatternProcessStrictModify(m_rEntry
, m_aEditMask
, m_aLiteralMask
, m_bSameMask
);
968 m_aModifyHdl
.Call(m_rEntry
);
971 IMPL_LINK(PatternFormatter
, KeyInputHdl
, const KeyEvent
&, rKEvt
, bool)
973 if (m_aKeyPressHdl
.Call(rKEvt
))
975 if (rKEvt
.GetKeyCode().IsMod2())
977 EntryImplementation
aAdapt(*this);
978 return ImplPatternProcessKeyInput(aAdapt
, rKEvt
, m_aEditMask
, m_aLiteralMask
,
980 m_bSameMask
, m_bInPattKeyInput
);
984 void PatternFormatter::SetString( const OUString
& rStr
)
988 GetField()->SetText( rStr
);
989 MarkToBeReformatted( false );
993 OUString
PatternFormatter::GetString() const
998 return ImplPatternReformat( GetField()->GetText(), m_aEditMask
, maLiteralMask
, 0/*nFormatFlags*/ );
1001 void PatternFormatter::Reformat()
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)
1018 void PatternField::dispose()
1021 SpinField::dispose();
1026 class EditImplementation
: public IEditImplementation
1029 EditImplementation(Edit
& 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();
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(),
1077 ImplIsSameMask(), ImplGetInPattKeyInput() ) )
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()) )
1094 return SpinField::EventNotify( rNEvt
);
1097 void PatternField::Modify()
1099 if ( !ImplGetInPattKeyInput() )
1101 if ( IsStrictFormat() )
1102 ImplPatternProcessStrictModify( GetField(), GetEditMask(), GetLiteralMask(), ImplIsSameMask() );
1104 MarkToBeReformatted( true );
1107 SpinField::Modify();
1110 PatternBox::PatternBox(vcl::Window
* pParent
, WinBits nWinStyle
)
1111 : ComboBox( pParent
, nWinStyle
)
1112 , PatternFormatter(this)
1117 void PatternBox::dispose()
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(),
1130 ImplIsSameMask(), ImplGetInPattKeyInput() ) )
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()) )
1147 return ComboBox::EventNotify( rNEvt
);
1150 void PatternBox::Modify()
1152 if ( !ImplGetInPattKeyInput() )
1154 if ( IsStrictFormat() )
1155 ImplPatternProcessStrictModify( GetField(), GetEditMask(), GetLiteralMask(), ImplIsSameMask() );
1157 MarkToBeReformatted( true );
1163 void PatternBox::ReformatAll()
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*/ );
1172 InsertEntry( aStr
, i
);
1174 PatternFormatter::Reformat();
1175 SetUpdateMode( true );
1178 static ExtDateFieldFormat
ImplGetExtFormat( LongDateOrder 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
)
1193 while (i1
!= rStr
.getLength() && (rStr
[i1
] < '0' || rStr
[i1
] > '9')) {
1197 while (i2
!= rStr
.getLength() && rStr
[i2
] >= '0' && rStr
[i2
] <= '9') {
1200 sal_Int32 nValue
= o3tl::toInt32(rStr
.subView(i1
, i2
-i1
));
1201 rStr
= rStr
.copy(std::min(i2
+1, rStr
.getLength()));
1205 static bool ImplCutMonthName( OUString
& rStr
, std::u16string_view _rLookupMonthName
)
1207 sal_Int32 index
= 0;
1208 rStr
= rStr
.replaceFirst(_rLookupMonthName
, "", &index
);
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
)
1218 if ( ImplCutMonthName( rStr
, rMonths
[i
].FullName
) )
1221 // short month name?
1222 if ( ImplCutMonthName( rStr
, rMonths
[i
].AbbrevName
) )
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
);
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
);
1248 const uno::Sequence
< i18n::CalendarItem2
> aPartitiveMonths
= rCalendarWrapper
.getPartitiveMonths();
1249 if (aPartitiveMonths
!= aMonths
)
1251 nMonth
= ImplGetMonthFromCalendarItem( rStr
, aPartitiveMonths
);
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
);
1272 if (rCalendar
.Months
!= rCalendar
.GenitiveMonths
)
1274 nMonth
= ImplGetMonthFromCalendarItem( rStr
, rCalendar
.GenitiveMonths
);
1279 if (rCalendar
.Months
!= rCalendar
.PartitiveMonths
)
1281 nMonth
= ImplGetMonthFromCalendarItem( rStr
, rCalendar
.PartitiveMonths
);
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
) )
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();
1329 case LongDateOrder::MDY
:
1330 nMonth
= ImplCutMonthFromString( aStr
, aCalendarName
, rLocaleDataWrapper
, rCalendarWrapper
);
1331 nDay
= ImplCutNumberFromString( aStr
);
1332 nYear
= ImplCutNumberFromString( aStr
);
1334 case LongDateOrder::DMY
:
1335 nDay
= ImplCutNumberFromString( aStr
);
1336 nMonth
= ImplCutMonthFromString( aStr
, aCalendarName
, rLocaleDataWrapper
, rCalendarWrapper
);
1337 nYear
= ImplCutNumberFromString( aStr
);
1339 case LongDateOrder::YDM
:
1340 nYear
= ImplCutNumberFromString( aStr
);
1341 nDay
= ImplCutNumberFromString( aStr
);
1342 nMonth
= ImplCutMonthFromString( aStr
, aCalendarName
, rLocaleDataWrapper
, rCalendarWrapper
);
1344 case LongDateOrder::YMD
:
1346 nYear
= ImplCutNumberFromString( aStr
);
1347 nMonth
= ImplCutMonthFromString( aStr
, aCalendarName
, rLocaleDataWrapper
, rCalendarWrapper
);
1348 nDay
= ImplCutNumberFromString( aStr
);
1351 if (aCalendarName
!= "gregorian")
1353 // Calendar widget is Gregorian, convert date.
1355 bError
= !nDay
|| !nMonth
|| !nYear
;
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();
1367 Date aDate
= aCW
.getEpochStart() + aCW
.getDateTime();
1368 nYear
= aDate
.GetYear();
1369 nMonth
= aDate
.GetMonth();
1370 nDay
= aDate
.GetDay();
1379 // Check if year is present:
1380 OUString aDateSep
= ImplGetDateSep( rLocaleDataWrapper
, eDateOrder
);
1381 sal_Int32 nSepPos
= aStr
.indexOf( aDateSep
);
1384 nSepPos
= aStr
.indexOf( aDateSep
, nSepPos
+1 );
1385 if ( ( nSepPos
< 0 ) || ( nSepPos
== (aStr
.getLength()-1) ) )
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
);
1404 nYear
= ImplGetNum( pBuf
, bError
);
1407 case ExtDateFieldFormat::ShortMMDDYY
:
1408 case ExtDateFieldFormat::ShortMMDDYYYY
:
1410 nMonth
= ImplGetNum( pBuf
, bError
);
1411 ImplSkipDelimiters( pBuf
);
1412 nDay
= ImplGetNum( pBuf
, bError
);
1413 ImplSkipDelimiters( pBuf
);
1415 nYear
= ImplGetNum( pBuf
, bError
);
1418 case ExtDateFieldFormat::ShortYYMMDD
:
1419 case ExtDateFieldFormat::ShortYYYYMMDD
:
1420 case ExtDateFieldFormat::ShortYYMMDD_DIN5008
:
1421 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008
:
1424 nYear
= ImplGetNum( pBuf
, bError
);
1425 ImplSkipDelimiters( pBuf
);
1426 nMonth
= ImplGetNum( pBuf
, bError
);
1427 ImplSkipDelimiters( pBuf
);
1428 nDay
= ImplGetNum( pBuf
, bError
);
1434 OSL_FAIL( "DateOrder???" );
1439 if ( bError
|| !nDay
|| !nMonth
)
1442 Date
aNewDate( nDay
, nMonth
, nYear
);
1443 DateFormatter::ExpandCentury( aNewDate
, officecfg::Office::Common::DateFormat::TwoDigitYear::get() );
1444 if ( aNewDate
.IsValidDate() )
1452 void DateFormatter::ImplDateReformat( const OUString
& rStr
, OUString
& rOutStr
)
1454 Date
aDate( Date::EMPTY
);
1455 if (!TextToDate(rStr
, aDate
, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper()))
1458 Date aTempDate
= aDate
;
1459 if ( aTempDate
> GetMax() )
1460 aTempDate
= GetMax();
1461 else if ( aTempDate
< GetMin() )
1462 aTempDate
= GetMin();
1464 rOutStr
= ImplGetDateAsText( aTempDate
);
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
;
1479 case DateOrder::MDY
:
1480 eDateFormat
= bShowCentury
? ExtDateFieldFormat::ShortMMDDYYYY
: ExtDateFieldFormat::ShortMMDDYY
;
1483 eDateFormat
= bShowCentury
? ExtDateFieldFormat::ShortYYYYMMDD
: ExtDateFieldFormat::ShortYYMMDD
;
1490 OUString
DateFormatter::FormatDate(const Date
& rDate
, ExtDateFieldFormat eExtFormat
,
1491 const LocaleDataWrapper
& rLocaleData
,
1492 const Formatter::StaticFormatter
& rStaticFormatter
)
1494 bool bShowCentury
= false;
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;
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
)
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));
1548 pFormatter
->GetOutputString( rDate
- pFormatter
->GetNullDate(), nIndex
, aStr
, &pCol
);
1549 // Reset to what other uses may expect.
1550 pFormatter
->ChangeIntl( aFormatterLang
.getLanguageType(false));
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
);
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
);
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 );
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());
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
)
1652 void DateField::ImplDateSpinArea( bool bUp
)
1654 // increment days if all is selected
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
);
1666 ExtDateFieldFormat eFormat
= GetExtDateFormat( true );
1667 sal_Int8 nDateArea
= GetDateArea(eFormat
, aText
, aSelection
.Max(), ImplGetLocaleDataWrapper());
1671 case ExtDateFieldFormat::ShortMMDDYY
:
1672 case ExtDateFieldFormat::ShortMMDDYYYY
:
1675 case 1: ImplDateIncrementMonth( aDate
, bUp
);
1677 case 2: ImplDateIncrementDay( aDate
, bUp
);
1679 case 3: ImplDateIncrementYear( aDate
, bUp
);
1683 case ExtDateFieldFormat::ShortDDMMYY
:
1684 case ExtDateFieldFormat::ShortDDMMYYYY
:
1687 case 1: ImplDateIncrementDay( aDate
, bUp
);
1689 case 2: ImplDateIncrementMonth( aDate
, bUp
);
1691 case 3: ImplDateIncrementYear( aDate
, bUp
);
1695 case ExtDateFieldFormat::ShortYYMMDD
:
1696 case ExtDateFieldFormat::ShortYYYYMMDD
:
1697 case ExtDateFieldFormat::ShortYYMMDD_DIN5008
:
1698 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008
:
1701 case 1: ImplDateIncrementYear( aDate
, bUp
);
1703 case 2: ImplDateIncrementMonth( aDate
, bUp
);
1705 case 3: ImplDateIncrementDay( aDate
, bUp
);
1710 OSL_FAIL( "invalid conversion" );
1715 ImplNewFieldValue( aDate
);
1718 DateFormatter::DateFormatter(Edit
* pEdit
)
1719 : FormatterBase(pEdit
)
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
;
1752 ExtDateFieldFormat
DateFormatter::GetExtDateFormat( bool bResolveSystemFormat
) const
1754 ExtDateFieldFormat eDateFormat
= mnExtDateFormat
;
1756 if (bResolveSystemFormat
)
1757 eDateFormat
= ResolveSystemFormat(eDateFormat
, ImplGetLocaleDataWrapper());
1762 void DateFormatter::ReformatAll()
1767 void DateFormatter::SetMin( const Date
& rNewMin
)
1770 if ( !IsEmptyFieldValue() )
1774 void DateFormatter::SetMax( const Date
& rNewMax
)
1777 if ( !IsEmptyFieldValue() )
1781 void DateFormatter::SetLongFormat( bool bLong
)
1783 mbLongFormat
= bLong
;
1785 // #91913# Remove LongFormat and DateShowCentury - redundant
1788 SetExtDateFormat( ExtDateFieldFormat::SystemLong
);
1792 if( mnExtDateFormat
== ExtDateFieldFormat::SystemLong
)
1793 SetExtDateFormat( ExtDateFieldFormat::SystemShort
);
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;
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;
1843 return eExtDateFormat
;
1847 void DateFormatter::SetShowDateCentury( bool bShowDateCentury
)
1849 mbShowDateCentury
= bShowDateCentury
;
1851 SetExtDateFormat(ChangeDateCentury(GetExtDateFormat(), bShowDateCentury
));
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
)
1868 else if ( aNewDate
< maMin
)
1870 maLastDate
= aNewDate
;
1873 ImplSetText( ImplGetDateAsText( aNewDate
), pNewSelection
);
1876 void DateFormatter::ImplNewFieldValue( const Date
& rDate
)
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
);
1911 if (TextToDate(GetField()->GetText(), aDate
, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper()))
1913 if ( aDate
> maMax
)
1915 else if ( aDate
< maMin
)
1920 // !!! We should find out why dates are treated differently than other fields (see
1923 if ( !ImplAllowMalformedInput() )
1925 if ( maLastDate
.GetDate() )
1927 else if ( !IsEmptyFieldValueEnabled() )
1928 aDate
= Date( Date::SYSTEM
);
1931 aDate
= Date( Date::EMPTY
); // set invalid date
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() )
1953 else if ( !maLastDate
.GetDate() )
1955 Date
aDate( Date::EMPTY
);
1956 bEmpty
= !TextToDate(GetField()->GetText(), aDate
, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper());
1962 void DateFormatter::Reformat()
1967 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
1971 ImplDateReformat( GetField()->GetText(), aStr
);
1973 if ( !aStr
.isEmpty() )
1975 ImplSetText( aStr
);
1976 (void)TextToDate(aStr
, maLastDate
, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper());
1980 if ( maLastDate
.GetDate() )
1981 SetDate( maLastDate
);
1982 else if ( !IsEmptyFieldValueEnabled() )
1983 SetDate( Date( Date::SYSTEM
) );
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) )
2005 rDate
.SetYear( nDateYear
+ (nCentury
*100) );
2009 DateField::DateField( vcl::Window
* pParent
, WinBits nWinStyle
) :
2010 SpinField( pParent
, nWinStyle
),
2011 DateFormatter(this),
2012 maFirst( GetMin() ),
2015 SetText( ImplGetLocaleDataWrapper().getDate( ImplGetFieldDate() ) );
2020 void DateField::dispose()
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() ) )
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
2050 bool bTextLen
= !GetText().isEmpty();
2051 if ( bTextLen
|| !IsEmptyFieldValueEnabled() )
2053 if ( !ImplAllowMalformedInput() )
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
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();
2087 void DateField::Modify()
2089 MarkToBeReformatted( true );
2090 SpinField::Modify();
2093 void DateField::Up()
2095 ImplDateSpinArea( true );
2099 void DateField::Down()
2101 ImplDateSpinArea( false );
2105 void DateField::First()
2107 ImplNewFieldValue( maFirst
);
2111 void DateField::Last()
2113 ImplNewFieldValue( maLast
);
2117 DateBox::DateBox(vcl::Window
* pParent
, WinBits nWinStyle
)
2118 : ComboBox( pParent
, nWinStyle
)
2119 , DateFormatter(this)
2121 SetText( ImplGetLocaleDataWrapper().getDate( ImplGetFieldDate() ) );
2125 void DateBox::dispose()
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() ) )
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();
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() )
2169 SetEmptyFieldValueData( true );
2174 return ComboBox::EventNotify( rNEvt
);
2177 void DateBox::Modify()
2179 MarkToBeReformatted( true );
2183 void DateBox::ReformatAll()
2186 SetUpdateMode( false );
2187 const sal_Int32 nEntryCount
= GetEntryCount();
2188 for ( sal_Int32 i
=0; i
< nEntryCount
; ++i
)
2190 ImplDateReformat( GetEntry( i
), aStr
);
2192 InsertEntry( aStr
, i
);
2194 DateFormatter::Reformat();
2195 SetUpdateMode( true );
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
);
2217 void DateFormatter::SetDate(const Date
& rDate
)
2219 auto nDate
= rDate
.GetDate();
2220 bool bForceOutput
= GetEntryText().isEmpty() && rDate
== GetDate();
2223 ImplSetValue(nDate
, true);
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);
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());
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
)
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) ||
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
== '-')) )
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' )
2310 static bool ImplIsValidTimePortion( bool _bSkipInvalidCharacters
, const OUString
& _rStr
)
2312 if ( !_bSkipInvalidCharacters
)
2314 if ( ( _rStr
.getLength() > 2 ) || _rStr
.isEmpty() || !ImplIsOnlyDigits( _rStr
) )
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);
2329 if ( !ImplIsValidTimePortion( _bSkipInvalidCharacters
, sPortion
) )
2331 *_pPortion
= static_cast<short>(sPortion
.toInt32());
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
);
2343 sal_Int64 nNanoSec
= 0;
2344 tools::Time
aTime( 0, 0, 0 );
2349 // Search for separators
2350 if (!rLocaleDataWrapper
.getTimeSep().isEmpty())
2352 OUStringBuffer
aSepStr(",.;:/");
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
]))
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] == '-' )
2373 if ( eFormat
!= TimeFieldFormat::F_SEC_CS
)
2376 nSepPos
= aStr
.getLength();
2377 if ( !ImplCutTimePortion( aStr
, nSepPos
, _bSkipInvalidCharacters
, &nHour
) )
2380 nSepPos
= aStr
.indexOf( rLocaleDataWrapper
.getTimeSep() );
2381 if ( !aStr
.isEmpty() && aStr
[0] == '-' )
2385 if ( !ImplCutTimePortion( aStr
, nSepPos
, _bSkipInvalidCharacters
, &nMinute
) )
2388 nSepPos
= aStr
.indexOf( rLocaleDataWrapper
.getTimeSep() );
2389 if ( !aStr
.isEmpty() && aStr
[0] == '-' )
2393 if ( !ImplCutTimePortion( aStr
, nSepPos
, _bSkipInvalidCharacters
, &nSecond
) )
2395 if ( !aStr
.isEmpty() && aStr
[0] == '-' )
2397 nNanoSec
= o3tl::toInt64(aStr
);
2400 nSecond
= static_cast<short>(o3tl::toInt32(aStr
));
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;
2410 nHour
+= nMinute
/ 60;
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] == '-' )
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] == '-' )
2434 nSecond
= static_cast<short>(o3tl::toInt32(aStr
.subView( 0, nSepPos
)));
2435 aStr
.remove( 0, nSepPos
+1 );
2439 nHour
+= nMinute
/ 60;
2445 nMinute
+= nSecond
/ 60;
2447 nHour
+= nMinute
/ 60;
2450 nNanoSec
= o3tl::toInt64(aStr
);
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' )
2469 // round if negative?
2470 nNanoSec
= (nNanoSec
+ 5) / 10;
2475 assert(nNanoSec
> -1000000000 && nNanoSec
< 1000000000);
2476 if ( (nMinute
> 59) || (nSecond
> 59) || (nNanoSec
> 1000000000) )
2479 if ( eFormat
== TimeFieldFormat::F_NONE
)
2480 nSecond
= nNanoSec
= 0;
2481 else if ( eFormat
== TimeFieldFormat::F_SEC
)
2486 if ( bNegative
|| (nHour
< 0) || (nMinute
< 0) ||
2487 (nSecond
< 0) || (nNanoSec
< 0) )
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 ) ) )
2497 if ( (nHour
== 12) && ( ( aUpperCaseStr
.indexOf( "AM" ) >= 0 ) || ( aUpperCaseStr
.indexOf( aAMlocalised
) >= 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
) );
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"
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
) );
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() ) )
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
)
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
;
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
);
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();
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
)
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
)
2609 if (nPos
== std::u16string_view::npos
|| static_cast<sal_Int32
>(nPos
) >= nCursor
)
2620 size_t nPos
= rText
.find(rLocaleDataWrapper
.getTime100SecSep());
2621 if (nPos
== std::u16string_view::npos
|| static_cast<sal_Int32
>(nPos
) >= nCursor
)
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
);
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 );
2651 aAddTime
= -aAddTime
;
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
;
2668 void TimeField::ImplTimeSpinArea( bool bUp
)
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
)
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
2690 , mbEnforceValidValue(true)
2695 TimeFormatter::~TimeFormatter()
2699 void TimeFormatter::ReformatAll()
2704 void TimeFormatter::SetMin( const tools::Time
& rNewMin
)
2707 if ( !IsEmptyFieldValue() )
2711 void TimeFormatter::SetMax( const tools::Time
& rNewMax
)
2714 if ( !IsEmptyFieldValue() )
2718 void TimeFormatter::SetTimeFormat( TimeFormat eNewFormat
)
2720 mnTimeFormat
= eNewFormat
;
2724 void TimeFormatter::SetFormat( TimeFieldFormat eNewFormat
)
2726 meFormat
= eNewFormat
;
2730 void TimeFormatter::SetDuration( bool bNewDuration
)
2732 mbDuration
= bNewDuration
;
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
)
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
)
2776 bool b100Sec
= false;
2777 if ( eFormat
!= TimeFieldFormat::F_NONE
)
2779 if ( eFormat
== TimeFieldFormat::F_SEC_CS
)
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
;
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
);
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();
2813 aStr
+= "PM"; // rLocaleData.getTimePM();
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
;
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 );
2847 bool bAllowMalformed
= ImplAllowMalformedInput();
2848 if ( TextToTime( GetField()->GetText(), aTime
, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper(), !bAllowMalformed
) )
2850 if ( aTime
> GetMax() )
2852 else if ( aTime
< GetMin() )
2857 if ( bAllowMalformed
)
2858 aTime
= tools::Time( 99, 99, 99 ); // set invalid time
2867 void TimeFormatter::Reformat()
2872 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
2876 ImplTimeReformat( GetField()->GetText(), aStr
);
2878 if ( !aStr
.isEmpty() )
2880 ImplSetText( aStr
);
2881 (void)TextToTime(aStr
, maLastTime
, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper());
2884 SetTime( maLastTime
);
2887 TimeField::TimeField( vcl::Window
* pParent
, WinBits nWinStyle
) :
2888 SpinField( pParent
, nWinStyle
),
2889 TimeFormatter(this),
2890 maFirst( GetMin() ),
2893 SetText( ImplGetLocaleDataWrapper().getTime( maFieldTime
, false ) );
2897 void TimeField::dispose()
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() ) )
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() )
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
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();
2949 void TimeField::Modify()
2951 MarkToBeReformatted( true );
2952 SpinField::Modify();
2955 void TimeField::Up()
2957 ImplTimeSpinArea( true );
2961 void TimeField::Down()
2963 ImplTimeSpinArea( false );
2967 void TimeField::First()
2969 ImplNewFieldValue( maFirst
);
2973 void TimeField::Last()
2975 ImplNewFieldValue( maLast
);
2979 void TimeField::SetExtFormat( ExtTimeFieldFormat eFormat
)
2983 case ExtTimeFieldFormat::Short24H
:
2985 SetTimeFormat( TimeFormat::Hour24
);
2986 SetDuration( false );
2987 SetFormat( TimeFieldFormat::F_NONE
);
2990 case ExtTimeFieldFormat::Long24H
:
2992 SetTimeFormat( TimeFormat::Hour24
);
2993 SetDuration( false );
2994 SetFormat( TimeFieldFormat::F_SEC
);
2997 case ExtTimeFieldFormat::Short12H
:
2999 SetTimeFormat( TimeFormat::Hour12
);
3000 SetDuration( false );
3001 SetFormat( TimeFieldFormat::F_NONE
);
3004 case ExtTimeFieldFormat::Long12H
:
3006 SetTimeFormat( TimeFormat::Hour12
);
3007 SetDuration( false );
3008 SetFormat( TimeFieldFormat::F_SEC
);
3011 case ExtTimeFieldFormat::ShortDuration
:
3013 SetDuration( true );
3014 SetFormat( TimeFieldFormat::F_NONE
);
3017 case ExtTimeFieldFormat::LongDuration
:
3019 SetDuration( true );
3020 SetFormat( TimeFieldFormat::F_SEC
);
3023 default: OSL_FAIL( "ExtTimeFieldFormat unknown!" );
3026 if ( GetField() && !GetField()->GetText().isEmpty() )
3027 SetUserTime( GetTime() );
3031 TimeBox::TimeBox(vcl::Window
* pParent
, WinBits nWinStyle
)
3032 : ComboBox(pParent
, nWinStyle
)
3033 , TimeFormatter(this)
3035 SetText( ImplGetLocaleDataWrapper().getTime( maFieldTime
, false ) );
3039 void TimeBox::dispose()
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() ) )
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()) )
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();
3080 void TimeBox::Modify()
3082 MarkToBeReformatted( true );
3086 void TimeBox::ReformatAll()
3089 SetUpdateMode( false );
3090 const sal_Int32 nEntryCount
= GetEntryCount();
3091 for ( sal_Int32 i
=0; i
< nEntryCount
; ++i
)
3093 ImplTimeReformat( GetEntry( i
), aStr
);
3095 InsertEntry( aStr
, i
);
3097 TimeFormatter::Reformat();
3098 SetUpdateMode( true );
3103 tools::Time
TimeFormatter::ConvertValue(int nValue
)
3105 tools::Time
aTime(tools::Time::EMPTY
);
3106 aTime
.MakeTimeFromMS(nValue
);
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();
3121 ImplSetValue(nTime
, true);
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);
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
);
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;
3178 nIncrements
= 1000 * 60 * 60;
3179 else if (nTimeArea
== 2)
3180 nIncrements
= 1000 * 60;
3181 else if (nTimeArea
== 3)
3184 SetSpinSize(nIncrements
);
3188 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */