LibreOffice Module vcl (master)  1
field2.cxx
Go to the documentation of this file.
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>
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 
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 
59 uno::Reference< i18n::XCharacterClassification > const & ImplGetCharClass()
60 {
61  ImplSVData *const pSVData = ImplGetSVData();
62  assert(pSVData);
63 
64  if (!pSVData->m_xCharClass.is())
65  {
67  }
68 
69  return pSVData->m_xCharClass;
70 }
71 
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 
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 
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 
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 
143 static void ImplSkipDelimiters( const sal_Unicode*& rpBuf )
144 {
145  while( ( *rpBuf == ',' ) || ( *rpBuf == '.' ) || ( *rpBuf == ';' ) ||
146  ( *rpBuf == ':' ) || ( *rpBuf == '-' ) || ( *rpBuf == '/' ) )
147  {
148  rpBuf++;
149  }
150 }
151 
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(),
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 
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 
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 
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 
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 
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 
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 
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 
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 
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:
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 
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 {
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 
843 PatternFormatter::PatternFormatter(Edit* pEdit)
844  : FormatterBase(pEdit)
845 {
846  mbSameMask = true;
847  mbInPattKeyInput = false;
848 }
849 
850 PatternFormatter::~PatternFormatter()
851 {
852 }
853 
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:
868  EntryImplementation(weld::PatternFormatter& rFormatter)
869  : m_rFormatter(rFormatter)
870  , m_rEntry(rFormatter.get_widget())
871  {
872  }
873 
874  virtual OUString GetText() const override
875  {
876  return m_rEntry.get_text();
877  }
878 
879  virtual void SetText(const OUString& rStr, const Selection& rSelection) override
880  {
881  m_rEntry.set_text(rStr);
882  SetSelection(rSelection);
883  }
884 
885  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 
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 
899  virtual bool IsInsertMode() const override
900  {
901  return !m_rEntry.get_overwrite_mode();
902  }
903 
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 {
918  {
919  if (bStrict != m_bStrictFormat)
920  {
921  m_bStrictFormat = bStrict;
922  if (m_bStrictFormat)
923  ReformatAll();
924  }
925  }
926 
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 
937  {
941  }
942 
944  {
945  m_bReformat = false;
946  }
947 
949  {
950  if (m_bReformat)
951  ReformatAll();
952  }
953 
955  {
956  if (!m_bInPattKeyInput)
957  {
958  if (m_bStrictFormat)
960  else
961  m_bReformat = true;
962  }
963  m_aModifyHdl.Call(m_rEntry);
964  }
965 
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 
979 void PatternFormatter::SetString( const OUString& rStr )
980 {
981  if ( GetField() )
982  {
983  GetField()->SetText( rStr );
984  MarkToBeReformatted( false );
985  }
986 }
987 
988 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 
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 
1006 PatternField::PatternField(vcl::Window* pParent, WinBits nWinStyle)
1007  : SpinField(pParent, nWinStyle)
1008  , PatternFormatter(this)
1009 {
1010  Reformat();
1011 }
1012 
1013 void PatternField::dispose()
1014 {
1015  ClearField();
1017 }
1018 
1019 namespace
1020 {
1021  class EditImplementation : public IEditImplementation
1022  {
1023  public:
1024  EditImplementation(Edit& rEdit)
1025  : m_rEdit(rEdit)
1026  {
1027  }
1028 
1029  virtual OUString GetText() const override
1030  {
1031  return m_rEdit.GetText();
1032  }
1033 
1034  virtual void SetText(const OUString& rStr, const Selection& rSelection) override
1035  {
1036  m_rEdit.SetText(rStr, rSelection);
1037  }
1038 
1039  virtual Selection GetSelection() const override
1040  {
1041  return m_rEdit.GetSelection();
1042  }
1043 
1044  virtual void SetSelection(const Selection& rSelection) override
1045  {
1046  m_rEdit.SetSelection(rSelection);
1047  }
1048 
1049  virtual bool IsInsertMode() const override
1050  {
1051  return m_rEdit.IsInsertMode();
1052  }
1053 
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 
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 
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 
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 
1103 }
1104 
1105 PatternBox::PatternBox(vcl::Window* pParent, WinBits nWinStyle)
1106  : ComboBox( pParent, nWinStyle )
1107  , PatternFormatter(this)
1108 {
1109  Reformat();
1110 }
1111 
1112 void PatternBox::dispose()
1113 {
1114  ClearField();
1116 }
1117 
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 
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 
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 
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 
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 
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 
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 
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 
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 
1289 static OUString ImplGetDateSep( const LocaleDataWrapper& rLocaleDataWrapper, ExtDateFieldFormat eFormat )
1290 {
1292  return "-";
1293  else
1294  return rLocaleDataWrapper.getDateSep();
1295 }
1296 
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 
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  {
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;
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;
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 
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 {
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:
1473  break;
1474  case DateOrder::MDY:
1476  break;
1477  default:
1479  }
1480  }
1481  return eDateFormat;
1482  }
1483 }
1484 
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  {
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  {
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  }
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;
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;
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 
1589 OUString DateFormatter::ImplGetDateAsText( const Date& rDate ) const
1590 {
1591  return DateFormatter::FormatDate(rDate, GetExtDateFormat(), ImplGetLocaleDataWrapper(), maStaticFormatter);
1592 }
1593 
1594 static void ImplDateIncrementDay( Date& rDate, bool bUp )
1595 {
1596  DateFormatter::ExpandCentury( rDate );
1597  rDate.AddDays( bUp ? 1 : -1 );
1598 }
1599 
1600 static void ImplDateIncrementMonth( Date& rDate, bool bUp )
1601 {
1602  DateFormatter::ExpandCentury( rDate );
1603  rDate.AddMonths( bUp ? 1 : -1 );
1604 }
1605 
1606 static void ImplDateIncrementYear( Date& rDate, bool bUp )
1607 {
1608  DateFormatter::ExpandCentury( rDate );
1609  rDate.AddYears( bUp ? 1 : -1 );
1610 }
1611 
1612 bool DateFormatter::ImplAllowMalformedInput() const
1613 {
1614  return !IsEnforceValidValue();
1615 }
1616 
1617 int DateFormatter::GetDateArea(ExtDateFieldFormat eFormat, const OUString& rText, int nCursor, const LocaleDataWrapper& rLocaleDataWrapper)
1618 {
1619  sal_Int8 nDateArea = 0;
1620 
1621  if ( eFormat == ExtDateFieldFormat::SystemLong )
1622  {
1623  eFormat = ImplGetExtFormat(rLocaleDataWrapper.getLongDateOrder());
1624  nDateArea = 1;
1625  }
1626  else
1627  {
1628  // search area
1629  sal_Int32 nPos = 0;
1630  OUString aDateSep = ImplGetDateSep(rLocaleDataWrapper, eFormat);
1631  for ( sal_Int8 i = 1; i <= 3; i++ )
1632  {
1633  nPos = rText.indexOf( aDateSep, nPos );
1634  if (nPos < 0 || nPos >= nCursor)
1635  {
1636  nDateArea = i;
1637  break;
1638  }
1639  else
1640  nPos++;
1641  }
1642  }
1643 
1644  return nDateArea;
1645 }
1646 
1648 {
1649  // increment days if all is selected
1650  if ( !GetField() )
1651  return;
1652 
1653  Date aDate( GetDate() );
1654  Selection aSelection = GetField()->GetSelection();
1655  aSelection.Justify();
1656  OUString aText( GetText() );
1657  if ( static_cast<sal_Int32>(aSelection.Len()) == aText.getLength() )
1658  ImplDateIncrementDay( aDate, bUp );
1659  else
1660  {
1661  ExtDateFieldFormat eFormat = GetExtDateFormat( true );
1662  sal_Int8 nDateArea = GetDateArea(eFormat, aText, aSelection.Max(), ImplGetLocaleDataWrapper());
1663 
1664  switch( eFormat )
1665  {
1668  switch( nDateArea )
1669  {
1670  case 1: ImplDateIncrementMonth( aDate, bUp );
1671  break;
1672  case 2: ImplDateIncrementDay( aDate, bUp );
1673  break;
1674  case 3: ImplDateIncrementYear( aDate, bUp );
1675  break;
1676  }
1677  break;
1680  switch( nDateArea )
1681  {
1682  case 1: ImplDateIncrementDay( aDate, bUp );
1683  break;
1684  case 2: ImplDateIncrementMonth( aDate, bUp );
1685  break;
1686  case 3: ImplDateIncrementYear( aDate, bUp );
1687  break;
1688  }
1689  break;
1694  switch( nDateArea )
1695  {
1696  case 1: ImplDateIncrementYear( aDate, bUp );
1697  break;
1698  case 2: ImplDateIncrementMonth( aDate, bUp );
1699  break;
1700  case 3: ImplDateIncrementDay( aDate, bUp );
1701  break;
1702  }
1703  break;
1704  default:
1705  OSL_FAIL( "invalid conversion" );
1706  break;
1707  }
1708  }
1709 
1710  ImplNewFieldValue( aDate );
1711 }
1712 
1713 DateFormatter::DateFormatter(Edit* pEdit)
1714  : FormatterBase(pEdit)
1715  , maFieldDate(0)
1716  , maLastDate(0)
1717  , maMin(1, 1, 1900)
1718  , maMax(31, 12, 2200)
1719  , mbLongFormat(false)
1720  , mbShowDateCentury(true)
1721  , mnExtDateFormat(ExtDateFieldFormat::SystemShort)
1722  , mbEnforceValidValue(true)
1723 {
1724 }
1725 
1726 DateFormatter::~DateFormatter()
1727 {
1728 }
1729 
1730 CalendarWrapper& DateFormatter::GetCalendarWrapper() const
1731 {
1732  if (!mxCalendarWrapper)
1733  {
1734  const_cast<DateFormatter*>(this)->mxCalendarWrapper.reset( new CalendarWrapper( comphelper::getProcessComponentContext() ) );
1735  mxCalendarWrapper->loadDefaultCalendar( GetLocale() );
1736  }
1737 
1738  return *mxCalendarWrapper;
1739 }
1740 
1741 void DateFormatter::SetExtDateFormat( ExtDateFieldFormat eFormat )
1742 {
1743  mnExtDateFormat = eFormat;
1744  ReformatAll();
1745 }
1746 
1747 ExtDateFieldFormat DateFormatter::GetExtDateFormat( bool bResolveSystemFormat ) const
1748 {
1749  ExtDateFieldFormat eDateFormat = mnExtDateFormat;
1750 
1751  if (bResolveSystemFormat)
1752  eDateFormat = ResolveSystemFormat(eDateFormat, ImplGetLocaleDataWrapper());
1753 
1754  return eDateFormat;
1755 }
1756 
1757 void DateFormatter::ReformatAll()
1758 {
1759  Reformat();
1760 }
1761 
1762 void DateFormatter::SetMin( const Date& rNewMin )
1763 {
1764  maMin = rNewMin;
1765  if ( !IsEmptyFieldValue() )
1766  ReformatAll();
1767 }
1768 
1769 void DateFormatter::SetMax( const Date& rNewMax )
1770 {
1771  maMax = rNewMax;
1772  if ( !IsEmptyFieldValue() )
1773  ReformatAll();
1774 }
1775 
1776 void DateFormatter::SetLongFormat( bool bLong )
1777 {
1778  mbLongFormat = bLong;
1779 
1780  // #91913# Remove LongFormat and DateShowCentury - redundant
1781  if ( bLong )
1782  {
1783  SetExtDateFormat( ExtDateFieldFormat::SystemLong );
1784  }
1785  else
1786  {
1787  if( mnExtDateFormat == ExtDateFieldFormat::SystemLong )
1788  SetExtDateFormat( ExtDateFieldFormat::SystemShort );
1789  }
1790 
1791  ReformatAll();
1792 }
1793 
1794 namespace
1795 {
1796  ExtDateFieldFormat ChangeDateCentury(ExtDateFieldFormat eExtDateFormat, bool bShowDateCentury)
1797  {
1798  // #91913# Remove LongFormat and DateShowCentury - redundant
1799  if (bShowDateCentury)
1800  {
1801  switch (eExtDateFormat)
1802  {
1805  eExtDateFormat = ExtDateFieldFormat::SystemShortYYYY; break;
1807  eExtDateFormat = ExtDateFieldFormat::ShortDDMMYYYY; break;
1809  eExtDateFormat = ExtDateFieldFormat::ShortMMDDYYYY; break;
1811  eExtDateFormat = ExtDateFieldFormat::ShortYYYYMMDD; break;
1813  eExtDateFormat = ExtDateFieldFormat::ShortYYYYMMDD_DIN5008; break;
1814  default:
1815  ;
1816  }
1817  }
1818  else
1819  {
1820  switch (eExtDateFormat)
1821  {
1824  eExtDateFormat = ExtDateFieldFormat::SystemShortYY; break;
1826  eExtDateFormat = ExtDateFieldFormat::ShortDDMMYY; break;
1828  eExtDateFormat = ExtDateFieldFormat::ShortMMDDYY; break;
1830  eExtDateFormat = ExtDateFieldFormat::ShortYYMMDD; break;
1832  eExtDateFormat = ExtDateFieldFormat::ShortYYMMDD_DIN5008; break;
1833  default:
1834  ;
1835  }
1836  }
1837 
1838  return eExtDateFormat;
1839  }
1840 }
1841 
1842 void DateFormatter::SetShowDateCentury( bool bShowDateCentury )
1843 {
1844  mbShowDateCentury = bShowDateCentury;
1845 
1846  SetExtDateFormat(ChangeDateCentury(GetExtDateFormat(), bShowDateCentury));
1847 
1848  ReformatAll();
1849 }
1850 
1851 void DateFormatter::SetDate( const Date& rNewDate )
1852 {
1853  ImplSetUserDate( rNewDate );
1854  maFieldDate = maLastDate;
1855  maLastDate = GetDate();
1856 }
1857 
1858 void DateFormatter::ImplSetUserDate( const Date& rNewDate, Selection const * pNewSelection )
1859 {
1860  Date aNewDate = rNewDate;
1861  if ( aNewDate > maMax )
1862  aNewDate = maMax;
1863  else if ( aNewDate < maMin )
1864  aNewDate = maMin;
1865  maLastDate = aNewDate;
1866 
1867  if ( GetField() )
1868  ImplSetText( ImplGetDateAsText( aNewDate ), pNewSelection );
1869 }
1870 
1871 void DateFormatter::ImplNewFieldValue( const Date& rDate )
1872 {
1873  if ( !GetField() )
1874  return;
1875 
1876  Selection aSelection = GetField()->GetSelection();
1877  aSelection.Justify();
1878  OUString aText = GetField()->GetText();
1879 
1880  // If selected until the end then keep it that way
1881  if ( static_cast<sal_Int32>(aSelection.Max()) == aText.getLength() )
1882  {
1883  if ( !aSelection.Len() )
1884  aSelection.Min() = SELECTION_MAX;
1885  aSelection.Max() = SELECTION_MAX;
1886  }
1887 
1888  Date aOldLastDate = maLastDate;
1889  ImplSetUserDate( rDate, &aSelection );
1890  maLastDate = aOldLastDate;
1891 
1892  // Modify at Edit is only set at KeyInput
1893  if ( GetField()->GetText() != aText )
1894  {
1895  GetField()->SetModifyFlag();
1896  GetField()->Modify();
1897  }
1898 }
1899 
1900 Date DateFormatter::GetDate() const
1901 {
1902  Date aDate( Date::EMPTY );
1903 
1904  if ( GetField() )
1905  {
1906  if (TextToDate(GetField()->GetText(), aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper()))
1907  {
1908  if ( aDate > maMax )
1909  aDate = maMax;
1910  else if ( aDate < maMin )
1911  aDate = maMin;
1912  }
1913  else
1914  {
1915  // !!! We should find out why dates are treated differently than other fields (see
1916  // also bug: 52384)
1917 
1918  if ( !ImplAllowMalformedInput() )
1919  {
1920  if ( maLastDate.GetDate() )
1921  aDate = maLastDate;
1922  else if ( !IsEmptyFieldValueEnabled() )
1923  aDate = Date( Date::SYSTEM );
1924  }
1925  else
1926  aDate = Date( Date::EMPTY ); // set invalid date
1927  }
1928  }
1929 
1930  return aDate;
1931 }
1932 
1933 void DateFormatter::SetEmptyDate()
1934 {
1936 }
1937 
1938 bool DateFormatter::IsEmptyDate() const
1939 {
1940  bool bEmpty = FormatterBase::IsEmptyFieldValue();
1941 
1942  if ( GetField() && MustBeReformatted() && IsEmptyFieldValueEnabled() )
1943  {
1944  if ( GetField()->GetText().isEmpty() )
1945  {
1946  bEmpty = true;
1947  }
1948  else if ( !maLastDate.GetDate() )
1949  {
1950  Date aDate( Date::EMPTY );
1951  bEmpty = !TextToDate(GetField()->GetText(), aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper());
1952  }
1953  }
1954  return bEmpty;
1955 }
1956 
1957 void DateFormatter::Reformat()
1958 {
1959  if ( !GetField() )
1960  return;
1961 
1962  if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
1963  return;
1964 
1965  OUString aStr;
1966  ImplDateReformat( GetField()->GetText(), aStr );
1967 
1968  if ( !aStr.isEmpty() )
1969  {
1970  ImplSetText( aStr );
1971  (void)TextToDate(aStr, maLastDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper());
1972  }
1973  else
1974  {
1975  if ( maLastDate.GetDate() )
1976  SetDate( maLastDate );
1977  else if ( !IsEmptyFieldValueEnabled() )
1978  SetDate( Date( Date::SYSTEM ) );
1979  else
1980  {
1981  ImplSetText( OUString() );
1982  SetEmptyFieldValueData( true );
1983  }
1984  }
1985 }
1986 
1987 void DateFormatter::ExpandCentury( Date& rDate )
1988 {
1989  ExpandCentury(rDate, officecfg::Office::Common::DateFormat::TwoDigitYear::get());
1990 }
1991 
1992 void DateFormatter::ExpandCentury( Date& rDate, sal_uInt16 nTwoDigitYearStart )
1993 {
1994  sal_Int16 nDateYear = rDate.GetYear();
1995  if ( 0 <= nDateYear && nDateYear < 100 )
1996  {
1997  sal_uInt16 nCentury = nTwoDigitYearStart / 100;
1998  if ( nDateYear < (nTwoDigitYearStart % 100) )
1999  nCentury++;
2000  rDate.SetYear( nDateYear + (nCentury*100) );
2001  }
2002 }
2003 
2004 DateField::DateField( vcl::Window* pParent, WinBits nWinStyle ) :
2005  SpinField( pParent, nWinStyle ),
2006  DateFormatter(this),
2007  maFirst( GetMin() ),
2008  maLast( GetMax() )
2009 {
2010  SetText( ImplGetLocaleDataWrapper().getDate( ImplGetFieldDate() ) );
2011  Reformat();
2012  ResetLastDate();
2013 }
2014 
2016 {
2017  ClearField();
2019 }
2020 
2022 {
2023  if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && IsStrictFormat() &&
2024  ( GetExtDateFormat() != ExtDateFieldFormat::SystemLong ) &&
2025  !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
2026  {
2027  if ( ImplDateProcessKeyInput( *rNEvt.GetKeyEvent(), GetExtDateFormat( true ), ImplGetLocaleDataWrapper() ) )
2028  return true;
2029  }
2030 
2031  return SpinField::PreNotify( rNEvt );
2032 }
2033 
2035 {
2036  if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
2037  MarkToBeReformatted( false );
2038  else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
2039  {
2040  if ( MustBeReformatted() )
2041  {
2042  // !!! We should find out why dates are treated differently than other fields (see
2043  // also bug: 52384)
2044 
2045  bool bTextLen = !GetText().isEmpty();
2046  if ( bTextLen || !IsEmptyFieldValueEnabled() )
2047  {
2048  if ( !ImplAllowMalformedInput() )
2049  Reformat();
2050  else
2051  {
2052  Date aDate( 0, 0, 0 );
2053  if (TextToDate(GetText(), aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper()))
2054  // even with strict text analysis, our text is a valid date -> do a complete
2055  // reformat
2056  Reformat();
2057  }
2058  }
2059  else
2060  {
2061  ResetLastDate();
2062  SetEmptyFieldValueData( true );
2063  }
2064  }
2065  }
2066 
2067  return SpinField::EventNotify( rNEvt );
2068 }
2069 
2071 {
2072  SpinField::DataChanged( rDCEvt );
2073 
2075  {
2076  if (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE)
2077  ImplResetLocaleDataWrapper();
2078  ReformatAll();
2079  }
2080 }
2081 
2083 {
2084  MarkToBeReformatted( true );
2086 }
2087 
2089 {
2090  ImplDateSpinArea( true );
2091  SpinField::Up();
2092 }
2093 
2095 {
2096  ImplDateSpinArea( false );
2097  SpinField::Down();
2098 }
2099 
2101 {
2102  ImplNewFieldValue( maFirst );
2103  SpinField::First();
2104 }
2105 
2107 {
2108  ImplNewFieldValue( maLast );
2109  SpinField::Last();
2110 }
2111 
2112 DateBox::DateBox(vcl::Window* pParent, WinBits nWinStyle)
2113  : ComboBox( pParent, nWinStyle )
2114  , DateFormatter(this)
2115 {
2116  SetText( ImplGetLocaleDataWrapper().getDate( ImplGetFieldDate() ) );
2117  Reformat();
2118 }
2119 
2120 void DateBox::dispose()
2121 {
2122  ClearField();
2124 }
2125 
2126 bool DateBox::PreNotify( NotifyEvent& rNEvt )
2127 {
2128  if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && IsStrictFormat() &&
2129  ( GetExtDateFormat() != ExtDateFieldFormat::SystemLong ) &&
2130  !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
2131  {
2132  if ( ImplDateProcessKeyInput( *rNEvt.GetKeyEvent(), GetExtDateFormat( true ), ImplGetLocaleDataWrapper() ) )
2133  return true;
2134  }
2135 
2136  return ComboBox::PreNotify( rNEvt );
2137 }
2138 
2139 void DateBox::DataChanged( const DataChangedEvent& rDCEvt )
2140 {
2141  ComboBox::DataChanged( rDCEvt );
2142 
2143  if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
2144  {
2145  ImplResetLocaleDataWrapper();
2146  ReformatAll();
2147  }
2148 }
2149 
2150 bool DateBox::EventNotify( NotifyEvent& rNEvt )
2151 {
2152  if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
2153  MarkToBeReformatted( false );
2154  else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
2155  {
2156  if ( MustBeReformatted() )
2157  {
2158  bool bTextLen = !GetText().isEmpty();
2159  if ( bTextLen || !IsEmptyFieldValueEnabled() )
2160  Reformat();
2161  else
2162  {
2163  ResetLastDate();
2164  SetEmptyFieldValueData( true );
2165  }
2166  }
2167  }
2168 
2169  return ComboBox::EventNotify( rNEvt );
2170 }
2171 
2172 void DateBox::Modify()
2173 {
2174  MarkToBeReformatted( true );
2175  ComboBox::Modify();
2176 }
2177 
2178 void DateBox::ReformatAll()
2179 {
2180  OUString aStr;
2181  SetUpdateMode( false );
2182  const sal_Int32 nEntryCount = GetEntryCount();
2183  for ( sal_Int32 i=0; i < nEntryCount; ++i )
2184  {
2185  ImplDateReformat( GetEntry( i ), aStr );
2186  RemoveEntryAt(i);
2187  InsertEntry( aStr, i );
2188  }
2189  DateFormatter::Reformat();
2190  SetUpdateMode( true );
2191 }
2192 
2193 namespace weld
2194 {
2196  {
2197  if (!m_xCalendarWrapper)
2198  {
2200  m_xCalendarWrapper->loadDefaultCalendar(Application::GetSettings().GetLanguageTag().getLocale());
2201  }
2202  return *m_xCalendarWrapper;
2203  }
2204 
2205  void DateFormatter::SetShowDateCentury(bool bShowDateCentury)
2206  {
2207  m_eFormat = ChangeDateCentury(m_eFormat, bShowDateCentury);
2208 
2209  ReFormat();
2210  }
2211 
2212  void DateFormatter::SetDate(const Date& rDate)
2213  {
2214  auto nDate = rDate.GetDate();
2215  bool bForceOutput = GetEntryText().isEmpty() && rDate == GetDate();
2216  if (bForceOutput)
2217  {
2218  ImplSetValue(nDate, true);
2219  return;
2220  }
2221  SetValue(nDate);
2222  }
2223 
2225  {
2226  return Date(GetValue());
2227  }
2228 
2229  void DateFormatter::SetMin(const Date& rNewMin)
2230  {
2231  SetMinValue(rNewMin.GetDate());
2232  }
2233 
2234  void DateFormatter::SetMax(const Date& rNewMax)
2235  {
2236  SetMaxValue(rNewMax.GetDate());
2237  }
2238 
2239  OUString DateFormatter::FormatNumber(int nValue) const
2240  {
2242  return ::DateFormatter::FormatDate(Date(nValue), m_eFormat, rLocaleData, m_aStaticFormatter);
2243  }
2244 
2245  IMPL_LINK_NOARG(DateFormatter, FormatOutputHdl, LinkParamNone*, bool)
2246  {
2247  OUString sText = FormatNumber(GetValue());
2248  ImplSetTextImpl(sText, nullptr);
2249  return true;
2250  }
2251 
2252  IMPL_LINK(DateFormatter, ParseInputHdl, sal_Int64*, result, TriState)
2253  {
2254  const LocaleDataWrapper& rLocaleDataWrapper = Application::GetSettings().GetLocaleDataWrapper();
2255 
2256  Date aResult(Date::EMPTY);
2257  bool bRet = ::DateFormatter::TextToDate(GetEntryText(), aResult, ResolveSystemFormat(m_eFormat, rLocaleDataWrapper),
2258  rLocaleDataWrapper, GetCalendarWrapper());
2259  if (bRet)
2260  *result = aResult.GetDate();
2261 
2262  return bRet ? TRISTATE_TRUE : TRISTATE_FALSE;
2263  }
2264 }
2265 
2266 static bool ImplTimeProcessKeyInput( const KeyEvent& rKEvt,
2267  bool bStrictFormat, bool bDuration,
2268  TimeFieldFormat eFormat,
2269  const LocaleDataWrapper& rLocaleDataWrapper )
2270 {
2271  sal_Unicode cChar = rKEvt.GetCharCode();
2272 
2273  if ( !bStrictFormat )
2274  return false;
2275  else
2276  {
2277  sal_uInt16 nGroup = rKEvt.GetKeyCode().GetGroup();
2278  if ( (nGroup == KEYGROUP_FKEYS) || (nGroup == KEYGROUP_CURSOR) ||
2279  (nGroup == KEYGROUP_MISC) ||
2280  ((cChar >= '0') && (cChar <= '9')) ||
2281  rLocaleDataWrapper.getTimeSep() == OUStringChar(cChar) ||
2282  (rLocaleDataWrapper.getTimeAM().indexOf(cChar) != -1) ||
2283  (rLocaleDataWrapper.getTimePM().indexOf(cChar) != -1) ||
2284  // Accept AM/PM:
2285  (cChar == 'a') || (cChar == 'A') || (cChar == 'm') || (cChar == 'M') || (cChar == 'p') || (cChar == 'P') ||
2286  ((eFormat == TimeFieldFormat::F_SEC_CS) && rLocaleDataWrapper.getTime100SecSep() == OUStringChar(cChar)) ||
2287  (bDuration && (cChar == '-')) )
2288  return false;
2289  else
2290  return true;
2291  }
2292 }
2293 
2294 static bool ImplIsOnlyDigits( const OUStringBuffer& _rStr )
2295 {
2296  const sal_Unicode* _pChr = _rStr.getStr();
2297  for ( sal_Int32 i = 0; i < _rStr.getLength(); ++i, ++_pChr )
2298  {
2299  if ( *_pChr < '0' || *_pChr > '9' )
2300  return false;
2301  }
2302  return true;
2303 }
2304 
2305 static bool ImplIsValidTimePortion( bool _bSkipInvalidCharacters, const OUStringBuffer& _rStr )
2306 {
2307  if ( !_bSkipInvalidCharacters )
2308  {
2309  if ( ( _rStr.getLength() > 2 ) || _rStr.isEmpty() || !ImplIsOnlyDigits( _rStr ) )
2310  return false;
2311  }
2312  return true;
2313 }
2314 
2315 static bool ImplCutTimePortion( OUStringBuffer& _rStr, sal_Int32 _nSepPos, bool _bSkipInvalidCharacters, short* _pPortion )
2316 {
2317  OUString sPortion(_rStr.getStr(), _nSepPos );
2318 
2319  if (_nSepPos < _rStr.getLength())
2320  _rStr.remove(0, _nSepPos + 1);
2321  else
2322  _rStr.truncate();
2323 
2324  if ( !ImplIsValidTimePortion( _bSkipInvalidCharacters, sPortion ) )
2325  return false;
2326  *_pPortion = static_cast<short>(sPortion.toInt32());
2327  return true;
2328 }
2329 
2330 bool TimeFormatter::TextToTime(const OUString& rStr, tools::Time& rTime, TimeFieldFormat eFormat,
2331  bool bDuration, const LocaleDataWrapper& rLocaleDataWrapper, bool _bSkipInvalidCharacters)
2332 {
2333  OUStringBuffer aStr = rStr;
2334  short nHour = 0;
2335  short nMinute = 0;
2336  short nSecond = 0;
2337  sal_Int64 nNanoSec = 0;
2338  tools::Time aTime( 0, 0, 0 );
2339 
2340  if ( rStr.isEmpty() )
2341  return false;
2342 
2343  // Search for separators
2344  if (!rLocaleDataWrapper.getTimeSep().isEmpty())
2345  {
2346  OUStringBuffer aSepStr(",.;:/");
2347  if ( !bDuration )
2348  aSepStr.append('-');
2349 
2350  // Replace characters above by the separator character
2351  for (sal_Int32 i = 0; i < aSepStr.getLength(); ++i)
2352  {
2353  if (rLocaleDataWrapper.getTimeSep() == OUStringChar(aSepStr[i]))
2354  continue;
2355  for ( sal_Int32 j = 0; j < aStr.getLength(); j++ )
2356  {
2357  if (aStr[j] == aSepStr[i])
2358  aStr[j] = rLocaleDataWrapper.getTimeSep()[0];
2359  }
2360  }
2361  }
2362 
2363  bool bNegative = false;
2364  sal_Int32 nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2365  if ( aStr[0] == '-' )
2366  bNegative = true;
2367  if ( eFormat != TimeFieldFormat::F_SEC_CS )
2368  {
2369  if ( nSepPos < 0 )
2370  nSepPos = aStr.getLength();
2371  if ( !ImplCutTimePortion( aStr, nSepPos, _bSkipInvalidCharacters, &nHour ) )
2372  return false;
2373 
2374  nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2375  if ( !aStr.isEmpty() && aStr[0] == '-' )
2376  bNegative = true;
2377  if ( nSepPos >= 0 )
2378  {
2379  if ( !ImplCutTimePortion( aStr, nSepPos, _bSkipInvalidCharacters, &nMinute ) )
2380  return false;
2381 
2382  nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2383  if ( !aStr.isEmpty() && aStr[0] == '-' )
2384  bNegative = true;
2385  if ( nSepPos >= 0 )
2386  {
2387  if ( !ImplCutTimePortion( aStr, nSepPos, _bSkipInvalidCharacters, &nSecond ) )
2388  return false;
2389  if ( !aStr.isEmpty() && aStr[0] == '-' )
2390  bNegative = true;
2391  nNanoSec = aStr.toString().toInt64();
2392  }
2393  else
2394  nSecond = static_cast<short>(aStr.toString().toInt32());
2395  }
2396  else
2397  nMinute = static_cast<short>(aStr.toString().toInt32());
2398  }
2399  else if ( nSepPos < 0 )
2400  {
2401  nSecond = static_cast<short>(aStr.toString().toInt32());
2402  nMinute += nSecond / 60;
2403  nSecond %= 60;
2404  nHour += nMinute / 60;
2405  nMinute %= 60;
2406  }
2407  else
2408  {
2409  nSecond = static_cast<short>(aStr.copy( 0, nSepPos ).makeStringAndClear().toInt32());
2410  aStr.remove( 0, nSepPos+1 );
2411 
2412  nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2413  if ( !aStr.isEmpty() && aStr[0] == '-' )
2414  bNegative = true;
2415  if ( nSepPos >= 0 )
2416  {
2417  nMinute = nSecond;
2418  nSecond = static_cast<short>(aStr.copy( 0, nSepPos ).makeStringAndClear().toInt32());
2419  aStr.remove( 0, nSepPos+1 );
2420 
2421  nSepPos = aStr.indexOf( rLocaleDataWrapper.getTimeSep() );
2422  if ( !aStr.isEmpty() && aStr[0] == '-' )
2423  bNegative = true;
2424  if ( nSepPos >= 0 )
2425  {
2426  nHour = nMinute;
2427  nMinute = nSecond;
2428  nSecond = static_cast<short>(aStr.copy( 0, nSepPos ).makeStringAndClear().toInt32());
2429  aStr.remove( 0, nSepPos+1 );
2430  }
2431  else
2432  {
2433  nHour += nMinute / 60;
2434  nMinute %= 60;
2435  }
2436  }
2437  else
2438  {
2439  nMinute += nSecond / 60;
2440  nSecond %= 60;
2441  nHour += nMinute / 60;
2442  nMinute %= 60;
2443  }
2444  nNanoSec = aStr.toString().toInt64();
2445  }
2446 
2447  if ( nNanoSec )
2448  {
2449  assert(aStr.getLength() >= 1);
2450 
2451  sal_Int32 nLen = 1; // at least one digit, otherwise nNanoSec==0
2452 
2453  while ( aStr.getLength() > nLen && aStr[nLen] >= '0' && aStr[nLen] <= '9' )
2454  nLen++;
2455 
2456  while ( nLen < 9)
2457  {
2458  nNanoSec *= 10;
2459  ++nLen;
2460  }
2461  while ( nLen > 9 )
2462  {
2463  // round if negative?
2464  nNanoSec = (nNanoSec + 5) / 10;
2465  --nLen;
2466  }
2467  }
2468 
2469  assert(nNanoSec > -1000000000 && nNanoSec < 1000000000);
2470  if ( (nMinute > 59) || (nSecond > 59) || (nNanoSec > 1000000000) )
2471  return false;
2472 
2473  if ( eFormat == TimeFieldFormat::F_NONE )
2474  nSecond = nNanoSec = 0;
2475  else if ( eFormat == TimeFieldFormat::F_SEC )
2476  nNanoSec = 0;
2477 
2478  if ( !bDuration )
2479  {
2480  if ( bNegative || (nHour < 0) || (nMinute < 0) ||
2481  (nSecond < 0) || (nNanoSec < 0) )
2482  return false;
2483 
2484  OUString aUpperCaseStr = aStr.toString().toAsciiUpperCase();
2485  OUString aAMlocalised(rLocaleDataWrapper.getTimeAM().toAsciiUpperCase());
2486  OUString aPMlocalised(rLocaleDataWrapper.getTimePM().toAsciiUpperCase());
2487 
2488  if ( (nHour < 12) && ( ( aUpperCaseStr.indexOf( "PM" ) >= 0 ) || ( aUpperCaseStr.indexOf( aPMlocalised ) >= 0 ) ) )
2489  nHour += 12;
2490 
2491  if ( (nHour == 12) && ( ( aUpperCaseStr.indexOf( "AM" ) >= 0 ) || ( aUpperCaseStr.indexOf( aAMlocalised ) >= 0 ) ) )
2492  nHour = 0;
2493 
2494  aTime = tools::Time( static_cast<sal_uInt16>(nHour), static_cast<sal_uInt16>(nMinute), static_cast<sal_uInt16>(nSecond),
2495  static_cast<sal_uInt32>(nNanoSec) );
2496  }
2497  else
2498  {
2499  assert( !bNegative || (nHour < 0) || (nMinute < 0) ||
2500  (nSecond < 0) || (nNanoSec < 0) );
2501  if ( bNegative || (nHour < 0) || (nMinute < 0) ||
2502  (nSecond < 0) || (nNanoSec < 0) )
2503  {
2504  // LEM TODO: this looks weird... I think buggy when parsing "05:-02:18"
2505  bNegative = true;
2506  nHour = nHour < 0 ? -nHour : nHour;
2507  nMinute = nMinute < 0 ? -nMinute : nMinute;
2508  nSecond = nSecond < 0 ? -nSecond : nSecond;
2509  nNanoSec = nNanoSec < 0 ? -nNanoSec : nNanoSec;
2510  }
2511 
2512  aTime = tools::Time( static_cast<sal_uInt16>(nHour), static_cast<sal_uInt16>(nMinute), static_cast<sal_uInt16>(nSecond),
2513  static_cast<sal_uInt32>(nNanoSec) );
2514  if ( bNegative )
2515  aTime = -aTime;
2516  }
2517 
2518  rTime = aTime;
2519 
2520  return true;
2521 }
2522 
2523 void TimeFormatter::ImplTimeReformat( const OUString& rStr, OUString& rOutStr )
2524 {
2525  tools::Time aTime( 0, 0, 0 );
2526  if ( !TextToTime( rStr, aTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper() ) )
2527  return;
2528 
2529  tools::Time aTempTime = aTime;
2530  if ( aTempTime > GetMax() )
2531  aTempTime = GetMax() ;
2532  else if ( aTempTime < GetMin() )
2533  aTempTime = GetMin();
2534 
2535  bool bSecond = false;
2536  bool b100Sec = false;
2537  if ( meFormat != TimeFieldFormat::F_NONE )
2538  bSecond = true;
2539 
2540  if ( meFormat == TimeFieldFormat::F_SEC_CS )
2541  {
2542  sal_uLong n = aTempTime.GetHour() * 3600L;
2543  n += aTempTime.GetMin() * 60L;
2544  n += aTempTime.GetSec();
2545  rOutStr = OUString::number( n );
2546  rOutStr += ImplGetLocaleDataWrapper().getTime100SecSep();
2547  std::ostringstream ostr;
2548  ostr.fill('0');
2549  ostr.width(9);
2550  ostr << aTempTime.GetNanoSec();
2551  rOutStr += OUString::createFromAscii(ostr.str().c_str());
2552  }
2553  else if ( mbDuration )
2554  rOutStr = ImplGetLocaleDataWrapper().getDuration( aTempTime, bSecond, b100Sec );
2555  else
2556  {
2557  rOutStr = ImplGetLocaleDataWrapper().getTime( aTempTime, bSecond, b100Sec );
2558  if ( GetTimeFormat() == TimeFormat::Hour12 )
2559  {
2560  if ( aTempTime.GetHour() > 12 )
2561  {
2562  tools::Time aT( aTempTime );
2563  aT.SetHour( aT.GetHour() % 12 );
2564  rOutStr = ImplGetLocaleDataWrapper().getTime( aT, bSecond, b100Sec );
2565  }
2566  // Don't use LocaleDataWrapper, we want AM/PM
2567  if ( aTempTime.GetHour() < 12 )
2568  rOutStr += "AM"; // ImplGetLocaleDataWrapper().getTimeAM();
2569  else
2570  rOutStr += "PM"; // ImplGetLocaleDataWrapper().getTimePM();
2571  }
2572  }
2573 }
2574 
2575 bool TimeFormatter::ImplAllowMalformedInput() const
2576 {
2577  return !IsEnforceValidValue();
2578 }
2579 
2580 int TimeFormatter::GetTimeArea(TimeFieldFormat eFormat, const OUString& rText, int nCursor,
2581  const LocaleDataWrapper& rLocaleDataWrapper)
2582 {
2583  int nTimeArea = 0;
2584 
2585  // Area search
2586  if (eFormat != TimeFieldFormat::F_SEC_CS)
2587  {
2588  //Which area is the cursor in of HH:MM:SS.TT
2589  for ( sal_Int32 i = 1, nPos = 0; i <= 4; i++ )
2590  {
2591  sal_Int32 nPos1 = rText.indexOf(rLocaleDataWrapper.getTimeSep(), nPos);
2592  sal_Int32 nPos2 = rText.indexOf(rLocaleDataWrapper.getTime100SecSep(), nPos);
2593  //which ever comes first, bearing in mind that one might not be there
2594  if (nPos1 >= 0 && nPos2 >= 0)
2595  nPos = std::min(nPos1, nPos2);
2596  else if (nPos1 >= 0)
2597  nPos = nPos1;
2598  else
2599  nPos = nPos2;
2600  if (nPos < 0 || nPos >= nCursor)
2601  {
2602  nTimeArea = i;
2603  break;
2604  }
2605  else
2606  nPos++;
2607  }
2608  }
2609  else
2610  {
2611  sal_Int32 nPos = rText.indexOf(rLocaleDataWrapper.getTime100SecSep());
2612  if (nPos < 0 || nPos >= nCursor)
2613  nTimeArea = 3;
2614  else
2615  nTimeArea = 4;
2616  }
2617 
2618  return nTimeArea;
2619 }
2620 
2621 tools::Time TimeFormatter::SpinTime(bool bUp, const tools::Time& rTime, TimeFieldFormat eFormat,
2622  bool bDuration, const OUString& rText, int nCursor,
2623  const LocaleDataWrapper& rLocaleDataWrapper)
2624 {
2625  tools::Time aTime(rTime);
2626 
2627  int nTimeArea = GetTimeArea(eFormat, rText, nCursor, rLocaleDataWrapper);
2628 
2629  if ( nTimeArea )
2630  {
2631  tools::Time aAddTime( 0, 0, 0 );
2632  if ( nTimeArea == 1 )
2633  aAddTime = tools::Time( 1, 0 );
2634  else if ( nTimeArea == 2 )
2635  aAddTime = tools::Time( 0, 1 );
2636  else if ( nTimeArea == 3 )
2637  aAddTime = tools::Time( 0, 0, 1 );
2638  else if ( nTimeArea == 4 )
2639  aAddTime = tools::Time( 0, 0, 0, 1 );
2640 
2641  if ( !bUp )
2642  aAddTime = -aAddTime;
2643 
2644  aTime += aAddTime;
2645  if (!bDuration)
2646  {
2647  tools::Time aAbsMaxTime( 23, 59, 59, 999999999 );
2648  if ( aTime > aAbsMaxTime )
2649  aTime = aAbsMaxTime;
2650  tools::Time aAbsMinTime( 0, 0 );
2651  if ( aTime < aAbsMinTime )
2652  aTime = aAbsMinTime;
2653  }
2654  }
2655 
2656  return aTime;
2657 }
2658 
2659 void TimeField::ImplTimeSpinArea( bool bUp )
2660 {
2661  if ( GetField() )
2662  {
2663  tools::Time aTime( GetTime() );
2664  OUString aText( GetText() );
2665  Selection aSelection( GetField()->GetSelection() );
2666 
2667  aTime = TimeFormatter::SpinTime(bUp, aTime, GetFormat(), IsDuration(), aText, aSelection.Max(), ImplGetLocaleDataWrapper());
2668 
2669  ImplNewFieldValue( aTime );
2670  }
2671 }
2672 
2673 TimeFormatter::TimeFormatter(Edit* pEdit)
2674  : FormatterBase(pEdit)
2675  , maLastTime(0, 0)
2676  , maMin(0, 0)
2677  , maMax(23, 59, 59, 999999999)
2678  , meFormat(TimeFieldFormat::F_NONE)
2679  , mnTimeFormat(TimeFormat::Hour24) // Should become an ExtTimeFieldFormat in next implementation, merge with mbDuration and meFormat
2680  , mbDuration(false)
2681  , mbEnforceValidValue(true)
2682  , maFieldTime(0, 0)
2683 {
2684 }
2685 
2686 TimeFormatter::~TimeFormatter()
2687 {
2688 }
2689 
2690 void TimeFormatter::ReformatAll()
2691 {
2692  Reformat();
2693 }
2694 
2695 void TimeFormatter::SetMin( const tools::Time& rNewMin )
2696 {
2697  maMin = rNewMin;
2698  if ( !IsEmptyFieldValue() )
2699  ReformatAll();
2700 }
2701 
2702 void TimeFormatter::SetMax( const tools::Time& rNewMax )
2703 {
2704  maMax = rNewMax;
2705  if ( !IsEmptyFieldValue() )
2706  ReformatAll();
2707 }
2708 
2709 void TimeFormatter::SetTimeFormat( TimeFormat eNewFormat )
2710 {
2711  mnTimeFormat = eNewFormat;
2712 }
2713 
2714 
2715 void TimeFormatter::SetFormat( TimeFieldFormat eNewFormat )
2716 {
2717  meFormat = eNewFormat;
2718  ReformatAll();
2719 }
2720 
2721 void TimeFormatter::SetDuration( bool bNewDuration )
2722 {
2723  mbDuration = bNewDuration;
2724  ReformatAll();
2725 }
2726 
2727 void TimeFormatter::SetTime( const tools::Time& rNewTime )
2728 {
2729  SetUserTime( rNewTime );
2730  maFieldTime = maLastTime;
2731  SetEmptyFieldValueData( false );
2732 }
2733 
2734 void TimeFormatter::ImplNewFieldValue( const tools::Time& rTime )
2735 {
2736  if ( !GetField() )
2737  return;
2738 
2739  Selection aSelection = GetField()->GetSelection();
2740  aSelection.Justify();
2741  OUString aText = GetField()->GetText();
2742 
2743  // If selected until the end then keep it that way
2744  if ( static_cast<sal_Int32>(aSelection.Max()) == aText.getLength() )
2745  {
2746  if ( !aSelection.Len() )
2747  aSelection.Min() = SELECTION_MAX;
2748  aSelection.Max() = SELECTION_MAX;
2749  }
2750 
2751  tools::Time aOldLastTime = maLastTime;
2752  ImplSetUserTime( rTime, &aSelection );
2753  maLastTime = aOldLastTime;
2754 
2755  // Modify at Edit is only set at KeyInput
2756  if ( GetField()->GetText() != aText )
2757  {
2758  GetField()->SetModifyFlag();
2759  GetField()->Modify();
2760  }
2761 }
2762 
2763 OUString TimeFormatter::FormatTime(const tools::Time& rNewTime, TimeFieldFormat eFormat, TimeFormat eHourFormat, bool bDuration, const LocaleDataWrapper& rLocaleData)
2764 {
2765  OUString aStr;
2766  bool bSec = false;
2767  bool b100Sec = false;
2768  if ( eFormat != TimeFieldFormat::F_NONE )
2769  bSec = true;
2770  if ( eFormat == TimeFieldFormat::F_SEC_CS )
2771  b100Sec = true;
2772  if ( eFormat == TimeFieldFormat::F_SEC_CS )
2773  {
2774  sal_uLong n = rNewTime.GetHour() * 3600L;
2775  n += rNewTime.GetMin() * 60L;
2776  n += rNewTime.GetSec();
2777  aStr = OUString::number( n ) + rLocaleData.getTime100SecSep();
2778  std::ostringstream ostr;
2779  ostr.fill('0');
2780  ostr.width(9);
2781  ostr << rNewTime.GetNanoSec();
2782  aStr += OUString::createFromAscii(ostr.str().c_str());
2783  }
2784  else if ( bDuration )
2785  {
2786  aStr = rLocaleData.getDuration( rNewTime, bSec, b100Sec );
2787  }
2788  else
2789  {
2790  aStr = rLocaleData.getTime( rNewTime, bSec, b100Sec );
2791  if ( eHourFormat == TimeFormat::Hour12 )
2792  {
2793  if ( rNewTime.GetHour() > 12 )
2794  {
2795  tools::Time aT( rNewTime );
2796  aT.SetHour( aT.GetHour() % 12 );
2797  aStr = rLocaleData.getTime( aT, bSec, b100Sec );
2798  }
2799  // Don't use LocaleDataWrapper, we want AM/PM
2800  if ( rNewTime.GetHour() < 12 )
2801  aStr += "AM"; // rLocaleData.getTimeAM();
2802  else
2803  aStr += "PM"; // rLocaleData.getTimePM();
2804  }
2805  }
2806 
2807  return aStr;
2808 }
2809 
2810 void TimeFormatter::ImplSetUserTime( const tools::Time& rNewTime, Selection const * pNewSelection )
2811 {
2812  tools::Time aNewTime = rNewTime;
2813  if ( aNewTime > GetMax() )
2814  aNewTime = GetMax();
2815  else if ( aNewTime < GetMin() )
2816  aNewTime = GetMin();
2817  maLastTime = aNewTime;
2818 
2819  if ( GetField() )
2820  {
2821  OUString aStr = TimeFormatter::FormatTime(aNewTime, meFormat, GetTimeFormat(), mbDuration, ImplGetLocaleDataWrapper());
2822  ImplSetText( aStr, pNewSelection );
2823  }
2824 }
2825 
2826 void TimeFormatter::SetUserTime( const tools::Time& rNewTime )
2827 {
2828  ImplSetUserTime( rNewTime );
2829 }
2830 
2831 tools::Time TimeFormatter::GetTime() const
2832 {
2833  tools::Time aTime( 0, 0, 0 );
2834 
2835  if ( GetField() )
2836  {
2837  bool bAllowMalformed = ImplAllowMalformedInput();
2838  if ( TextToTime( GetField()->GetText(), aTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper(), !bAllowMalformed ) )
2839  {
2840  if ( aTime > GetMax() )
2841  aTime = GetMax();
2842  else if ( aTime < GetMin() )
2843  aTime = GetMin();
2844  }
2845  else
2846  {
2847  if ( bAllowMalformed )
2848  aTime = tools::Time( 99, 99, 99 ); // set invalid time
2849  else
2850  aTime = maLastTime;
2851  }
2852  }
2853 
2854  return aTime;
2855 }
2856 
2857 void TimeFormatter::Reformat()
2858 {
2859  if ( !GetField() )
2860  return;
2861 
2862  if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
2863  return;
2864 
2865  OUString aStr;
2866  ImplTimeReformat( GetField()->GetText(), aStr );
2867 
2868  if ( !aStr.isEmpty() )
2869  {
2870  ImplSetText( aStr );
2871  (void)TextToTime(aStr, maLastTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper());
2872  }
2873  else
2874  SetTime( maLastTime );
2875 }
2876 
2877 TimeField::TimeField( vcl::Window* pParent, WinBits nWinStyle ) :
2878  SpinField( pParent, nWinStyle ),
2879  TimeFormatter(this),
2880  maFirst( GetMin() ),
2881  maLast( GetMax() )
2882 {
2883  SetText( ImplGetLocaleDataWrapper().getTime( maFieldTime, false ) );
2884  Reformat();
2885 }
2886 
2887 void TimeField::dispose()
2888 {
2889  ClearField();
2891 }
2892 
2893 bool TimeField::PreNotify( NotifyEvent& rNEvt )
2894 {
2895  if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
2896  {
2897  if ( ImplTimeProcessKeyInput( *rNEvt.GetKeyEvent(), IsStrictFormat(), IsDuration(), GetFormat(), ImplGetLocaleDataWrapper() ) )
2898  return true;
2899  }
2900 
2901  return SpinField::PreNotify( rNEvt );
2902 }
2903 
2904 bool TimeField::EventNotify( NotifyEvent& rNEvt )
2905 {
2906  if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
2907  MarkToBeReformatted( false );
2908  else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
2909  {
2910  if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
2911  {
2912  if ( !ImplAllowMalformedInput() )
2913  Reformat();
2914  else
2915  {
2916  tools::Time aTime( 0, 0, 0 );
2917  if ( TextToTime( GetText(), aTime, GetFormat(), IsDuration(), ImplGetLocaleDataWrapper(), false ) )
2918  // even with strict text analysis, our text is a valid time -> do a complete
2919  // reformat
2920  Reformat();
2921  }
2922  }
2923  }
2924 
2925  return SpinField::EventNotify( rNEvt );
2926 }
2927 
2928 void TimeField::DataChanged( const DataChangedEvent& rDCEvt )
2929 {
2930  SpinField::DataChanged( rDCEvt );
2931 
2932  if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
2933  {
2934  ImplResetLocaleDataWrapper();
2935  ReformatAll();
2936  }
2937 }
2938 
2939 void TimeField::Modify()
2940 {
2941  MarkToBeReformatted( true );
2943 }
2944 
2945 void TimeField::Up()
2946 {
2947  ImplTimeSpinArea( true );
2948  SpinField::Up();
2949 }
2950 
2951 void TimeField::Down()
2952 {
2953  ImplTimeSpinArea( false );
2954  SpinField::Down();
2955 }
2956 
2957 void TimeField::First()
2958 {
2959  ImplNewFieldValue( maFirst );
2960  SpinField::First();
2961 }
2962 
2963 void TimeField::Last()
2964 {
2965  ImplNewFieldValue( maLast );
2966  SpinField::Last();
2967 }
2968 
2969 void TimeField::SetExtFormat( ExtTimeFieldFormat eFormat )
2970 {
2971  switch ( eFormat )
2972  {
2974  {
2975  SetTimeFormat( TimeFormat::Hour24 );
2976  SetDuration( false );
2978  }
2979  break;
2981  {
2982  SetTimeFormat( TimeFormat::Hour24 );
2983  SetDuration( false );
2985  }
2986  break;
2988  {
2989  SetTimeFormat( TimeFormat::Hour12 );
2990  SetDuration( false );
2992  }
2993  break;
2995  {
2996  SetTimeFormat( TimeFormat::Hour12 );
2997  SetDuration( false );
2999  }
3000  break;
3002  {
3003  SetDuration( true );
3005  }
3006  break;
3008  {
3009  SetDuration( true );
3011  }
3012  break;
3013  default: OSL_FAIL( "ExtTimeFieldFormat unknown!" );
3014  }
3015 
3016  if ( GetField() && !GetField()->GetText().isEmpty() )
3017  SetUserTime( GetTime() );
3018  ReformatAll();
3019 }
3020 
3021 TimeBox::TimeBox(vcl::Window* pParent, WinBits nWinStyle)
3022  : ComboBox(pParent, nWinStyle)
3023  , TimeFormatter(this)
3024 {
3025  SetText( ImplGetLocaleDataWrapper().getTime( maFieldTime, false ) );
3026  Reformat();
3027 }
3028 
3029 void TimeBox::dispose()
3030 {
3031  ClearField();
3033 }
3034 
3035 bool TimeBox::PreNotify( NotifyEvent& rNEvt )
3036 {
3037  if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
3038  {
3039  if ( ImplTimeProcessKeyInput( *rNEvt.GetKeyEvent(), IsStrictFormat(), IsDuration(), GetFormat(), ImplGetLocaleDataWrapper() ) )
3040  return true;
3041  }
3042 
3043  return ComboBox::PreNotify( rNEvt );
3044 }
3045 
3046 bool TimeBox::EventNotify( NotifyEvent& rNEvt )
3047 {
3048  if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
3049  MarkToBeReformatted( false );
3050  else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
3051  {
3052  if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
3053  Reformat();
3054  }
3055 
3056  return ComboBox::EventNotify( rNEvt );
3057 }
3058 
3059 void TimeBox::DataChanged( const DataChangedEvent& rDCEvt )
3060 {
3061  ComboBox::DataChanged( rDCEvt );
3062 
3063  if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
3064  {
3065  ImplResetLocaleDataWrapper();
3066  ReformatAll();
3067  }
3068 }
3069 
3070 void TimeBox::Modify()
3071 {
3072  MarkToBeReformatted( true );
3073  ComboBox::Modify();
3074 }
3075 
3076 void TimeBox::ReformatAll()
3077 {
3078  OUString aStr;
3079  SetUpdateMode( false );
3080  const sal_Int32 nEntryCount = GetEntryCount();
3081  for ( sal_Int32 i=0; i < nEntryCount; ++i )
3082  {
3083  ImplTimeReformat( GetEntry( i ), aStr );
3084  RemoveEntryAt(i);
3085  InsertEntry( aStr, i );
3086  }
3087  TimeFormatter::Reformat();
3088  SetUpdateMode( true );
3089 }
3090 
3091 namespace weld
3092 {
3094  {
3095  tools::Time aTime(0);
3096  aTime.MakeTimeFromMS(nValue);
3097  return aTime;
3098  }
3099 
3101  {
3102  return rTime.GetMSFromTime();
3103  }
3104 
3105  void TimeFormatter::SetTime(const tools::Time& rTime)
3106  {
3107  auto nTime = ConvertValue(rTime);
3108  bool bForceOutput = GetEntryText().isEmpty() && rTime == GetTime();
3109  if (bForceOutput)
3110  {
3111  ImplSetValue(nTime, true);
3112  return;
3113  }
3114  SetValue(nTime);
3115  }
3116 
3118  {
3119  return ConvertValue(GetValue());
3120  }
3121 
3122  void TimeFormatter::SetMin(const tools::Time& rNewMin)
3123  {
3124  SetMinValue(ConvertValue(rNewMin));
3125  }
3126 
3127  void TimeFormatter::SetMax(const tools::Time& rNewMax)
3128  {
3129  SetMaxValue(ConvertValue(rNewMax));
3130  }
3131 
3132  OUString TimeFormatter::FormatNumber(int nValue) const
3133  {
3135  return ::TimeFormatter::FormatTime(ConvertValue(nValue), m_eFormat, m_eTimeFormat, m_bDuration, rLocaleData);
3136  }
3137 
3138  IMPL_LINK_NOARG(TimeFormatter, FormatOutputHdl, LinkParamNone*, bool)
3139  {
3140  OUString sText = FormatNumber(GetValue());
3141  ImplSetTextImpl(sText, nullptr);
3142  return true;
3143  }
3144 
3145  IMPL_LINK(TimeFormatter, ParseInputHdl, sal_Int64*, result, TriState)
3146  {
3147  const LocaleDataWrapper& rLocaleDataWrapper = Application::GetSettings().GetLocaleDataWrapper();
3148 
3149  tools::Time aResult(0);
3150  bool bRet = ::TimeFormatter::TextToTime(GetEntryText(), aResult, m_eFormat, m_bDuration, rLocaleDataWrapper);
3151  if (bRet)
3152  *result = ConvertValue(aResult);
3153 
3154  return bRet ? TRISTATE_TRUE : TRISTATE_FALSE;
3155  }
3156 
3157  IMPL_LINK(TimeFormatter, CursorChangedHdl, weld::Entry&, rEntry, void)
3158  {
3159  int nStartPos, nEndPos;
3160  rEntry.get_selection_bounds(nStartPos, nEndPos);
3161 
3163  const int nTimeArea = ::TimeFormatter::GetTimeArea(m_eFormat, GetEntryText(), nEndPos, rLocaleData);
3164 
3165  int nIncrements = 1;
3166 
3167  if (nTimeArea == 1)
3168  nIncrements = 1000 * 60 * 60;
3169  else if (nTimeArea == 2)
3170  nIncrements = 1000 * 60;
3171  else if (nTimeArea == 3)
3172  nIncrements = 1000;
3173 
3174  SetSpinSize(nIncrements);
3175  }
3176 }
3177 
3178 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
tools::Long Max() const
constexpr sal_uInt16 KEYGROUP_MISC
Definition: keycodes.hxx:41
#define EDITMASK_UPPERALPHANUM
Definition: field2.cxx:53
Date maFirst
Definition: field.hxx:477
const LocaleDataWrapper & GetLocaleDataWrapper() const
virtual bool get_editable() const =0
sal_Int32 nIndex
static sal_Unicode ImplPatternChar(sal_Unicode cChar, char cEditMask)
Definition: field2.cxx:199
virtual void SetMinValue(double dMin) override
Definition: weldutils.cxx:204
static sal_uInt16 ImplGetNum(const sal_Unicode *&rpBuf, bool &rbError)
Definition: field2.cxx:124
ExtTimeFieldFormat
Definition: vclenum.hxx:146
virtual void DataChanged(const DataChangedEvent &rDCEvt) override
Definition: field2.cxx:2070
static void ImplPatternMaxPos(const OUString &rStr, const OString &rEditMask, sal_uInt16 nFormatFlags, bool bSameMask, sal_Int32 nCursorPos, sal_Int32 &rPos)
Definition: field2.cxx:326
TimeFieldFormat
Definition: vclenum.hxx:28
void SetStrictFormat(bool bStrict)
Definition: field2.cxx:917
static bool ImplPatternProcessKeyInput(IEditImplementation &rEdit, const KeyEvent &rKEvt, const OString &rEditMask, const OUString &rLiteralMask, bool bStrictFormat, bool bSameMask, bool &rbInKeyInput)
Definition: field2.cxx:521
static sal_uInt16 ImplCutNumberFromString(OUString &rStr)
Definition: field2.cxx:1185
void GetSelection(struct ESelection &rSel, SvxTextForwarder const *pForwarder) noexcept
static bool isLetterType(sal_Int32 nType)
std::string GetValue
sal_uInt32 GetFormatIndex(NfIndexTableOffset, LanguageType eLnge=LANGUAGE_DONTKNOW)
signed char sal_Int8
LanguageType getLanguageType(bool bResolveSystem=true) const
OUString FormatNumber(int nValue) const
Definition: field2.cxx:2239
css::uno::Sequence< css::i18n::Calendar2 > getAllCalendars() const
sal_uIntPtr sal_uLong
static const AllSettings & GetSettings()
Gets the application's settings.
Definition: svapp.cxx:733
void AddYears(sal_Int16 nAddYears)
sal_Int64 n
static bool ImplCutMonthName(OUString &rStr, std::u16string_view _rLookupMonthName)
Definition: field2.cxx:1200
Any GetTime(const OUString &val)
aBuf
the first 4 of these are only used by base/dbaccess
const OUString & getDateSep() const
void SetEmptyFieldValue()
Definition: field.cxx:528
sal_uInt16 GetGroup() const
Definition: keycod.hxx:64
#define EDITMASK_UPPERALLCHAR
Definition: field2.cxx:57
sal_uInt16 GetCode() const
Definition: keycod.hxx:51
void SetMask(const OString &rEditMask, const OUString &rLiteralMask)
Definition: field2.cxx:854
DataChangedEventType GetType() const
Definition: event.hxx:362
const KeyEvent * GetKeyEvent() const
Definition: event.hxx:316
#define EDITMASK_UPPERALPHA
Definition: field2.cxx:51
DateOrder getDateOrder() const
static sal_Int32 ImplPatternLeftPos(std::string_view rEditMask, sal_Int32 nCursorPos)
Definition: field2.cxx:466
static sal_Unicode * ImplAddString(sal_Unicode *pBuf, const OUString &rStr)
Definition: field2.cxx:72
virtual void Up()
Definition: spinfld.cxx:358
void AddMonths(sal_Int32 nAddMonths)
OUString FormatNumber(int nValue) const
Definition: field2.cxx:3132
TimeFormat m_eTimeFormat
Definition: weldutils.hxx:321
virtual void set_text(const OUString &rText)=0
TRISTATE_TRUE
static ExtDateFieldFormat ImplGetExtFormat(LongDateOrder eOld)
Definition: field2.cxx:1173
static sal_Unicode * ImplAddNum(sal_Unicode *pBuf, sal_uLong nNumber, int nMinLen)
Definition: field2.cxx:79
static OUString ImplGetDateSep(const LocaleDataWrapper &rLocaleDataWrapper, ExtDateFieldFormat eFormat)
Definition: field2.cxx:1289
void ReFormat()
Definition: fmtfield.cxx:686
Date maLast
Definition: field.hxx:478
DateField(vcl::Window *pParent, WinBits nWinStyle)
Definition: field2.cxx:2004
const css::lang::Locale & getLocale(bool bResolveSystem=true) const
CalendarWrapper & GetCalendarWrapper() const
Definition: field2.cxx:1730
static void ImplSkipDelimiters(const sal_Unicode *&rpBuf)
Definition: field2.cxx:143
static tools::Time ConvertValue(int nValue)
Definition: field2.cxx:3093
virtual void Modify() override
Definition: field2.cxx:2082
constexpr sal_uInt16 KEY_END
Definition: keycodes.hxx:115
OUString getDuration(const tools::Time &rTime, bool bSec=true, bool b100Sec=false) const
sal_uInt16 GetHour() const
static bool ImplCutTimePortion(OUStringBuffer &_rStr, sal_Int32 _nSepPos, bool _bSkipInvalidCharacters, short *_pPortion)
Definition: field2.cxx:2315
LanguageTag getLoadedLanguageTag() const
sal_Int64 WinBits
sal_uInt16 sal_Unicode
virtual void dispose() override
This is intended to be used to clear any locally held references to other Window-subclass objects...
Definition: spinfld.cxx:351
virtual void set_overwrite_mode(bool bOn)=0
virtual void dispose() override
This is intended to be used to clear any locally held references to other Window-subclass objects...
Definition: combobox.cxx:125
TimeFormat
Definition: vclenum.hxx:141
sal_uInt32 GetNanoSec() const
constexpr OUStringLiteral IsReadOnly(u"IsReadOnly")
virtual void SetMaxValue(double dMin) override
Definition: weldutils.cxx:218
sal_uInt16 GetMonth() const
IMPL_LINK(CustomWeld, DoResize, const Size &, rSize, void)
Definition: customweld.cxx:46
double GetValue()
Definition: fmtfield.cxx:860
#define EDITMASK_ALLCHAR
Definition: field2.cxx:56
sal_uInt16 GetMin() const
AllSettingsFlags GetFlags() const
Definition: event.hxx:363
void SetShowDateCentury(bool bShowCentury)
Definition: field2.cxx:1842
bool IsEmptyFieldValue() const
Definition: field.cxx:535
OUString FormatNumber(sal_uInt32 nNum, SvxNumType nFormat, LanguageType nLang)
void SetYear(sal_Int16 nNewYear)
LongDateOrder getLongDateOrder() const
virtual const Selection & GetSelection() const
Definition: edit.cxx:2469
constexpr sal_uInt16 KEYGROUP_CURSOR
Definition: keycodes.hxx:40
sal_Int32 GetDate() const
Definition: edit.hxx:55
virtual void dispose() override
This is intended to be used to clear any locally held references to other Window-subclass objects...
Definition: field2.cxx:2015
ExtDateFieldFormat m_eFormat
Definition: weldutils.hxx:351
StaticFormatter m_aStaticFormatter
Definition: formatter.hxx:127
virtual void Down()
Definition: spinfld.cxx:363
const OUString & getTime100SecSep() const
virtual void Last() override
Definition: field2.cxx:2106
const LanguageTag & GetLanguageTag() const
sal_Int16 GetYear() const
LongDateOrder
ImplSVData * ImplGetSVData()
Definition: svdata.cxx:75
sal_uInt16 GetSec() const
const LanguageTag & GetLanguageTag() const
#define EDITMASK_NUM
Definition: field2.cxx:54
virtual void DataChanged(const DataChangedEvent &rDCEvt) override
Definition: combobox.cxx:694
const OUString & getTimePM() const
#define DBG_UNHANDLED_EXCEPTION(...)
OUString getUniqueID() const
virtual bool get_selection_bounds(int &rStartPos, int &rEndPos)=0
virtual bool EventNotify(NotifyEvent &rNEvt) override
Definition: spinfld.cxx:484
void Justify()
static bool ImplIsOnlyDigits(const OUStringBuffer &_rStr)
Definition: field2.cxx:2294
virtual bool PreNotify(NotifyEvent &rNEvt)
Definition: event.cxx:52
virtual OUString GetEntryText() const override
Definition: weldutils.cxx:175
int i
constexpr sal_uInt16 KEYGROUP_FKEYS
Definition: keycodes.hxx:39
virtual bool EventNotify(NotifyEvent &rNEvt) override
Definition: combobox.cxx:716
void SetTime(const tools::Time &rNewTime)
Definition: field2.cxx:2727
virtual void First() override
Definition: field2.cxx:2100
constexpr sal_uInt16 KEY_HOME
Definition: keycodes.hxx:114
void SetValue(double dVal)
Definition: fmtfield.cxx:855
static void ImplDateIncrementMonth(Date &rDate, bool bUp)
Definition: field2.cxx:1600
TRISTATE_FALSE
virtual OUString GetText() const override
Definition: edit.cxx:2565
virtual void select_region(int nStartPos, int nEndPos)=0
virtual void Modify()
Definition: edit.cxx:2315
Link< weld::Entry &, void > m_aModifyHdl
Definition: weldutils.hxx:380
sal_uInt16 GetDay() const
#define EDITMASK_ALPHA
Definition: field2.cxx:50
MouseNotifyEvent GetType() const
Definition: event.hxx:308
TimeFieldFormat m_eFormat
Definition: weldutils.hxx:320
void AddDays(sal_Int32 nAddDays)
static bool ImplCommaPointCharEqual(sal_Unicode c1, sal_Unicode c2)
Definition: field2.cxx:216
tuple index
css::uno::Sequence< css::i18n::CalendarItem2 > getPartitiveMonths() const
virtual bool EventNotify(NotifyEvent &rNEvt) override
Definition: field2.cxx:2034
static bool isLetterNumericType(sal_Int32 nType)
virtual OUString get_text() const =0
tools::Long Len() const
void SetMax(const Date &rNewMax)
Definition: field2.cxx:1769
static bool ImplDateProcessKeyInput(const KeyEvent &rKEvt, ExtDateFieldFormat eFormat, const LocaleDataWrapper &rLocaleDataWrapper)
Definition: field2.cxx:1297
void ImplSetValue(double dValue, bool bForce)
Definition: fmtfield.cxx:738
constexpr sal_uInt16 KEY_RIGHT
Definition: keycodes.hxx:113
static sal_uInt16 ImplCutMonthFromString(OUString &rStr, OUString &rCalendarName, const LocaleDataWrapper &rLocaleData, const CalendarWrapper &rCalendarWrapper)
Definition: field2.cxx:1223
const LanguageTag & getLocale()
ExtDateFieldFormat
Definition: vclenum.hxx:154
virtual void Down() override
Definition: field2.cxx:2094
const vcl::KeyCode & GetKeyCode() const
Definition: event.hxx:57
css::uno::Reference< css::i18n::XCharacterClassification > m_xCharClass
Definition: svdata.hxx:411
void SetMin(const Date &rNewMin)
Definition: field2.cxx:1762
static bool ImplTimeProcessKeyInput(const KeyEvent &rKEvt, bool bStrictFormat, bool bDuration, TimeFieldFormat eFormat, const LocaleDataWrapper &rLocaleDataWrapper)
Definition: field2.cxx:2266
virtual void First()
Definition: spinfld.cxx:368
bool IsShift() const
Definition: keycod.hxx:56
sal_Int32 GetMSFromTime() const
virtual void DataChanged(const DataChangedEvent &rDCEvt) override
Definition: spinfld.cxx:810
const LanguageTag & getLanguageTag() const
uno::Reference< i18n::XCharacterClassification > const & ImplGetCharClass()
Definition: field2.cxx:59
IMPL_LINK_NOARG(HexColorControl, OnAsyncModifyHdl, void *, void)
void ChangeIntl(LanguageType eLnge)
#define EDITMASK_ALPHANUM
Definition: field2.cxx:52
VCL_DLLPUBLIC css::uno::Reference< css::i18n::XCharacterClassification > CreateCharacterClassification()
Definition: unohelp.cxx:43
std::unique_ptr< CalendarWrapper > m_xCalendarWrapper
Definition: weldutils.hxx:352
tools::Time GetTime()
Definition: field2.cxx:2831
const OUString & getTimeAM() const
static sal_Unicode * ImplAddSNum(sal_Unicode *pBuf, sal_Int32 nNumber, int nMinLen)
Definition: field2.cxx:114
static OUString ImplPatternProcessStrictModify(const OUString &rText, const OString &rEditMask, const OUString &rLiteralMask, bool bSameMask)
Definition: field2.cxx:366
tools::Long Min() const
static bool ImplIsPatternChar(sal_Unicode cChar, char cEditMask)
Definition: field2.cxx:152
static bool isNumericType(sal_Int32 nType)
sal_Unicode GetCharCode() const
Definition: event.hxx:56
css::uno::Sequence< css::i18n::CalendarItem2 > getMonths() const
bool IsMod1() const
Definition: keycod.hxx:58
static sal_uInt16 ImplGetMonthFromCalendarItem(OUString &rStr, const uno::Sequence< i18n::CalendarItem2 > &rMonths)
Definition: field2.cxx:1207
virtual void Last()
Definition: spinfld.cxx:373
#define EDITMASK_NUMSPACE
Definition: field2.cxx:55
Reference< XComponentContext > getProcessComponentContext()
QPRO_FUNC_TYPE nType
const OUString & getTimeSep() const
constexpr sal_uInt16 KEY_BACKSPACE
Definition: keycodes.hxx:122
virtual bool PreNotify(NotifyEvent &rNEvt) override
Definition: spinfld.cxx:832
virtual void Up() override
Definition: field2.cxx:2088
virtual bool PreNotify(NotifyEvent &rNEvt) override
Definition: field2.cxx:2021
static OUString ImplPatternReformat(const OUString &rStr, const OString &rEditMask, const OUString &rLiteralMask, sal_uInt16 nFormatFlags)
Definition: field2.cxx:227
sal_Int64 ConvertValue(sal_Int64 nValue, sal_Int64 mnBaseValue, sal_uInt16 nDecDigits, FieldUnit eInUnit, FieldUnit eOutUnit)
Definition: field.cxx:1028
A widget used to choose from a list of items and which has an entry.
Definition: combobox.hxx:38
#define SELECTION_MAX
Any result
const css::uno::Reference< css::uno::XComponentContext > & getComponentContext() const
static void ImplDateIncrementDay(Date &rDate, bool bUp)
Definition: field2.cxx:1594
weld::Entry & m_rEntry
Definition: weldutils.hxx:379
constexpr sal_uInt16 KEY_DELETE
Definition: keycodes.hxx:125
#define EDITMASK_LITERAL
Definition: field2.cxx:49
constexpr sal_uInt16 KEY_LEFT
Definition: keycodes.hxx:112
void MakeTimeFromMS(sal_Int32 nMS)
virtual void SetText(const OUString &rStr) override
Definition: edit.cxx:2546
OUString getTime(const tools::Time &rTime, bool bSec=true, bool b100Sec=false) const
constexpr sal_uInt16 KEY_INSERT
Definition: keycodes.hxx:124
void SetMax(const tools::Time &rNewMax)
Definition: field2.cxx:2702
const Date & GetNullDate() const
TriState
void ImplDateSpinArea(bool bUp)
Definition: field2.cxx:1647
void loadCalendar(const OUString &rUniqueID, const css::lang::Locale &rLocale, bool bTimeZoneUTC=true)
aStr
static bool ImplIsValidTimePortion(bool _bSkipInvalidCharacters, const OUStringBuffer &_rStr)
Definition: field2.cxx:2305
void SetDate(const Date &rNewDate)
Definition: field2.cxx:1851
#define PATTERN_FORMAT_EMPTYLITERALS
Definition: field.hxx:523
void GetOutputString(const double &fOutNumber, sal_uInt32 nFIndex, OUString &sOutString, const Color **ppColor, bool bUseStarFormat=false)
sal_uInt16 GetYearUnsigned() const
css::uno::Sequence< css::i18n::CalendarItem2 > getGenitiveMonths() const
sal_uInt16 nPos
sal_Int16 nValue
void SetFormat(LotusContext &rContext, SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt8 nFormat, sal_uInt8 nSt)
void SetMin(const tools::Time &rNewMin)
Definition: field2.cxx:2695
virtual void Modify() override
Definition: combobox.cxx:821
bool IsMod2() const
Definition: keycod.hxx:60
OStringBuffer & padToLength(OStringBuffer &rBuffer, sal_Int32 nLength, char cFill= '\0')
static sal_Int32 ImplPatternRightPos(const OUString &rStr, const OString &rEditMask, sal_uInt16 nFormatFlags, bool bSameMask, sal_Int32 nCursorPos)
Definition: field2.cxx:483
static void ImplDateIncrementYear(Date &rDate, bool bUp)
Definition: field2.cxx:1606
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo