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