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