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