build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / vcl / source / control / field2.cxx
blob2cb03cf61973415ac96ffb20482ae52819b02ff7
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>
24 #include <tools/diagnose_ex.h>
25 #include <comphelper/processfactory.hxx>
26 #include <comphelper/string.hxx>
27 #include <tools/rc.h>
28 #include <vcl/svapp.hxx>
29 #include <vcl/event.hxx>
30 #include <vcl/field.hxx>
31 #include <vcl/unohelp.hxx>
32 #include <vcl/settings.hxx>
34 #include <svdata.hxx>
36 #include <i18nlangtag/mslangid.hxx>
38 #include <com/sun/star/lang/Locale.hpp>
39 #include <com/sun/star/i18n/XCharacterClassification.hpp>
40 #include <com/sun/star/i18n/KCharacterType.hpp>
42 #include <unotools/localedatawrapper.hxx>
43 #include <unotools/calendarwrapper.hxx>
44 #include <unotools/charclass.hxx>
45 #include <unotools/misccfg.hxx>
47 using namespace ::com::sun::star;
48 using namespace ::comphelper;
50 #define EDITMASK_LITERAL 'L'
51 #define EDITMASK_ALPHA 'a'
52 #define EDITMASK_UPPERALPHA 'A'
53 #define EDITMASK_ALPHANUM 'c'
54 #define EDITMASK_UPPERALPHANUM 'C'
55 #define EDITMASK_NUM 'N'
56 #define EDITMASK_NUMSPACE 'n'
57 #define EDITMASK_ALLCHAR 'x'
58 #define EDITMASK_UPPERALLCHAR 'X'
60 uno::Reference< i18n::XCharacterClassification > const & ImplGetCharClass()
62 static uno::Reference< i18n::XCharacterClassification > xCharClass;
63 if ( !xCharClass.is() )
64 xCharClass = vcl::unohelper::CreateCharacterClassification();
66 return xCharClass;
69 static sal_Unicode* ImplAddString( sal_Unicode* pBuf, const OUString& rStr )
71 if ( rStr.getLength() == 1 )
72 *pBuf++ = rStr[0];
73 else if ( rStr.isEmpty() )
75 else
77 memcpy( pBuf, rStr.getStr(), rStr.getLength() * sizeof(sal_Unicode) );
78 pBuf += rStr.getLength();
80 return pBuf;
83 static sal_Unicode* ImplAddNum( sal_Unicode* pBuf, sal_uLong nNumber, int nMinLen )
85 // fill temp buffer with digits
86 sal_Unicode aTempBuf[30];
87 sal_Unicode* pTempBuf = aTempBuf;
90 *pTempBuf = (sal_Unicode)(nNumber % 10) + '0';
91 pTempBuf++;
92 nNumber /= 10;
93 if ( nMinLen )
94 nMinLen--;
96 while ( nNumber );
98 // fill with zeros up to the minimal length
99 while ( nMinLen > 0 )
101 *pBuf = '0';
102 pBuf++;
103 nMinLen--;
106 // copy temp buffer to real buffer
109 pTempBuf--;
110 *pBuf = *pTempBuf;
111 pBuf++;
113 while ( pTempBuf != aTempBuf );
115 return pBuf;
118 static sal_Unicode* ImplAddSNum( sal_Unicode* pBuf, sal_Int32 nNumber, int nMinLen )
120 if (nNumber < 0)
122 *pBuf++ = '-';
123 nNumber = -nNumber;
125 return ImplAddNum( pBuf, nNumber, nMinLen);
128 static sal_uInt16 ImplGetNum( const sal_Unicode*& rpBuf, bool& rbError )
130 if ( !*rpBuf )
132 rbError = true;
133 return 0;
136 sal_uInt16 nNumber = 0;
137 while( ( *rpBuf >= '0' ) && ( *rpBuf <= '9' ) )
139 nNumber *= 10;
140 nNumber += *rpBuf - '0';
141 rpBuf++;
144 return nNumber;
147 static void ImplSkipDelimiters( const sal_Unicode*& rpBuf )
149 while( ( *rpBuf == ',' ) || ( *rpBuf == '.' ) || ( *rpBuf == ';' ) ||
150 ( *rpBuf == ':' ) || ( *rpBuf == '-' ) || ( *rpBuf == '/' ) )
152 rpBuf++;
156 static bool ImplIsPatternChar( sal_Unicode cChar, sal_Char cEditMask )
158 sal_Int32 nType = 0;
162 OUString aCharStr(cChar);
163 nType = ImplGetCharClass()->getStringType( aCharStr, 0, aCharStr.getLength(),
164 Application::GetSettings().GetLanguageTag().getLocale() );
166 catch (const css::uno::Exception&)
168 SAL_WARN( "vcl.control", "ImplIsPatternChar: Exception caught!" );
169 DBG_UNHANDLED_EXCEPTION();
170 return false;
173 if ( (cEditMask == EDITMASK_ALPHA) || (cEditMask == EDITMASK_UPPERALPHA) )
175 if( !CharClass::isLetterType( nType ) )
176 return false;
178 else if ( cEditMask == EDITMASK_NUM )
180 if( !CharClass::isNumericType( nType ) )
181 return false;
183 else if ( (cEditMask == EDITMASK_ALPHANUM) || (cEditMask == EDITMASK_UPPERALPHANUM) )
185 if( !CharClass::isLetterNumericType( nType ) )
186 return false;
188 else if ( (cEditMask == EDITMASK_ALLCHAR) || (cEditMask == EDITMASK_UPPERALLCHAR) )
190 if ( cChar < 32 )
191 return false;
193 else if ( cEditMask == EDITMASK_NUMSPACE )
195 if ( !CharClass::isNumericType( nType ) && ( cChar != ' ' ) )
196 return false;
198 else
199 return false;
201 return true;
204 static sal_Unicode ImplPatternChar( sal_Unicode cChar, sal_Char cEditMask )
206 if ( ImplIsPatternChar( cChar, cEditMask ) )
208 if ( (cEditMask == EDITMASK_UPPERALPHA) ||
209 (cEditMask == EDITMASK_UPPERALPHANUM) ||
210 ( cEditMask == EDITMASK_UPPERALLCHAR ) )
212 cChar = ImplGetCharClass()->toUpper(OUString(cChar), 0, 1,
213 Application::GetSettings().GetLanguageTag().getLocale())[0];
215 return cChar;
217 else
218 return 0;
221 static bool ImplCommaPointCharEqual( sal_Unicode c1, sal_Unicode c2 )
223 if ( c1 == c2 )
224 return true;
225 else if ( ((c1 == '.') || (c1 == ',')) &&
226 ((c2 == '.') || (c2 == ',')) )
227 return true;
228 else
229 return false;
232 static OUString ImplPatternReformat( const OUString& rStr,
233 const OString& rEditMask,
234 const OUString& rLiteralMask,
235 sal_uInt16 nFormatFlags )
237 if (rEditMask.isEmpty())
238 return rStr;
240 OUStringBuffer aOutStr = OUString(rLiteralMask);
241 sal_Unicode cTempChar;
242 sal_Unicode cChar;
243 sal_Unicode cLiteral;
244 sal_Char cMask;
245 sal_Int32 nStrIndex = 0;
246 sal_Int32 i = 0;
247 sal_Int32 n;
249 while ( i < rEditMask.getLength() )
251 if ( nStrIndex >= rStr.getLength() )
252 break;
254 cChar = rStr[nStrIndex];
255 cLiteral = rLiteralMask[i];
256 cMask = rEditMask[i];
258 // current position is a literal
259 if ( cMask == EDITMASK_LITERAL )
261 // if it is a literal copy otherwise ignore because it might be the next valid
262 // character of the string
263 if ( ImplCommaPointCharEqual( cChar, cLiteral ) )
264 nStrIndex++;
265 else
267 // Otherwise we check if it is a invalid character. This is the case if it does not
268 // fit in the pattern of the next non-literal character.
269 n = i+1;
270 while ( n < rEditMask.getLength() )
272 if ( rEditMask[n] != EDITMASK_LITERAL )
274 if ( !ImplIsPatternChar( cChar, rEditMask[n] ) )
275 nStrIndex++;
276 break;
279 n++;
283 else
285 // valid character at this position
286 cTempChar = ImplPatternChar( cChar, cMask );
287 if ( cTempChar )
289 // use this character
290 aOutStr[i] = cTempChar;
291 nStrIndex++;
293 else
295 // copy if it is a literal character
296 if ( cLiteral == cChar )
297 nStrIndex++;
298 else
300 // If the invalid character might be the next literal character then we jump
301 // ahead to it, otherwise we ignore it. Do only if empty literals are allowed.
302 if ( nFormatFlags & PATTERN_FORMAT_EMPTYLITERALS )
304 n = i;
305 while ( n < rEditMask.getLength() )
307 if ( rEditMask[n] == EDITMASK_LITERAL )
309 if ( ImplCommaPointCharEqual( cChar, rLiteralMask[n] ) )
310 i = n+1;
312 break;
315 n++;
319 nStrIndex++;
320 continue;
325 i++;
328 return aOutStr.makeStringAndClear();
331 static void ImplPatternMaxPos( const OUString& rStr, const OString& rEditMask,
332 sal_uInt16 nFormatFlags, bool bSameMask,
333 sal_Int32 nCursorPos, sal_Int32& rPos )
336 // last position must not be longer than the contained string
337 sal_Int32 nMaxPos = rStr.getLength();
339 // if non empty literals are allowed ignore blanks at the end as well
340 if ( bSameMask && !(nFormatFlags & PATTERN_FORMAT_EMPTYLITERALS) )
342 while ( nMaxPos )
344 if ( (rEditMask[nMaxPos-1] != EDITMASK_LITERAL) &&
345 (rStr[nMaxPos-1] != ' ') )
346 break;
347 nMaxPos--;
350 // if we are in front of a literal, continue search until first character after the literal
351 sal_Int32 nTempPos = nMaxPos;
352 while ( nTempPos < rEditMask.getLength() )
354 if ( rEditMask[nTempPos] != EDITMASK_LITERAL )
356 nMaxPos = nTempPos;
357 break;
359 nTempPos++;
363 if ( rPos > nMaxPos )
364 rPos = nMaxPos;
366 // character should not move left
367 if ( rPos < nCursorPos )
368 rPos = nCursorPos;
371 static void ImplPatternProcessStrictModify( Edit* pEdit,
372 const OString& rEditMask,
373 const OUString& rLiteralMask,
374 sal_uInt16 nFormatFlags, bool bSameMask )
376 OUString aText = pEdit->GetText();
378 // remove leading blanks
379 if ( bSameMask && !(nFormatFlags & PATTERN_FORMAT_EMPTYLITERALS) )
381 sal_Int32 i = 0;
382 sal_Int32 nMaxLen = aText.getLength();
383 while ( i < nMaxLen )
385 if ( (rEditMask[i] != EDITMASK_LITERAL) &&
386 (aText[i] != ' ') )
387 break;
389 i++;
391 // keep all literal characters
392 while ( i && (rEditMask[i] == EDITMASK_LITERAL) )
393 i--;
394 aText = aText.copy( i );
397 OUString aNewText = ImplPatternReformat( aText, rEditMask, rLiteralMask, nFormatFlags );
398 if ( aNewText != aText )
400 // adjust selection such that it remains at the end if it was there before
401 Selection aSel = pEdit->GetSelection();
402 sal_Int64 nMaxSel = std::max( aSel.Min(), aSel.Max() );
403 if ( nMaxSel >= aText.getLength() )
405 sal_Int32 nMaxPos = aNewText.getLength();
406 ImplPatternMaxPos( aNewText, rEditMask, nFormatFlags, bSameMask, nMaxSel, nMaxPos );
407 if ( aSel.Min() == aSel.Max() )
409 aSel.Min() = nMaxPos;
410 aSel.Max() = aSel.Min();
412 else if ( aSel.Min() > aSel.Max() )
413 aSel.Min() = nMaxPos;
414 else
415 aSel.Max() = nMaxPos;
417 pEdit->SetText( aNewText, aSel );
421 static sal_Int32 ImplPatternLeftPos(const OString& rEditMask, sal_Int32 nCursorPos)
423 // search non-literal predecessor
424 sal_Int32 nNewPos = nCursorPos;
425 sal_Int32 nTempPos = nNewPos;
426 while ( nTempPos )
428 if ( rEditMask[nTempPos-1] != EDITMASK_LITERAL )
430 nNewPos = nTempPos-1;
431 break;
433 nTempPos--;
435 return nNewPos;
438 static sal_Int32 ImplPatternRightPos( const OUString& rStr, const OString& rEditMask,
439 sal_uInt16 nFormatFlags, bool bSameMask,
440 sal_Int32 nCursorPos )
442 // search non-literal successor
443 sal_Int32 nNewPos = nCursorPos;
445 for(sal_Int32 nTempPos = nNewPos+1; nTempPos < rEditMask.getLength(); ++nTempPos )
447 if ( rEditMask[nTempPos] != EDITMASK_LITERAL )
449 nNewPos = nTempPos;
450 break;
453 ImplPatternMaxPos( rStr, rEditMask, nFormatFlags, bSameMask, nCursorPos, nNewPos );
454 return nNewPos;
457 static bool ImplPatternProcessKeyInput( Edit* pEdit, const KeyEvent& rKEvt,
458 const OString& rEditMask,
459 const OUString& rLiteralMask,
460 bool bStrictFormat,
461 sal_uInt16 nFormatFlags,
462 bool bSameMask,
463 bool& rbInKeyInput )
465 if ( rEditMask.isEmpty() || !bStrictFormat )
466 return false;
468 Selection aOldSel = pEdit->GetSelection();
469 vcl::KeyCode aCode = rKEvt.GetKeyCode();
470 sal_Unicode cChar = rKEvt.GetCharCode();
471 sal_uInt16 nKeyCode = aCode.GetCode();
472 bool bShift = aCode.IsShift();
473 sal_Int32 nCursorPos = static_cast<sal_Int32>(aOldSel.Max());
474 sal_Int32 nNewPos;
475 sal_Int32 nTempPos;
477 if ( nKeyCode && !aCode.IsMod1() && !aCode.IsMod2() )
479 if ( nKeyCode == KEY_LEFT )
481 Selection aSel( ImplPatternLeftPos( rEditMask, nCursorPos ) );
482 if ( bShift )
483 aSel.Min() = aOldSel.Min();
484 pEdit->SetSelection( aSel );
485 return true;
487 else if ( nKeyCode == KEY_RIGHT )
489 // Use the start of selection as minimum; even a small position is allowed in case that
490 // all was selected by the focus
491 Selection aSel( aOldSel );
492 aSel.Justify();
493 nCursorPos = aSel.Min();
494 aSel.Max() = ImplPatternRightPos( pEdit->GetText(), rEditMask, nFormatFlags, bSameMask, nCursorPos );
495 if ( bShift )
496 aSel.Min() = aOldSel.Min();
497 else
498 aSel.Min() = aSel.Max();
499 pEdit->SetSelection( aSel );
500 return true;
502 else if ( nKeyCode == KEY_HOME )
504 // Home is the position of the first non-literal character
505 nNewPos = 0;
506 while ( (nNewPos < rEditMask.getLength()) &&
507 (rEditMask[nNewPos] == EDITMASK_LITERAL) )
508 nNewPos++;
510 // Home should not move to the right
511 if ( nCursorPos < nNewPos )
512 nNewPos = nCursorPos;
513 Selection aSel( nNewPos );
514 if ( bShift )
515 aSel.Min() = aOldSel.Min();
516 pEdit->SetSelection( aSel );
517 return true;
519 else if ( nKeyCode == KEY_END )
521 // End is position of last non-literal character
522 nNewPos = rEditMask.getLength();
523 while ( nNewPos &&
524 (rEditMask[nNewPos-1] == EDITMASK_LITERAL) )
525 nNewPos--;
526 // Use the start of selection as minimum; even a small position is allowed in case that
527 // all was selected by the focus
528 Selection aSel( aOldSel );
529 aSel.Justify();
530 nCursorPos = static_cast<sal_Int32>(aSel.Min());
531 ImplPatternMaxPos( pEdit->GetText(), rEditMask, nFormatFlags, bSameMask, nCursorPos, nNewPos );
532 aSel.Max() = nNewPos;
533 if ( bShift )
534 aSel.Min() = aOldSel.Min();
535 else
536 aSel.Min() = aSel.Max();
537 pEdit->SetSelection( aSel );
538 return true;
540 else if ( (nKeyCode == KEY_BACKSPACE) || (nKeyCode == KEY_DELETE) )
542 OUString aOldStr( pEdit->GetText() );
543 OUStringBuffer aStr( aOldStr );
544 Selection aSel = aOldSel;
546 aSel.Justify();
547 nNewPos = static_cast<sal_Int32>(aSel.Min());
549 // if selection then delete it
550 if ( aSel.Len() )
552 if ( bSameMask )
553 aStr.remove( static_cast<sal_Int32>(aSel.Min()), static_cast<sal_Int32>(aSel.Len()) );
554 else
556 OUString aRep = rLiteralMask.copy( static_cast<sal_Int32>(aSel.Min()), static_cast<sal_Int32>(aSel.Len()) );
557 aStr.remove( aSel.Min(), aRep.getLength() );
558 aStr.insert( aSel.Min(), aRep );
561 else
563 if ( nKeyCode == KEY_BACKSPACE )
565 nTempPos = nNewPos;
566 nNewPos = ImplPatternLeftPos( rEditMask, nTempPos );
568 else
569 nTempPos = ImplPatternRightPos( aStr.toString(), rEditMask, nFormatFlags, bSameMask, nNewPos );
571 if ( nNewPos != nTempPos )
573 if ( bSameMask )
575 if ( rEditMask[nNewPos] != EDITMASK_LITERAL )
576 aStr.remove( nNewPos, 1 );
578 else
580 aStr[nNewPos] = rLiteralMask[nNewPos];
585 if ( aOldStr != aStr.toString() )
587 if ( bSameMask )
588 aStr = ImplPatternReformat( aStr.toString(), rEditMask, rLiteralMask, nFormatFlags );
589 rbInKeyInput = true;
590 pEdit->SetText( aStr.toString(), Selection( nNewPos ) );
591 pEdit->SetModifyFlag();
592 pEdit->Modify();
593 rbInKeyInput = false;
595 else
596 pEdit->SetSelection( Selection( nNewPos ) );
598 return true;
600 else if ( nKeyCode == KEY_INSERT )
602 // you can only set InsertModus for a PatternField if the
603 // mask is equal at all input positions
604 if ( !bSameMask )
606 return true;
611 if ( rKEvt.GetKeyCode().IsMod2() || (cChar < 32) || (cChar == 127) )
612 return false;
614 Selection aSel = aOldSel;
615 aSel.Justify();
616 nNewPos = aSel.Min();
618 if ( nNewPos < rEditMask.getLength() )
620 sal_Unicode cPattChar = ImplPatternChar( cChar, rEditMask[nNewPos] );
621 if ( cPattChar )
622 cChar = cPattChar;
623 else
625 // If no valid character, check if the user wanted to jump to next literal. We do this
626 // only if we're after a character, so that literals that were skipped automatically
627 // do not influence the position anymore.
628 if ( nNewPos &&
629 (rEditMask[nNewPos-1] != EDITMASK_LITERAL) &&
630 !aSel.Len() )
632 // search for next character not being a literal
633 nTempPos = nNewPos;
634 while ( nTempPos < rEditMask.getLength() )
636 if ( rEditMask[nTempPos] == EDITMASK_LITERAL )
638 // only valid if no literal present
639 if ( (rEditMask[nTempPos+1] != EDITMASK_LITERAL ) &&
640 ImplCommaPointCharEqual( cChar, rLiteralMask[nTempPos] ) )
642 nTempPos++;
643 ImplPatternMaxPos( pEdit->GetText(), rEditMask, nFormatFlags, bSameMask, nNewPos, nTempPos );
644 if ( nTempPos > nNewPos )
646 pEdit->SetSelection( Selection( nTempPos ) );
647 return true;
650 break;
652 nTempPos++;
656 cChar = 0;
659 else
660 cChar = 0;
661 if ( cChar )
663 OUStringBuffer aStr = pEdit->GetText();
664 bool bError = false;
665 if ( bSameMask && pEdit->IsInsertMode() )
667 // crop spaces and literals at the end until current position
668 sal_Int32 n = aStr.getLength();
669 while ( n && (n > nNewPos) )
671 if ( (aStr[n-1] != ' ') &&
672 ((n > rEditMask.getLength()) || (rEditMask[n-1] != EDITMASK_LITERAL)) )
673 break;
675 n--;
677 aStr.truncate( n );
679 if ( aSel.Len() )
680 aStr.remove( aSel.Min(), aSel.Len() );
682 if ( aStr.getLength() < rEditMask.getLength() )
684 // possibly extend string until cursor position
685 if ( aStr.getLength() < nNewPos )
686 aStr.append( rLiteralMask.copy( aStr.getLength(), nNewPos-aStr.getLength() ));
687 if ( nNewPos < aStr.getLength() )
688 aStr.insert( cChar, nNewPos );
689 else if ( nNewPos < rEditMask.getLength() )
690 aStr.append(cChar);
691 aStr = ImplPatternReformat( aStr.toString(), rEditMask, rLiteralMask, nFormatFlags );
693 else
694 bError = true;
696 else
698 if ( aSel.Len() )
700 // delete selection
701 OUString aRep = rLiteralMask.copy( aSel.Min(), aSel.Len() );
702 aStr.remove( aSel.Min(), aRep.getLength() );
703 aStr.insert( aSel.Min(), aRep );
706 if ( nNewPos < aStr.getLength() )
707 aStr[nNewPos] = cChar;
708 else if ( nNewPos < rEditMask.getLength() )
709 aStr.append(cChar);
712 if ( !bError )
714 rbInKeyInput = true;
715 Selection aNewSel( ImplPatternRightPos( aStr.toString(), rEditMask, nFormatFlags, bSameMask, nNewPos ) );
716 pEdit->SetText( aStr.toString(), aNewSel );
717 pEdit->SetModifyFlag();
718 pEdit->Modify();
719 rbInKeyInput = false;
723 return true;
726 void PatternFormatter::ImplSetMask(const OString& rEditMask, const OUString& rLiteralMask)
728 m_aEditMask = rEditMask;
729 maLiteralMask = rLiteralMask;
730 mbSameMask = true;
732 if ( m_aEditMask.getLength() != maLiteralMask.getLength() )
734 OUStringBuffer aBuf(maLiteralMask);
735 if (m_aEditMask.getLength() < aBuf.getLength())
736 aBuf.remove(m_aEditMask.getLength(), aBuf.getLength() - m_aEditMask.getLength());
737 else
738 comphelper::string::padToLength(aBuf, m_aEditMask.getLength(), ' ');
739 maLiteralMask = aBuf.makeStringAndClear();
742 // Strict mode allows only the input mode if only equal characters are allowed as mask and if
743 // only spaces are specified which are not allowed by the mask
744 sal_Int32 i = 0;
745 sal_Char c = 0;
746 while ( i < rEditMask.getLength() )
748 sal_Char cTemp = rEditMask[i];
749 if ( cTemp != EDITMASK_LITERAL )
751 if ( (cTemp == EDITMASK_ALLCHAR) ||
752 (cTemp == EDITMASK_UPPERALLCHAR) ||
753 (cTemp == EDITMASK_NUMSPACE) )
755 mbSameMask = false;
756 break;
758 if ( i < rLiteralMask.getLength() )
760 if ( rLiteralMask[i] != ' ' )
762 mbSameMask = false;
763 break;
766 if ( !c )
767 c = cTemp;
768 if ( cTemp != c )
770 mbSameMask = false;
771 break;
774 i++;
778 PatternFormatter::PatternFormatter()
780 mbSameMask = true;
781 mbInPattKeyInput = false;
784 PatternFormatter::~PatternFormatter()
788 void PatternFormatter::SetMask( const OString& rEditMask,
789 const OUString& rLiteralMask )
791 ImplSetMask( rEditMask, rLiteralMask );
792 ReformatAll();
795 void PatternFormatter::SetString( const OUString& rStr )
797 maFieldString = rStr;
798 if ( GetField() )
800 GetField()->SetText( rStr );
801 MarkToBeReformatted( false );
805 OUString PatternFormatter::GetString() const
807 if ( !GetField() )
808 return OUString();
809 else
810 return ImplPatternReformat( GetField()->GetText(), m_aEditMask, maLiteralMask, 0/*nFormatFlags*/ );
813 void PatternFormatter::Reformat()
815 if ( GetField() )
817 ImplSetText( ImplPatternReformat( GetField()->GetText(), m_aEditMask, maLiteralMask, 0/*nFormatFlags*/ ) );
818 if ( !mbSameMask && IsStrictFormat() && !GetField()->IsReadOnly() )
819 GetField()->SetInsertMode( false );
823 PatternField::PatternField( vcl::Window* pParent, WinBits nWinStyle ) :
824 SpinField( pParent, nWinStyle )
826 SetField( this );
827 Reformat();
830 void PatternField::dispose()
832 PatternFormatter::SetField( nullptr );
833 SpinField::dispose();
836 bool PatternField::PreNotify( NotifyEvent& rNEvt )
838 if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
840 if ( ImplPatternProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), GetEditMask(), GetLiteralMask(),
841 IsStrictFormat(), 0/*nFormatFlags*/,
842 ImplIsSameMask(), ImplGetInPattKeyInput() ) )
843 return true;
846 return SpinField::PreNotify( rNEvt );
849 bool PatternField::EventNotify( NotifyEvent& rNEvt )
851 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
852 MarkToBeReformatted( false );
853 else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
855 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
856 Reformat();
859 return SpinField::EventNotify( rNEvt );
862 void PatternField::Modify()
864 if ( !ImplGetInPattKeyInput() )
866 if ( IsStrictFormat() )
867 ImplPatternProcessStrictModify( GetField(), GetEditMask(), GetLiteralMask(), 0/*nFormatFlags*/, ImplIsSameMask() );
868 else
869 MarkToBeReformatted( true );
872 SpinField::Modify();
875 PatternBox::PatternBox( vcl::Window* pParent, WinBits nWinStyle ) :
876 ComboBox( pParent, nWinStyle )
878 SetField( this );
879 Reformat();
882 void PatternBox::dispose()
884 PatternFormatter::SetField( nullptr );
885 ComboBox::dispose();
888 bool PatternBox::PreNotify( NotifyEvent& rNEvt )
890 if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
892 if ( ImplPatternProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), GetEditMask(), GetLiteralMask(),
893 IsStrictFormat(), 0/*nFormatFlags*/,
894 ImplIsSameMask(), ImplGetInPattKeyInput() ) )
895 return true;
898 return ComboBox::PreNotify( rNEvt );
901 bool PatternBox::EventNotify( NotifyEvent& rNEvt )
903 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
904 MarkToBeReformatted( false );
905 else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
907 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
908 Reformat();
911 return ComboBox::EventNotify( rNEvt );
914 void PatternBox::Modify()
916 if ( !ImplGetInPattKeyInput() )
918 if ( IsStrictFormat() )
919 ImplPatternProcessStrictModify( GetField(), GetEditMask(), GetLiteralMask(), 0/*nFormatFlags*/, ImplIsSameMask() );
920 else
921 MarkToBeReformatted( true );
924 ComboBox::Modify();
927 void PatternBox::ReformatAll()
929 OUString aStr;
930 SetUpdateMode( false );
931 const sal_Int32 nEntryCount = GetEntryCount();
932 for ( sal_Int32 i=0; i < nEntryCount; ++i )
934 aStr = ImplPatternReformat( GetEntry( i ), GetEditMask(), GetLiteralMask(), 0/*nFormatFlags*/ );
935 RemoveEntryAt(i);
936 InsertEntry( aStr, i );
938 PatternFormatter::Reformat();
939 SetUpdateMode( true );
942 static ExtDateFieldFormat ImplGetExtFormat( DateFormat eOld )
944 switch( eOld )
946 case DMY: return ExtDateFieldFormat::ShortDDMMYY;
947 case MDY: return ExtDateFieldFormat::ShortMMDDYY;
948 default: return ExtDateFieldFormat::ShortYYMMDD;
952 static sal_uInt16 ImplCutNumberFromString( OUString& rStr )
954 sal_Int32 i1 = 0;
955 while (i1 != rStr.getLength() && !(rStr[i1] >= '0' && rStr[i1] <= '9')) {
956 ++i1;
958 sal_Int32 i2 = i1;
959 while (i2 != rStr.getLength() && rStr[i2] >= '0' && rStr[i2] <= '9') {
960 ++i2;
962 sal_Int32 nValue = rStr.copy(i1, i2-i1).toInt32();
963 rStr = rStr.copy(std::min(i2+1, rStr.getLength()));
964 return nValue;
967 static bool ImplCutMonthName( OUString& rStr, const OUString& _rLookupMonthName )
969 sal_Int32 index = 0;
970 rStr = rStr.replaceFirst(_rLookupMonthName, "", &index);
971 return index >= 0;
974 static sal_uInt16 ImplCutMonthFromString( OUString& rStr, const CalendarWrapper& rCalendarWrapper )
976 // search for a month' name
977 for ( sal_uInt16 i=1; i <= 12; i++ )
979 OUString aMonthName = rCalendarWrapper.getMonths()[i-1].FullName;
980 // long month name?
981 if ( ImplCutMonthName( rStr, aMonthName ) )
982 return i;
984 // short month name?
985 OUString aAbbrevMonthName = rCalendarWrapper.getMonths()[i-1].AbbrevName;
986 if ( ImplCutMonthName( rStr, aAbbrevMonthName ) )
987 return i;
990 return ImplCutNumberFromString( rStr );
993 static OUString ImplGetDateSep( const LocaleDataWrapper& rLocaleDataWrapper, ExtDateFieldFormat eFormat )
995 if ( ( eFormat == ExtDateFieldFormat::ShortYYMMDD_DIN5008 ) || ( eFormat == ExtDateFieldFormat::ShortYYYYMMDD_DIN5008 ) )
996 return OUString("-");
997 else
998 return rLocaleDataWrapper.getDateSep();
1001 static bool ImplDateProcessKeyInput( Edit*, const KeyEvent& rKEvt, ExtDateFieldFormat eFormat,
1002 const LocaleDataWrapper& rLocaleDataWrapper )
1004 sal_Unicode cChar = rKEvt.GetCharCode();
1005 sal_uInt16 nGroup = rKEvt.GetKeyCode().GetGroup();
1006 if ( (nGroup == KEYGROUP_FKEYS) || (nGroup == KEYGROUP_CURSOR) ||
1007 (nGroup == KEYGROUP_MISC)||
1008 ((cChar >= '0') && (cChar <= '9')) ||
1009 (cChar == ImplGetDateSep( rLocaleDataWrapper, eFormat )[0]) )
1010 return false;
1011 else
1012 return true;
1015 static bool ImplDateGetValue( const OUString& rStr, Date& rDate, ExtDateFieldFormat eDateFormat,
1016 const LocaleDataWrapper& rLocaleDataWrapper, const CalendarWrapper& rCalendarWrapper,
1017 const AllSettings& )
1019 sal_uInt16 nDay = 0;
1020 sal_uInt16 nMonth = 0;
1021 sal_uInt16 nYear = 0;
1022 bool bError = false;
1023 OUString aStr( rStr );
1025 if ( eDateFormat == ExtDateFieldFormat::SystemLong )
1027 DateFormat eFormat = rLocaleDataWrapper.getLongDateFormat();
1028 switch( eFormat )
1030 case MDY:
1031 nMonth = ImplCutMonthFromString( aStr, rCalendarWrapper );
1032 nDay = ImplCutNumberFromString( aStr );
1033 nYear = ImplCutNumberFromString( aStr );
1034 break;
1035 case DMY:
1036 nDay = ImplCutNumberFromString( aStr );
1037 nMonth = ImplCutMonthFromString( aStr, rCalendarWrapper );
1038 nYear = ImplCutNumberFromString( aStr );
1039 break;
1040 case YMD:
1041 default:
1042 nYear = ImplCutNumberFromString( aStr );
1043 nMonth = ImplCutMonthFromString( aStr, rCalendarWrapper );
1044 nDay = ImplCutNumberFromString( aStr );
1045 break;
1048 else
1050 bool bYear = true;
1052 // Check if year is present:
1053 OUString aDateSep = ImplGetDateSep( rLocaleDataWrapper, eDateFormat );
1054 sal_Int32 nSepPos = aStr.indexOf( aDateSep );
1055 if ( nSepPos < 0 )
1056 return false;
1057 nSepPos = aStr.indexOf( aDateSep, nSepPos+1 );
1058 if ( ( nSepPos < 0 ) || ( nSepPos == (aStr.getLength()-1) ) )
1060 bYear = false;
1061 nYear = Date( Date::SYSTEM ).GetYearUnsigned();
1064 const sal_Unicode* pBuf = aStr.getStr();
1065 ImplSkipDelimiters( pBuf );
1067 switch ( eDateFormat )
1069 case ExtDateFieldFormat::ShortDDMMYY:
1070 case ExtDateFieldFormat::ShortDDMMYYYY:
1072 nDay = ImplGetNum( pBuf, bError );
1073 ImplSkipDelimiters( pBuf );
1074 nMonth = ImplGetNum( pBuf, bError );
1075 ImplSkipDelimiters( pBuf );
1076 if ( bYear )
1077 nYear = ImplGetNum( pBuf, bError );
1079 break;
1080 case ExtDateFieldFormat::ShortMMDDYY:
1081 case ExtDateFieldFormat::ShortMMDDYYYY:
1083 nMonth = ImplGetNum( pBuf, bError );
1084 ImplSkipDelimiters( pBuf );
1085 nDay = ImplGetNum( pBuf, bError );
1086 ImplSkipDelimiters( pBuf );
1087 if ( bYear )
1088 nYear = ImplGetNum( pBuf, bError );
1090 break;
1091 case ExtDateFieldFormat::ShortYYMMDD:
1092 case ExtDateFieldFormat::ShortYYYYMMDD:
1093 case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
1094 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1096 if ( bYear )
1097 nYear = ImplGetNum( pBuf, bError );
1098 ImplSkipDelimiters( pBuf );
1099 nMonth = ImplGetNum( pBuf, bError );
1100 ImplSkipDelimiters( pBuf );
1101 nDay = ImplGetNum( pBuf, bError );
1103 break;
1105 default:
1107 OSL_FAIL( "DateFormat???" );
1112 if ( bError || !nDay || !nMonth )
1113 return false;
1115 Date aNewDate( nDay, nMonth, nYear );
1116 DateFormatter::ExpandCentury( aNewDate, utl::MiscCfg().GetYear2000() );
1117 if ( aNewDate.IsValidDate() )
1119 rDate = aNewDate;
1120 return true;
1122 return false;
1125 bool DateFormatter::ImplDateReformat( const OUString& rStr, OUString& rOutStr, const AllSettings& rSettings )
1127 Date aDate( Date::EMPTY );
1128 if ( !ImplDateGetValue( rStr, aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper(), GetFieldSettings() ) )
1129 return true;
1131 Date aTempDate = aDate;
1132 if ( aTempDate > GetMax() )
1133 aTempDate = GetMax();
1134 else if ( aTempDate < GetMin() )
1135 aTempDate = GetMin();
1137 rOutStr = ImplGetDateAsText( aTempDate, rSettings );
1139 return true;
1142 OUString DateFormatter::ImplGetDateAsText( const Date& rDate,
1143 const AllSettings& ) const
1145 bool bShowCentury = false;
1146 switch ( GetExtDateFormat() )
1148 case ExtDateFieldFormat::SystemShortYYYY:
1149 case ExtDateFieldFormat::SystemLong:
1150 case ExtDateFieldFormat::ShortDDMMYYYY:
1151 case ExtDateFieldFormat::ShortMMDDYYYY:
1152 case ExtDateFieldFormat::ShortYYYYMMDD:
1153 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1155 bShowCentury = true;
1157 break;
1158 default:
1160 bShowCentury = false;
1164 if ( !bShowCentury )
1166 // Check if I have to use force showing the century
1167 sal_uInt16 nTwoDigitYearStart = utl::MiscCfg().GetYear2000();
1168 sal_uInt16 nYear = rDate.GetYearUnsigned();
1170 // If year is not in double digit range
1171 if ( (nYear < nTwoDigitYearStart) || (nYear >= nTwoDigitYearStart+100) )
1172 bShowCentury = true;
1175 sal_Unicode aBuf[128];
1176 sal_Unicode* pBuf = aBuf;
1178 OUString aDateSep = ImplGetDateSep( ImplGetLocaleDataWrapper(), GetExtDateFormat( true ) );
1179 sal_uInt16 nDay = rDate.GetDay();
1180 sal_uInt16 nMonth = rDate.GetMonth();
1181 sal_Int16 nYear = rDate.GetYear();
1182 sal_uInt16 nYearLen = bShowCentury ? 4 : 2;
1184 if ( !bShowCentury )
1185 nYear %= 100;
1187 switch ( GetExtDateFormat( true ) )
1189 case ExtDateFieldFormat::SystemLong:
1191 return ImplGetLocaleDataWrapper().getLongDate( rDate, GetCalendarWrapper(), !bShowCentury );
1193 case ExtDateFieldFormat::ShortDDMMYY:
1194 case ExtDateFieldFormat::ShortDDMMYYYY:
1196 pBuf = ImplAddNum( pBuf, nDay, 2 );
1197 pBuf = ImplAddString( pBuf, aDateSep );
1198 pBuf = ImplAddNum( pBuf, nMonth, 2 );
1199 pBuf = ImplAddString( pBuf, aDateSep );
1200 pBuf = ImplAddSNum( pBuf, nYear, nYearLen );
1202 break;
1203 case ExtDateFieldFormat::ShortMMDDYY:
1204 case ExtDateFieldFormat::ShortMMDDYYYY:
1206 pBuf = ImplAddNum( pBuf, nMonth, 2 );
1207 pBuf = ImplAddString( pBuf, aDateSep );
1208 pBuf = ImplAddNum( pBuf, nDay, 2 );
1209 pBuf = ImplAddString( pBuf, aDateSep );
1210 pBuf = ImplAddSNum( pBuf, nYear, nYearLen );
1212 break;
1213 case ExtDateFieldFormat::ShortYYMMDD:
1214 case ExtDateFieldFormat::ShortYYYYMMDD:
1215 case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
1216 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1218 pBuf = ImplAddSNum( pBuf, nYear, nYearLen );
1219 pBuf = ImplAddString( pBuf, aDateSep );
1220 pBuf = ImplAddNum( pBuf, nMonth, 2 );
1221 pBuf = ImplAddString( pBuf, aDateSep );
1222 pBuf = ImplAddNum( pBuf, nDay, 2 );
1224 break;
1225 default:
1227 OSL_FAIL( "DateFormat???" );
1231 return OUString(aBuf, pBuf-aBuf);
1234 static void ImplDateIncrementDay( Date& rDate, bool bUp )
1236 DateFormatter::ExpandCentury( rDate );
1238 if ( bUp )
1240 if ( (rDate.GetDay() != 31) || (rDate.GetMonth() != 12) || (rDate.GetYear() != SAL_MAX_INT16) )
1241 ++rDate;
1243 else
1245 if ( (rDate.GetDay() != 1 ) || (rDate.GetMonth() != 1) || (rDate.GetYear() != SAL_MIN_INT16) )
1246 --rDate;
1250 static void ImplDateIncrementMonth( Date& rDate, bool bUp )
1252 DateFormatter::ExpandCentury( rDate );
1254 sal_uInt16 nMonth = rDate.GetMonth();
1255 sal_Int16 nYear = rDate.GetYear();
1256 if ( bUp )
1258 if ( (nMonth == 12) && (nYear < SAL_MAX_INT16) )
1260 rDate.SetMonth( 1 );
1261 rDate.SetYear( rDate.GetNextYear() );
1263 else
1265 if ( nMonth < 12 )
1266 rDate.SetMonth( nMonth + 1 );
1269 else
1271 if ( (nMonth == 1) && (nYear > SAL_MIN_INT16) )
1273 rDate.SetMonth( 12 );
1274 rDate.SetYear( rDate.GetPrevYear() );
1276 else
1278 if ( nMonth > 1 )
1279 rDate.SetMonth( nMonth - 1 );
1283 sal_uInt16 nDaysInMonth = Date::GetDaysInMonth( rDate.GetMonth(), rDate.GetYear());
1284 if ( rDate.GetDay() > nDaysInMonth )
1285 rDate.SetDay( nDaysInMonth );
1288 static void ImplDateIncrementYear( Date& rDate, bool bUp )
1290 DateFormatter::ExpandCentury( rDate );
1292 sal_Int16 nYear = rDate.GetYear();
1293 sal_uInt16 nMonth = rDate.GetMonth();
1294 if ( bUp )
1296 if ( nYear < SAL_MAX_INT16 )
1297 rDate.SetYear( rDate.GetNextYear() );
1299 else
1301 if ( nYear > SAL_MIN_INT16 )
1302 rDate.SetYear( rDate.GetPrevYear() );
1304 if (nMonth == 2)
1306 // Handle February 29 from leap year to non-leap year.
1307 sal_uInt16 nDay = rDate.GetDay();
1308 if (nDay > 28)
1310 // The check would not be necessary if it was guaranteed that the
1311 // date was valid before and actually was a leap year,
1312 // de-/incrementing a leap year with 29 always results in 28.
1313 sal_uInt16 nDaysInMonth = Date::GetDaysInMonth( nMonth, rDate.GetYear());
1314 if (nDay > nDaysInMonth)
1315 rDate.SetDay( nDaysInMonth);
1320 bool DateFormatter::ImplAllowMalformedInput() const
1322 return !IsEnforceValidValue();
1325 void DateField::ImplDateSpinArea( bool bUp )
1327 // increment days if all is selected
1328 if ( GetField() )
1330 Date aDate( GetDate() );
1331 Selection aSelection = GetField()->GetSelection();
1332 aSelection.Justify();
1333 OUString aText( GetText() );
1334 if ( (sal_Int32)aSelection.Len() == aText.getLength() )
1335 ImplDateIncrementDay( aDate, bUp );
1336 else
1338 sal_Int8 nDateArea = 0;
1340 ExtDateFieldFormat eFormat = GetExtDateFormat( true );
1341 if ( eFormat == ExtDateFieldFormat::SystemLong )
1343 eFormat = ImplGetExtFormat( ImplGetLocaleDataWrapper().getLongDateFormat() );
1344 nDateArea = 1;
1346 else
1348 // search area
1349 sal_Int32 nPos = 0;
1350 OUString aDateSep = ImplGetDateSep( ImplGetLocaleDataWrapper(), eFormat );
1351 for ( sal_Int8 i = 1; i <= 3; i++ )
1353 nPos = aText.indexOf( aDateSep, nPos );
1354 if (nPos < 0 || nPos >= (sal_Int32)aSelection.Max())
1356 nDateArea = i;
1357 break;
1359 else
1360 nPos++;
1364 switch( eFormat )
1366 case ExtDateFieldFormat::ShortMMDDYY:
1367 case ExtDateFieldFormat::ShortMMDDYYYY:
1368 switch( nDateArea )
1370 case 1: ImplDateIncrementMonth( aDate, bUp );
1371 break;
1372 case 2: ImplDateIncrementDay( aDate, bUp );
1373 break;
1374 case 3: ImplDateIncrementYear( aDate, bUp );
1375 break;
1377 break;
1378 case ExtDateFieldFormat::ShortDDMMYY:
1379 case ExtDateFieldFormat::ShortDDMMYYYY:
1380 switch( nDateArea )
1382 case 1: ImplDateIncrementDay( aDate, bUp );
1383 break;
1384 case 2: ImplDateIncrementMonth( aDate, bUp );
1385 break;
1386 case 3: ImplDateIncrementYear( aDate, bUp );
1387 break;
1389 break;
1390 case ExtDateFieldFormat::ShortYYMMDD:
1391 case ExtDateFieldFormat::ShortYYYYMMDD:
1392 case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
1393 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1394 switch( nDateArea )
1396 case 1: ImplDateIncrementYear( aDate, bUp );
1397 break;
1398 case 2: ImplDateIncrementMonth( aDate, bUp );
1399 break;
1400 case 3: ImplDateIncrementDay( aDate, bUp );
1401 break;
1403 break;
1404 default:
1405 OSL_FAIL( "invalid conversion" );
1406 break;
1410 ImplNewFieldValue( aDate );
1414 void DateFormatter::ImplInit()
1416 mbLongFormat = false;
1417 mbShowDateCentury = true;
1418 mpCalendarWrapper = nullptr;
1419 mnDateFormat = 0xFFFF;
1420 mnExtDateFormat = ExtDateFieldFormat::SystemShort;
1423 DateFormatter::DateFormatter() :
1424 maFieldDate( 0 ),
1425 maLastDate( 0 ),
1426 maMin( 1, 1, 1900 ),
1427 maMax( 31, 12, 2200 ),
1428 mbEnforceValidValue( true )
1430 ImplInit();
1433 DateFormatter::~DateFormatter()
1435 delete mpCalendarWrapper;
1436 mpCalendarWrapper = nullptr;
1439 void DateFormatter::SetLocale( const css::lang::Locale& rLocale )
1441 delete mpCalendarWrapper;
1442 mpCalendarWrapper = nullptr;
1443 FormatterBase::SetLocale( rLocale );
1446 CalendarWrapper& DateFormatter::GetCalendarWrapper() const
1448 if ( !mpCalendarWrapper )
1450 const_cast<DateFormatter*>(this)->mpCalendarWrapper = new CalendarWrapper( comphelper::getProcessComponentContext() );
1451 mpCalendarWrapper->loadDefaultCalendar( GetLocale() );
1454 return *mpCalendarWrapper;
1457 void DateFormatter::SetExtDateFormat( ExtDateFieldFormat eFormat )
1459 mnExtDateFormat = eFormat;
1460 ReformatAll();
1463 ExtDateFieldFormat DateFormatter::GetExtDateFormat( bool bResolveSystemFormat ) const
1465 ExtDateFieldFormat eDateFormat = (ExtDateFieldFormat)mnExtDateFormat;
1467 if ( bResolveSystemFormat && ( eDateFormat <= ExtDateFieldFormat::SystemShortYYYY ) )
1469 bool bShowCentury = (eDateFormat == ExtDateFieldFormat::SystemShortYYYY);
1470 switch ( ImplGetLocaleDataWrapper().getDateFormat() )
1472 case DMY: eDateFormat = bShowCentury ? ExtDateFieldFormat::ShortDDMMYYYY : ExtDateFieldFormat::ShortDDMMYY;
1473 break;
1474 case MDY: eDateFormat = bShowCentury ? ExtDateFieldFormat::ShortMMDDYYYY : ExtDateFieldFormat::ShortMMDDYY;
1475 break;
1476 default: eDateFormat = bShowCentury ? ExtDateFieldFormat::ShortYYYYMMDD : ExtDateFieldFormat::ShortYYMMDD;
1481 return eDateFormat;
1484 void DateFormatter::ReformatAll()
1486 Reformat();
1489 void DateFormatter::SetMin( const Date& rNewMin )
1491 maMin = rNewMin;
1492 if ( !IsEmptyFieldValue() )
1493 ReformatAll();
1496 void DateFormatter::SetMax( const Date& rNewMax )
1498 maMax = rNewMax;
1499 if ( !IsEmptyFieldValue() )
1500 ReformatAll();
1503 void DateFormatter::SetLongFormat( bool bLong )
1505 mbLongFormat = bLong;
1507 // #91913# Remove LongFormat and DateShowCentury - redundant
1508 if ( bLong )
1510 SetExtDateFormat( ExtDateFieldFormat::SystemLong );
1512 else
1514 if( mnExtDateFormat == ExtDateFieldFormat::SystemLong )
1515 SetExtDateFormat( ExtDateFieldFormat::SystemShort );
1518 ReformatAll();
1521 void DateFormatter::SetShowDateCentury( bool bShowDateCentury )
1523 mbShowDateCentury = bShowDateCentury;
1525 // #91913# Remove LongFormat and DateShowCentury - redundant
1526 if ( bShowDateCentury )
1528 switch ( GetExtDateFormat() )
1530 case ExtDateFieldFormat::SystemShort:
1531 case ExtDateFieldFormat::SystemShortYY:
1532 SetExtDateFormat( ExtDateFieldFormat::SystemShortYYYY ); break;
1533 case ExtDateFieldFormat::ShortDDMMYY:
1534 SetExtDateFormat( ExtDateFieldFormat::ShortDDMMYYYY ); break;
1535 case ExtDateFieldFormat::ShortMMDDYY:
1536 SetExtDateFormat( ExtDateFieldFormat::ShortMMDDYYYY ); break;
1537 case ExtDateFieldFormat::ShortYYMMDD:
1538 SetExtDateFormat( ExtDateFieldFormat::ShortYYYYMMDD ); break;
1539 case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
1540 SetExtDateFormat( ExtDateFieldFormat::ShortYYYYMMDD_DIN5008 ); break;
1541 default:
1545 else
1547 switch ( GetExtDateFormat() )
1549 case ExtDateFieldFormat::SystemShort:
1550 case ExtDateFieldFormat::SystemShortYYYY:
1551 SetExtDateFormat( ExtDateFieldFormat::SystemShortYY ); break;
1552 case ExtDateFieldFormat::ShortDDMMYYYY:
1553 SetExtDateFormat( ExtDateFieldFormat::ShortDDMMYY ); break;
1554 case ExtDateFieldFormat::ShortMMDDYYYY:
1555 SetExtDateFormat( ExtDateFieldFormat::ShortMMDDYY ); break;
1556 case ExtDateFieldFormat::ShortYYYYMMDD:
1557 SetExtDateFormat( ExtDateFieldFormat::ShortYYMMDD ); break;
1558 case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
1559 SetExtDateFormat( ExtDateFieldFormat::ShortYYMMDD_DIN5008 ); break;
1560 default:
1565 ReformatAll();
1568 void DateFormatter::SetDate( const Date& rNewDate )
1570 ImplSetUserDate( rNewDate );
1571 maFieldDate = maLastDate;
1572 maLastDate = GetDate();
1575 void DateFormatter::ImplSetUserDate( const Date& rNewDate, Selection* pNewSelection )
1577 Date aNewDate = rNewDate;
1578 if ( aNewDate > maMax )
1579 aNewDate = maMax;
1580 else if ( aNewDate < maMin )
1581 aNewDate = maMin;
1582 maLastDate = aNewDate;
1584 if ( GetField() )
1585 ImplSetText( ImplGetDateAsText( aNewDate, GetFieldSettings() ), pNewSelection );
1588 void DateFormatter::ImplNewFieldValue( const Date& rDate )
1590 if ( GetField() )
1592 Selection aSelection = GetField()->GetSelection();
1593 aSelection.Justify();
1594 OUString aText = GetField()->GetText();
1596 // If selected until the end then keep it that way
1597 if ( (sal_Int32)aSelection.Max() == aText.getLength() )
1599 if ( !aSelection.Len() )
1600 aSelection.Min() = SELECTION_MAX;
1601 aSelection.Max() = SELECTION_MAX;
1604 Date aOldLastDate = maLastDate;
1605 ImplSetUserDate( rDate, &aSelection );
1606 maLastDate = aOldLastDate;
1608 // Modify at Edit is only set at KeyInput
1609 if ( GetField()->GetText() != aText )
1611 GetField()->SetModifyFlag();
1612 GetField()->Modify();
1617 Date DateFormatter::GetDate() const
1619 Date aDate( Date::EMPTY );
1621 if ( GetField() )
1623 if ( ImplDateGetValue( GetField()->GetText(), aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper(), GetFieldSettings() ) )
1625 if ( aDate > maMax )
1626 aDate = maMax;
1627 else if ( aDate < maMin )
1628 aDate = maMin;
1630 else
1632 // !!! We should find out why dates are treated differently than other fields (see
1633 // also bug: 52384)
1635 if ( !ImplAllowMalformedInput() )
1637 if ( maLastDate.GetDate() )
1638 aDate = maLastDate;
1639 else if ( !IsEmptyFieldValueEnabled() )
1640 aDate = Date( Date::SYSTEM );
1642 else
1643 aDate = Date( Date::EMPTY ); // set invalid date
1647 return aDate;
1650 void DateFormatter::SetEmptyDate()
1652 FormatterBase::SetEmptyFieldValue();
1655 bool DateFormatter::IsEmptyDate() const
1657 bool bEmpty = FormatterBase::IsEmptyFieldValue();
1659 if ( GetField() && MustBeReformatted() && IsEmptyFieldValueEnabled() )
1661 if ( GetField()->GetText().isEmpty() )
1663 bEmpty = true;
1665 else if ( !maLastDate.GetDate() )
1667 Date aDate( Date::EMPTY );
1668 bEmpty = !ImplDateGetValue( GetField()->GetText(), aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper(), GetFieldSettings() );
1671 return bEmpty;
1674 void DateFormatter::Reformat()
1676 if ( !GetField() )
1677 return;
1679 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
1680 return;
1682 OUString aStr;
1683 bool bOK = ImplDateReformat( GetField()->GetText(), aStr, GetFieldSettings() );
1684 if( !bOK )
1685 return;
1687 if ( !aStr.isEmpty() )
1689 ImplSetText( aStr );
1690 ImplDateGetValue( aStr, maLastDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper(), GetFieldSettings() );
1692 else
1694 if ( maLastDate.GetDate() )
1695 SetDate( maLastDate );
1696 else if ( !IsEmptyFieldValueEnabled() )
1697 SetDate( Date( Date::SYSTEM ) );
1698 else
1700 ImplSetText( OUString() );
1701 SetEmptyFieldValueData( true );
1706 void DateFormatter::ExpandCentury( Date& rDate )
1708 ExpandCentury( rDate, utl::MiscCfg().GetYear2000() );
1711 void DateFormatter::ExpandCentury( Date& rDate, sal_uInt16 nTwoDigitYearStart )
1713 sal_Int16 nDateYear = rDate.GetYear();
1714 if ( 0 <= nDateYear && nDateYear < 100 )
1716 sal_uInt16 nCentury = nTwoDigitYearStart / 100;
1717 if ( nDateYear < (nTwoDigitYearStart % 100) )
1718 nCentury++;
1719 rDate.SetYear( nDateYear + (nCentury*100) );
1723 DateField::DateField( vcl::Window* pParent, WinBits nWinStyle ) :
1724 SpinField( pParent, nWinStyle ),
1725 maFirst( GetMin() ),
1726 maLast( GetMax() )
1728 SetField( this );
1729 SetText( ImplGetLocaleDataWrapper().getDate( ImplGetFieldDate() ) );
1730 Reformat();
1731 ResetLastDate();
1734 void DateField::dispose()
1736 DateFormatter::SetField( nullptr );
1737 SpinField::dispose();
1740 bool DateField::PreNotify( NotifyEvent& rNEvt )
1742 if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && IsStrictFormat() &&
1743 ( GetExtDateFormat() != ExtDateFieldFormat::SystemLong ) &&
1744 !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
1746 if ( ImplDateProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), GetExtDateFormat( true ), ImplGetLocaleDataWrapper() ) )
1747 return true;
1750 return SpinField::PreNotify( rNEvt );
1753 bool DateField::EventNotify( NotifyEvent& rNEvt )
1755 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
1756 MarkToBeReformatted( false );
1757 else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
1759 if ( MustBeReformatted() )
1761 // !!! We should find out why dates are treated differently than other fields (see
1762 // also bug: 52384)
1764 bool bTextLen = !GetText().isEmpty();
1765 if ( bTextLen || !IsEmptyFieldValueEnabled() )
1767 if ( !ImplAllowMalformedInput() )
1768 Reformat();
1769 else
1771 Date aDate( 0, 0, 0 );
1772 if ( ImplDateGetValue( GetText(), aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper(), GetFieldSettings() ) )
1773 // even with strict text analysis, our text is a valid date -> do a complete
1774 // reformat
1775 Reformat();
1778 else if ( !bTextLen && IsEmptyFieldValueEnabled() )
1780 ResetLastDate();
1781 SetEmptyFieldValueData( true );
1786 return SpinField::EventNotify( rNEvt );
1789 void DateField::DataChanged( const DataChangedEvent& rDCEvt )
1791 SpinField::DataChanged( rDCEvt );
1793 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & (AllSettingsFlags::LOCALE|AllSettingsFlags::MISC)) )
1795 if ( IsDefaultLocale() && ( rDCEvt.GetFlags() & AllSettingsFlags::LOCALE ) )
1796 ImplGetLocaleDataWrapper().setLanguageTag( GetSettings().GetLanguageTag() );
1797 ReformatAll();
1801 void DateField::Modify()
1803 MarkToBeReformatted( true );
1804 SpinField::Modify();
1807 void DateField::Up()
1809 ImplDateSpinArea( true );
1810 SpinField::Up();
1813 void DateField::Down()
1815 ImplDateSpinArea( false );
1816 SpinField::Down();
1819 void DateField::First()
1821 ImplNewFieldValue( maFirst );
1822 SpinField::First();
1825 void DateField::Last()
1827 ImplNewFieldValue( maLast );
1828 SpinField::Last();
1831 DateBox::DateBox( vcl::Window* pParent, WinBits nWinStyle ) :
1832 ComboBox( pParent, nWinStyle )
1834 SetField( this );
1835 SetText( ImplGetLocaleDataWrapper().getDate( ImplGetFieldDate() ) );
1836 Reformat();
1839 void DateBox::dispose()
1841 DateFormatter::SetField( nullptr );
1842 ComboBox::dispose();
1845 bool DateBox::PreNotify( NotifyEvent& rNEvt )
1847 if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && IsStrictFormat() &&
1848 ( GetExtDateFormat() != ExtDateFieldFormat::SystemLong ) &&
1849 !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
1851 if ( ImplDateProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), GetExtDateFormat( true ), ImplGetLocaleDataWrapper() ) )
1852 return true;
1855 return ComboBox::PreNotify( rNEvt );
1858 void DateBox::DataChanged( const DataChangedEvent& rDCEvt )
1860 ComboBox::DataChanged( rDCEvt );
1862 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
1864 if ( IsDefaultLocale() )
1865 ImplGetLocaleDataWrapper().setLanguageTag( GetSettings().GetLanguageTag() );
1866 ReformatAll();
1870 bool DateBox::EventNotify( NotifyEvent& rNEvt )
1872 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
1873 MarkToBeReformatted( false );
1874 else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
1876 if ( MustBeReformatted() )
1878 bool bTextLen = !GetText().isEmpty();
1879 if ( bTextLen || !IsEmptyFieldValueEnabled() )
1880 Reformat();
1881 else if ( !bTextLen && IsEmptyFieldValueEnabled() )
1883 ResetLastDate();
1884 SetEmptyFieldValueData( true );
1889 return ComboBox::EventNotify( rNEvt );
1892 void DateBox::Modify()
1894 MarkToBeReformatted( true );
1895 ComboBox::Modify();
1898 void DateBox::ReformatAll()
1900 OUString aStr;
1901 SetUpdateMode( false );
1902 const sal_Int32 nEntryCount = GetEntryCount();
1903 for ( sal_Int32 i=0; i < nEntryCount; ++i )
1905 ImplDateReformat( GetEntry( i ), aStr, GetFieldSettings() );
1906 RemoveEntryAt(i);
1907 InsertEntry( aStr, i );
1909 DateFormatter::Reformat();
1910 SetUpdateMode( true );
1913 static bool ImplTimeProcessKeyInput( Edit*, const KeyEvent& rKEvt,
1914 bool bStrictFormat, bool bDuration,
1915 TimeFieldFormat eFormat,
1916 const LocaleDataWrapper& rLocaleDataWrapper )
1918 sal_Unicode cChar = rKEvt.GetCharCode();
1920 if ( !bStrictFormat )
1921 return false;
1922 else
1924 sal_uInt16 nGroup = rKEvt.GetKeyCode().GetGroup();
1925 if ( (nGroup == KEYGROUP_FKEYS) || (nGroup == KEYGROUP_CURSOR) ||
1926 (nGroup == KEYGROUP_MISC) ||
1927 ((cChar >= '0') && (cChar <= '9')) ||
1928 string::equals(rLocaleDataWrapper.getTimeSep(), cChar) ||
1929 (rLocaleDataWrapper.getTimeAM().indexOf(cChar) != -1) ||
1930 (rLocaleDataWrapper.getTimePM().indexOf(cChar) != -1) ||
1931 // Accept AM/PM:
1932 (cChar == 'a') || (cChar == 'A') || (cChar == 'm') || (cChar == 'M') || (cChar == 'p') || (cChar == 'P') ||
1933 ((eFormat == TimeFieldFormat::F_100TH_SEC) && string::equals(rLocaleDataWrapper.getTime100SecSep(), cChar)) ||
1934 ((eFormat == TimeFieldFormat::F_SEC_CS) && string::equals(rLocaleDataWrapper.getTime100SecSep(), cChar)) ||
1935 (bDuration && (cChar == '-')) )
1936 return false;
1937 else
1938 return true;
1942 static bool ImplIsOnlyDigits( const OUStringBuffer& _rStr )
1944 const sal_Unicode* _pChr = _rStr.getStr();
1945 for ( sal_Int32 i = 0; i < _rStr.getLength(); ++i, ++_pChr )
1947 if ( *_pChr < '0' || *_pChr > '9' )
1948 return false;
1950 return true;
1953 static bool ImplIsValidTimePortion( bool _bSkipInvalidCharacters, const OUStringBuffer& _rStr )
1955 if ( !_bSkipInvalidCharacters )
1957 if ( ( _rStr.getLength() > 2 ) || _rStr.isEmpty() || !ImplIsOnlyDigits( _rStr ) )
1958 return false;
1960 return true;
1963 static bool ImplCutTimePortion( OUStringBuffer& _rStr, sal_Int32 _nSepPos, bool _bSkipInvalidCharacters, short* _pPortion )
1965 OUString sPortion(_rStr.getStr(), _nSepPos );
1966 _rStr = _nSepPos < _rStr.getLength()
1967 ? _rStr.copy( _nSepPos + 1 ) : OUStringBuffer();
1969 if ( !ImplIsValidTimePortion( _bSkipInvalidCharacters, sPortion ) )
1970 return false;
1971 *_pPortion = (short)sPortion.toInt32();
1972 return true;
1975 static bool ImplTimeGetValue( const OUString& rStr, tools::Time& rTime,
1976 TimeFieldFormat eFormat, bool bDuration,
1977 const LocaleDataWrapper& rLocaleDataWrapper, bool _bSkipInvalidCharacters = true )
1979 OUStringBuffer aStr = rStr;
1980 short nHour = 0;
1981 short nMinute = 0;
1982 short nSecond = 0;
1983 sal_Int64 nNanoSec = 0;
1984 tools::Time aTime( 0, 0, 0 );
1986 if ( rStr.isEmpty() )
1987 return false;
1989 // Search for separators
1990 if (!rLocaleDataWrapper.getTimeSep().isEmpty())
1992 OUStringBuffer aSepStr(",.;:/");
1993 if ( !bDuration )
1994 aSepStr.append('-');
1996 // Replace characters above by the separator character
1997 for (sal_Int32 i = 0; i < aSepStr.getLength(); ++i)
1999 if (string::equals(rLocaleDataWrapper.getTimeSep(), aSepStr[i]))
2000 continue;
2001 for ( sal_Int32 j = 0; j < aStr.getLength(); j++ )
2003 if (aStr[j] == aSepStr[i])
2004 aStr[j] = rLocaleDataWrapper.getTimeSep()[0];
2009 bool bNegative = false;
2010 sal_Int32 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2011 if ( aStr[0] == '-' )
2012 bNegative = true;
2013 if ( eFormat != TimeFieldFormat::F_SEC_CS )
2015 if ( nSepPos < 0 )
2016 nSepPos = aStr.getLength();
2017 if ( !ImplCutTimePortion( aStr, nSepPos, _bSkipInvalidCharacters, &nHour ) )
2018 return false;
2020 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2021 if ( !aStr.isEmpty() && aStr[0] == '-' )
2022 bNegative = true;
2023 if ( nSepPos >= 0 )
2025 if ( !ImplCutTimePortion( aStr, nSepPos, _bSkipInvalidCharacters, &nMinute ) )
2026 return false;
2028 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2029 if ( !aStr.isEmpty() && aStr[0] == '-' )
2030 bNegative = true;
2031 if ( nSepPos >= 0 )
2033 if ( !ImplCutTimePortion( aStr, nSepPos, _bSkipInvalidCharacters, &nSecond ) )
2034 return false;
2035 if ( !aStr.isEmpty() && aStr[0] == '-' )
2036 bNegative = true;
2037 nNanoSec = aStr.toString().toInt64();
2039 else
2040 nSecond = (short)aStr.toString().toInt32();
2042 else
2043 nMinute = (short)aStr.toString().toInt32();
2045 else if ( nSepPos < 0 )
2047 nSecond = (short)aStr.toString().toInt32();
2048 nMinute += nSecond / 60;
2049 nSecond %= 60;
2050 nHour += nMinute / 60;
2051 nMinute %= 60;
2053 else
2055 nSecond = (short)aStr.copy( 0, nSepPos ).toString().toInt32();
2056 aStr.remove( 0, nSepPos+1 );
2058 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2059 if ( !aStr.isEmpty() && aStr[0] == '-' )
2060 bNegative = true;
2061 if ( nSepPos >= 0 )
2063 nMinute = nSecond;
2064 nSecond = (short)aStr.copy( 0, nSepPos ).toString().toInt32();
2065 aStr.remove( 0, nSepPos+1 );
2067 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2068 if ( !aStr.isEmpty() && aStr[0] == '-' )
2069 bNegative = true;
2070 if ( nSepPos >= 0 )
2072 nHour = nMinute;
2073 nMinute = nSecond;
2074 nSecond = (short)aStr.copy( 0, nSepPos ).toString().toInt32();
2075 aStr.remove( 0, nSepPos+1 );
2077 else
2079 nHour += nMinute / 60;
2080 nMinute %= 60;
2083 else
2085 nMinute += nSecond / 60;
2086 nSecond %= 60;
2087 nHour += nMinute / 60;
2088 nMinute %= 60;
2090 nNanoSec = aStr.toString().toInt64();
2093 if ( nNanoSec )
2095 assert(aStr.getLength() >= 1);
2097 sal_Int32 nLen = 1; // at least one digit, otherwise nNanoSec==0
2099 while ( aStr.getLength() > nLen && aStr[nLen] >= '0' && aStr[nLen] <= '9' )
2100 nLen++;
2102 while ( nLen < 9)
2104 nNanoSec *= 10;
2105 ++nLen;
2107 while ( nLen > 9 )
2109 // round if negative?
2110 nNanoSec = (nNanoSec + 5) / 10;
2111 --nLen;
2115 assert(nNanoSec > -1000000000 && nNanoSec < 1000000000);
2116 if ( (nMinute > 59) || (nSecond > 59) || (nNanoSec > 1000000000) )
2117 return false;
2119 if ( eFormat == TimeFieldFormat::F_NONE )
2120 nSecond = nNanoSec = 0;
2121 else if ( eFormat == TimeFieldFormat::F_SEC )
2122 nNanoSec = 0;
2124 if ( !bDuration )
2126 if ( bNegative || (nHour < 0) || (nMinute < 0) ||
2127 (nSecond < 0) || (nNanoSec < 0) )
2128 return false;
2130 OUString aUpperCaseStr = aStr.toString().toAsciiUpperCase();
2131 OUString aAMlocalised(rLocaleDataWrapper.getTimeAM().toAsciiUpperCase());
2132 OUString aPMlocalised(rLocaleDataWrapper.getTimePM().toAsciiUpperCase());
2134 if ( (nHour < 12) && ( ( aUpperCaseStr.indexOf( "PM" ) >= 0 ) || ( aUpperCaseStr.indexOf( aPMlocalised ) >= 0 ) ) )
2135 nHour += 12;
2137 if ( (nHour == 12) && ( ( aUpperCaseStr.indexOf( "AM" ) >= 0 ) || ( aUpperCaseStr.indexOf( aAMlocalised ) >= 0 ) ) )
2138 nHour = 0;
2140 aTime = tools::Time( (sal_uInt16)nHour, (sal_uInt16)nMinute, (sal_uInt16)nSecond,
2141 (sal_uInt32)nNanoSec );
2143 else
2145 assert( !bNegative || (nHour < 0) || (nMinute < 0) ||
2146 (nSecond < 0) || (nNanoSec < 0) );
2147 if ( bNegative || (nHour < 0) || (nMinute < 0) ||
2148 (nSecond < 0) || (nNanoSec < 0) )
2150 // LEM TODO: this looks weird... I think buggy when parsing "05:-02:18"
2151 bNegative = true;
2152 nHour = nHour < 0 ? -nHour : nHour;
2153 nMinute = nMinute < 0 ? -nMinute : nMinute;
2154 nSecond = nSecond < 0 ? -nSecond : nSecond;
2155 nNanoSec = nNanoSec < 0 ? -nNanoSec : nNanoSec;
2158 aTime = tools::Time( (sal_uInt16)nHour, (sal_uInt16)nMinute, (sal_uInt16)nSecond,
2159 (sal_uInt32)nNanoSec );
2160 if ( bNegative )
2161 aTime = -aTime;
2164 rTime = aTime;
2166 return true;
2169 bool TimeFormatter::ImplTimeReformat( const OUString& rStr, OUString& rOutStr )
2171 tools::Time aTime( 0, 0, 0 );
2172 if ( !ImplTimeGetValue( rStr, aTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper() ) )
2173 return true;
2175 tools::Time aTempTime = aTime;
2176 if ( aTempTime > GetMax() )
2177 aTempTime = GetMax() ;
2178 else if ( aTempTime < GetMin() )
2179 aTempTime = GetMin();
2181 bool bSecond = false;
2182 bool b100Sec = false;
2183 if ( meFormat != TimeFieldFormat::F_NONE )
2184 bSecond = true;
2185 if ( meFormat == TimeFieldFormat::F_100TH_SEC )
2186 b100Sec = true;
2188 if ( meFormat == TimeFieldFormat::F_SEC_CS )
2190 sal_uLong n = aTempTime.GetHour() * 3600L;
2191 n += aTempTime.GetMin() * 60L;
2192 n += aTempTime.GetSec();
2193 rOutStr = OUString::number( n );
2194 rOutStr += ImplGetLocaleDataWrapper().getTime100SecSep();
2195 std::ostringstream ostr;
2196 ostr.fill('0');
2197 ostr.width(9);
2198 ostr << aTempTime.GetNanoSec();
2199 rOutStr += OUString::createFromAscii(ostr.str().c_str());
2201 else if ( mbDuration )
2202 rOutStr = ImplGetLocaleDataWrapper().getDuration( aTempTime, bSecond, b100Sec );
2203 else
2205 rOutStr = ImplGetLocaleDataWrapper().getTime( aTempTime, bSecond, b100Sec );
2206 if ( GetTimeFormat() == TimeFormat::Hour12 )
2208 if ( aTempTime.GetHour() > 12 )
2210 tools::Time aT( aTempTime );
2211 aT.SetHour( aT.GetHour() % 12 );
2212 rOutStr = ImplGetLocaleDataWrapper().getTime( aT, bSecond, b100Sec );
2214 // Don't use LocaleDataWrapper, we want AM/PM
2215 if ( aTempTime.GetHour() < 12 )
2216 rOutStr += "AM"; // ImplGetLocaleDataWrapper().getTimeAM();
2217 else
2218 rOutStr += "PM"; // ImplGetLocaleDataWrapper().getTimePM();
2222 return true;
2224 bool TimeFormatter::ImplAllowMalformedInput() const
2226 return !IsEnforceValidValue();
2229 void TimeField::ImplTimeSpinArea( bool bUp )
2231 if ( GetField() )
2233 sal_Int32 nTimeArea = 0;
2234 tools::Time aTime( GetTime() );
2235 OUString aText( GetText() );
2236 Selection aSelection( GetField()->GetSelection() );
2238 // Area search
2239 if ( GetFormat() != TimeFieldFormat::F_SEC_CS )
2241 //Which area is the cursor in of HH:MM:SS.TT
2242 for ( sal_Int32 i = 1, nPos = 0; i <= 4; i++ )
2244 sal_Int32 nPos1 = aText.indexOf( ImplGetLocaleDataWrapper().getTimeSep(), nPos );
2245 sal_Int32 nPos2 = aText.indexOf( ImplGetLocaleDataWrapper().getTime100SecSep(), nPos );
2246 //which ever comes first, bearing in mind that one might not be there
2247 if (nPos1 >= 0 && nPos2 >= 0)
2248 nPos = nPos1 < nPos2 ? nPos1 : nPos2;
2249 else if (nPos1 >= 0)
2250 nPos = nPos1;
2251 else
2252 nPos = nPos2;
2253 if ( nPos < 0 || nPos >= aSelection.Max() )
2255 nTimeArea = i;
2256 break;
2258 else
2259 nPos++;
2262 else
2264 sal_Int32 nPos = aText.indexOf( ImplGetLocaleDataWrapper().getTime100SecSep() );
2265 if ( nPos < 0 || nPos >= aSelection.Max() )
2266 nTimeArea = 3;
2267 else
2268 nTimeArea = 4;
2271 if ( nTimeArea )
2273 tools::Time aAddTime( 0, 0, 0 );
2274 if ( nTimeArea == 1 )
2275 aAddTime = tools::Time( 1, 0 );
2276 else if ( nTimeArea == 2 )
2277 aAddTime = tools::Time( 0, 1 );
2278 else if ( nTimeArea == 3 )
2279 aAddTime = tools::Time( 0, 0, 1 );
2280 else if ( nTimeArea == 4 )
2281 aAddTime = tools::Time( 0, 0, 0, 1 );
2283 if ( !bUp )
2284 aAddTime = -aAddTime;
2286 aTime += aAddTime;
2287 if ( !IsDuration() )
2289 tools::Time aAbsMaxTime( 23, 59, 59, 999999999 );
2290 if ( aTime > aAbsMaxTime )
2291 aTime = aAbsMaxTime;
2292 tools::Time aAbsMinTime( 0, 0 );
2293 if ( aTime < aAbsMinTime )
2294 aTime = aAbsMinTime;
2296 ImplNewFieldValue( aTime );
2302 void TimeFormatter::ImplInit()
2304 meFormat = TimeFieldFormat::F_NONE;
2305 mbDuration = false;
2306 mnTimeFormat = TimeFormat::Hour24; // Should become a ExtTimeFieldFormat in next implementation, merge with mbDuration and meFormat
2309 TimeFormatter::TimeFormatter() :
2310 maLastTime( 0, 0 ),
2311 maMin( 0, 0 ),
2312 maMax( 23, 59, 59, 999999999 ),
2313 mbEnforceValidValue( true ),
2314 maFieldTime( 0, 0 )
2316 ImplInit();
2319 TimeFormatter::~TimeFormatter()
2323 void TimeFormatter::ReformatAll()
2325 Reformat();
2328 void TimeFormatter::SetMin( const tools::Time& rNewMin )
2330 maMin = rNewMin;
2331 if ( !IsEmptyFieldValue() )
2332 ReformatAll();
2335 void TimeFormatter::SetMax( const tools::Time& rNewMax )
2337 maMax = rNewMax;
2338 if ( !IsEmptyFieldValue() )
2339 ReformatAll();
2342 void TimeFormatter::SetTimeFormat( TimeFormatter::TimeFormat eNewFormat )
2344 mnTimeFormat = eNewFormat;
2348 void TimeFormatter::SetFormat( TimeFieldFormat eNewFormat )
2350 meFormat = eNewFormat;
2351 ReformatAll();
2354 void TimeFormatter::SetDuration( bool bNewDuration )
2356 mbDuration = bNewDuration;
2357 ReformatAll();
2360 void TimeFormatter::SetTime( const tools::Time& rNewTime )
2362 SetUserTime( rNewTime );
2363 maFieldTime = maLastTime;
2364 SetEmptyFieldValueData( false );
2367 void TimeFormatter::ImplNewFieldValue( const tools::Time& rTime )
2369 if ( GetField() )
2371 Selection aSelection = GetField()->GetSelection();
2372 aSelection.Justify();
2373 OUString aText = GetField()->GetText();
2375 // If selected until the end then keep it that way
2376 if ( (sal_Int32)aSelection.Max() == aText.getLength() )
2378 if ( !aSelection.Len() )
2379 aSelection.Min() = SELECTION_MAX;
2380 aSelection.Max() = SELECTION_MAX;
2383 tools::Time aOldLastTime = maLastTime;
2384 ImplSetUserTime( rTime, &aSelection );
2385 maLastTime = aOldLastTime;
2387 // Modify at Edit is only set at KeyInput
2388 if ( GetField()->GetText() != aText )
2390 GetField()->SetModifyFlag();
2391 GetField()->Modify();
2396 void TimeFormatter::ImplSetUserTime( const tools::Time& rNewTime, Selection* pNewSelection )
2398 tools::Time aNewTime = rNewTime;
2399 if ( aNewTime > GetMax() )
2400 aNewTime = GetMax();
2401 else if ( aNewTime < GetMin() )
2402 aNewTime = GetMin();
2403 maLastTime = aNewTime;
2405 if ( GetField() )
2407 OUString aStr;
2408 bool bSec = false;
2409 bool b100Sec = false;
2410 if ( meFormat != TimeFieldFormat::F_NONE )
2411 bSec = true;
2412 if ( meFormat == TimeFieldFormat::F_100TH_SEC || meFormat == TimeFieldFormat::F_SEC_CS )
2413 b100Sec = true;
2414 if ( meFormat == TimeFieldFormat::F_SEC_CS )
2416 sal_uLong n = aNewTime.GetHour() * 3600L;
2417 n += aNewTime.GetMin() * 60L;
2418 n += aNewTime.GetSec();
2419 aStr = OUString::number( n );
2420 aStr += ImplGetLocaleDataWrapper().getTime100SecSep();
2421 std::ostringstream ostr;
2422 ostr.fill('0');
2423 ostr.width(9);
2424 ostr << aNewTime.GetNanoSec();
2425 aStr += OUString::createFromAscii(ostr.str().c_str());
2427 else if ( mbDuration )
2429 aStr = ImplGetLocaleDataWrapper().getDuration( aNewTime, bSec, b100Sec );
2431 else
2433 aStr = ImplGetLocaleDataWrapper().getTime( aNewTime, bSec, b100Sec );
2434 if ( GetTimeFormat() == TimeFormat::Hour12 )
2436 if ( aNewTime.GetHour() > 12 )
2438 tools::Time aT( aNewTime );
2439 aT.SetHour( aT.GetHour() % 12 );
2440 aStr = ImplGetLocaleDataWrapper().getTime( aT, bSec, b100Sec );
2442 // Don't use LocaleDataWrapper, we want AM/PM
2443 if ( aNewTime.GetHour() < 12 )
2444 aStr += "AM"; // ImplGetLocaleDataWrapper().getTimeAM();
2445 else
2446 aStr += "PM"; // ImplGetLocaleDataWrapper().getTimePM();
2450 ImplSetText( aStr, pNewSelection );
2454 void TimeFormatter::SetUserTime( const tools::Time& rNewTime )
2456 ImplSetUserTime( rNewTime );
2459 tools::Time TimeFormatter::GetTime() const
2461 tools::Time aTime( 0, 0, 0 );
2463 if ( GetField() )
2465 bool bAllowMailformed = ImplAllowMalformedInput();
2466 if ( ImplTimeGetValue( GetField()->GetText(), aTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper(), !bAllowMailformed ) )
2468 if ( aTime > GetMax() )
2469 aTime = GetMax();
2470 else if ( aTime < GetMin() )
2471 aTime = GetMin();
2473 else
2475 if ( bAllowMailformed )
2476 aTime = tools::Time( 99, 99, 99 ); // set invalid time
2477 else
2478 aTime = maLastTime;
2482 return aTime;
2485 void TimeFormatter::Reformat()
2487 if ( !GetField() )
2488 return;
2490 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
2491 return;
2493 OUString aStr;
2494 bool bOK = ImplTimeReformat( GetField()->GetText(), aStr );
2495 if ( !bOK )
2496 return;
2498 if ( !aStr.isEmpty() )
2500 ImplSetText( aStr );
2501 ImplTimeGetValue( aStr, maLastTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper() );
2503 else
2504 SetTime( maLastTime );
2507 TimeField::TimeField( vcl::Window* pParent, WinBits nWinStyle ) :
2508 SpinField( pParent, nWinStyle ),
2509 maFirst( GetMin() ),
2510 maLast( GetMax() )
2512 SetField( this );
2513 SetText( ImplGetLocaleDataWrapper().getTime( maFieldTime, false ) );
2514 Reformat();
2517 void TimeField::dispose()
2519 TimeFormatter::SetField( nullptr );
2520 SpinField::dispose();
2523 bool TimeField::PreNotify( NotifyEvent& rNEvt )
2525 if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
2527 if ( ImplTimeProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), IsStrictFormat(), IsDuration(), GetFormat(), ImplGetLocaleDataWrapper() ) )
2528 return true;
2531 return SpinField::PreNotify( rNEvt );
2534 bool TimeField::EventNotify( NotifyEvent& rNEvt )
2536 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
2537 MarkToBeReformatted( false );
2538 else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
2540 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
2542 if ( !ImplAllowMalformedInput() )
2543 Reformat();
2544 else
2546 tools::Time aTime( 0, 0, 0 );
2547 if ( ImplTimeGetValue( GetText(), aTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper(), false ) )
2548 // even with strict text analysis, our text is a valid time -> do a complete
2549 // reformat
2550 Reformat();
2555 return SpinField::EventNotify( rNEvt );
2558 void TimeField::DataChanged( const DataChangedEvent& rDCEvt )
2560 SpinField::DataChanged( rDCEvt );
2562 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
2564 if ( IsDefaultLocale() )
2565 ImplGetLocaleDataWrapper().setLanguageTag( GetSettings().GetLanguageTag() );
2566 ReformatAll();
2570 void TimeField::Modify()
2572 MarkToBeReformatted( true );
2573 SpinField::Modify();
2576 void TimeField::Up()
2578 ImplTimeSpinArea( true );
2579 SpinField::Up();
2582 void TimeField::Down()
2584 ImplTimeSpinArea( false );
2585 SpinField::Down();
2588 void TimeField::First()
2590 ImplNewFieldValue( maFirst );
2591 SpinField::First();
2594 void TimeField::Last()
2596 ImplNewFieldValue( maLast );
2597 SpinField::Last();
2600 void TimeField::SetExtFormat( ExtTimeFieldFormat eFormat )
2602 switch ( eFormat )
2604 case ExtTimeFieldFormat::Short24H:
2606 SetTimeFormat( TimeFormat::Hour24 );
2607 SetDuration( false );
2608 SetFormat( TimeFieldFormat::F_NONE );
2610 break;
2611 case ExtTimeFieldFormat::Long24H:
2613 SetTimeFormat( TimeFormat::Hour24 );
2614 SetDuration( false );
2615 SetFormat( TimeFieldFormat::F_SEC );
2617 break;
2618 case ExtTimeFieldFormat::Short12H:
2620 SetTimeFormat( TimeFormat::Hour12 );
2621 SetDuration( false );
2622 SetFormat( TimeFieldFormat::F_NONE );
2624 break;
2625 case ExtTimeFieldFormat::Long12H:
2627 SetTimeFormat( TimeFormat::Hour12 );
2628 SetDuration( false );
2629 SetFormat( TimeFieldFormat::F_SEC );
2631 break;
2632 case ExtTimeFieldFormat::ShortDuration:
2634 SetDuration( true );
2635 SetFormat( TimeFieldFormat::F_NONE );
2637 break;
2638 case ExtTimeFieldFormat::LongDuration:
2640 SetDuration( true );
2641 SetFormat( TimeFieldFormat::F_SEC );
2643 break;
2644 default: OSL_FAIL( "ExtTimeFieldFormat unknown!" );
2647 if ( GetField() && !GetField()->GetText().isEmpty() )
2648 SetUserTime( GetTime() );
2649 ReformatAll();
2652 TimeBox::TimeBox( vcl::Window* pParent, WinBits nWinStyle ) :
2653 ComboBox( pParent, nWinStyle )
2655 SetField( this );
2656 SetText( ImplGetLocaleDataWrapper().getTime( maFieldTime, false ) );
2657 Reformat();
2660 void TimeBox::dispose()
2662 TimeFormatter::SetField( nullptr );
2663 ComboBox::dispose();
2666 bool TimeBox::PreNotify( NotifyEvent& rNEvt )
2668 if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
2670 if ( ImplTimeProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), IsStrictFormat(), IsDuration(), GetFormat(), ImplGetLocaleDataWrapper() ) )
2671 return true;
2674 return ComboBox::PreNotify( rNEvt );
2677 bool TimeBox::EventNotify( NotifyEvent& rNEvt )
2679 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
2680 MarkToBeReformatted( false );
2681 else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
2683 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
2684 Reformat();
2687 return ComboBox::EventNotify( rNEvt );
2690 void TimeBox::DataChanged( const DataChangedEvent& rDCEvt )
2692 ComboBox::DataChanged( rDCEvt );
2694 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
2696 if ( IsDefaultLocale() )
2697 ImplGetLocaleDataWrapper().setLanguageTag( GetSettings().GetLanguageTag() );
2698 ReformatAll();
2702 void TimeBox::Modify()
2704 MarkToBeReformatted( true );
2705 ComboBox::Modify();
2708 void TimeBox::ReformatAll()
2710 OUString aStr;
2711 SetUpdateMode( false );
2712 const sal_Int32 nEntryCount = GetEntryCount();
2713 for ( sal_Int32 i=0; i < nEntryCount; ++i )
2715 ImplTimeReformat( GetEntry( i ), aStr );
2716 RemoveEntryAt(i);
2717 InsertEntry( aStr, i );
2719 TimeFormatter::Reformat();
2720 SetUpdateMode( true );
2723 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */