Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/vcl/source/control/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 94 kB image not shown  

Quelle  field2.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <sal/config.h>

#include <algorithm>
#include <string_view>

#include <comphelper/diagnose_ex.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/string.hxx>
#include <o3tl/string_view.hxx>
#include <officecfg/Office/Common.hxx>
#include <vcl/svapp.hxx>
#include <vcl/event.hxx>
#include <vcl/toolkit/field.hxx>
#include <vcl/unohelp.hxx>
#include <vcl/settings.hxx>
#include <vcl/weldutils.hxx>

#include <svdata.hxx>

#include <com/sun/star/i18n/XCharacterClassification.hpp>
#include <com/sun/star/i18n/CalendarFieldIndex.hdl>

#include <unotools/localedatawrapper.hxx>
#include <unotools/calendarwrapper.hxx>
#include <unotools/charclass.hxx>
#include <svl/numformat.hxx>
#include <svl/zforlist.hxx>
#include <tools/duration.hxx>

using namespace ::com::sun::star;
using namespace ::comphelper;

#define EDITMASK_LITERAL       'L'
#define EDITMASK_ALPHA         'a'
#define EDITMASK_UPPERALPHA    'A'
#define EDITMASK_ALPHANUM      'c'
#define EDITMASK_UPPERALPHANUM 'C'
#define EDITMASK_NUM           'N'
#define EDITMASK_NUMSPACE      'n'
#define EDITMASK_ALLCHAR       'x'
#define EDITMASK_UPPERALLCHAR  'X'

uno::Reference< i18n::XCharacterClassification > const & ImplGetCharClass()
{
    ImplSVData *const pSVData = ImplGetSVData();
    assert(pSVData);

    if (!pSVData->m_xCharClass.is())
    {
        pSVData->m_xCharClass = vcl::unohelper::CreateCharacterClassification();
    }

    return pSVData->m_xCharClass;
}

static sal_Unicode* ImplAddString( sal_Unicode* pBuf, const OUString& rStr )
{
    memcpy( pBuf, rStr.getStr(), rStr.getLength() * sizeof(sal_Unicode) );
    pBuf += rStr.getLength();
    return pBuf;
}

static sal_Unicode* ImplAddNum( sal_Unicode* pBuf, sal_uLong nNumber, int nMinLen )
{
    // fill temp buffer with digits
    sal_Unicode aTempBuf[30];
    sal_Unicode* pTempBuf = aTempBuf;
    do
    {
        *pTempBuf = static_cast<sal_Unicode>(nNumber % 10) + '0';
        pTempBuf++;
        nNumber /= 10;
        if ( nMinLen )
            nMinLen--;
    }
    while ( nNumber );

    // fill with zeros up to the minimal length
    while ( nMinLen > 0 )
    {
        *pBuf = '0';
        pBuf++;
        nMinLen--;
    }

    // copy temp buffer to real buffer
    do
    {
        pTempBuf--;
        *pBuf = *pTempBuf;
        pBuf++;
    }
    while ( pTempBuf != aTempBuf );

    return pBuf;
}

static sal_Unicode* ImplAddSNum( sal_Unicode* pBuf, sal_Int32 nNumber, int nMinLen )
{
    if (nNumber < 0)
    {
        *pBuf++ = '-';
        nNumber = -nNumber;
    }
    return ImplAddNum( pBuf, nNumber, nMinLen);
}

static sal_uInt16 ImplGetNum( const sal_Unicode*& rpBuf, bool& rbError )
{
    if ( !*rpBuf )
    {
        rbError = true;
        return 0;
    }

    sal_uInt16 nNumber = 0;
    while( ( *rpBuf >= '0' ) && ( *rpBuf <= '9' ) )
    {
        nNumber *= 10;
        nNumber += *rpBuf - '0';
        rpBuf++;
    }

    return nNumber;
}

static void ImplSkipDelimiters( const sal_Unicode*& rpBuf )
{
    while( ( *rpBuf == ',' ) || ( *rpBuf == '.' ) || ( *rpBuf == ';' ) ||
           ( *rpBuf == ':' ) || ( *rpBuf == '-' ) || ( *rpBuf == '/' ) )
    {
        rpBuf++;
    }
}

static bool ImplIsPatternChar( sal_Unicode cChar, char cEditMask )
{
    sal_Int32 nType = 0;

    try
    {
        OUString aCharStr(cChar);
        nType = ImplGetCharClass()->getCharacterType( aCharStr, 0,
                Application::GetSettings().GetLanguageTag().getLocale() );
    }
    catch (const css::uno::Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("vcl.control");
        return false;
    }

    if ( (cEditMask == EDITMASK_ALPHA) || (cEditMask == EDITMASK_UPPERALPHA) )
    {
        if( !CharClass::isLetterType( nType ) )
            return false;
    }
    else if ( cEditMask == EDITMASK_NUM )
    {
        if( !CharClass::isNumericType( nType ) )
            return false;
    }
    else if ( (cEditMask == EDITMASK_ALPHANUM) || (cEditMask == EDITMASK_UPPERALPHANUM) )
    {
        if( !CharClass::isLetterNumericType( nType ) )
            return false;
    }
    else if ( (cEditMask == EDITMASK_ALLCHAR) || (cEditMask == EDITMASK_UPPERALLCHAR) )
    {
        if ( cChar < 32 )
            return false;
    }
    else if ( cEditMask == EDITMASK_NUMSPACE )
    {
        if ( !CharClass::isNumericType( nType ) && ( cChar != ' ' ) )
            return false;
    }
    else
        return false;

    return true;
}

static sal_Unicode ImplPatternChar( sal_Unicode cChar, char cEditMask )
{
    if ( ImplIsPatternChar( cChar, cEditMask ) )
    {
        if ( (cEditMask == EDITMASK_UPPERALPHA) ||
             (cEditMask == EDITMASK_UPPERALPHANUM) ||
             ( cEditMask == EDITMASK_UPPERALLCHAR ) )
        {
            cChar = ImplGetCharClass()->toUpper(OUString(cChar), 0, 1,
                    Application::GetSettings().GetLanguageTag().getLocale())[0];
        }
        return cChar;
    }
    else
        return 0;
}

static bool ImplCommaPointCharEqual( sal_Unicode c1, sal_Unicode c2 )
{
    if ( c1 == c2 )
        return true;
    else if ( ((c1 == '.') || (c1 == ',')) &&
              ((c2 == '.') || (c2 == ',')) )
        return true;
    else
        return false;
}

static OUString ImplPatternReformat( const OUString& rStr,
                                     const OString& rEditMask,
                                     std::u16string_view rLiteralMask,
                                     sal_uInt16 nFormatFlags )
{
    if (rEditMask.isEmpty())
        return rStr;

    OUStringBuffer    aOutStr(rLiteralMask);
    sal_Unicode cTempChar;
    sal_Unicode cChar;
    sal_Unicode cLiteral;
    char    cMask;
    sal_Int32   nStrIndex = 0;
    sal_Int32   i = 0;
    sal_Int32   n;

    while ( i < rEditMask.getLength() )
    {
        if ( nStrIndex >= rStr.getLength() )
            break;

        cChar = rStr[nStrIndex];
        cLiteral = rLiteralMask[i];
        cMask = rEditMask[i];

        // current position is a literal
        if ( cMask == EDITMASK_LITERAL )
        {
            // if it is a literal copy otherwise ignore because it might be the next valid
            // character of the string
            if ( ImplCommaPointCharEqual( cChar, cLiteral ) )
                nStrIndex++;
            else
            {
                // Otherwise we check if it is an invalid character. This is the case if it does not
                // fit in the pattern of the next non-literal character.
                n = i+1;
                while ( n < rEditMask.getLength() )
                {
                    if ( rEditMask[n] != EDITMASK_LITERAL )
                    {
                        if ( !ImplIsPatternChar( cChar, rEditMask[n] ) )
                            nStrIndex++;
                        break;
                    }

                    n++;
                }
            }
        }
        else
        {
            // valid character at this position
            cTempChar = ImplPatternChar( cChar, cMask );
            if ( cTempChar )
            {
                // use this character
                aOutStr[i] = cTempChar;
                nStrIndex++;
            }
            else
            {
                // copy if it is a literal character
                if ( cLiteral == cChar )
                    nStrIndex++;
                else
                {
                    // If the invalid character might be the next literal character then we jump
                    // ahead to it, otherwise we ignore it. Do only if empty literals are allowed.
                    if ( nFormatFlags & PATTERN_FORMAT_EMPTYLITERALS )
                    {
                        n = i;
                        while ( n < rEditMask.getLength() )
                        {
                            if ( rEditMask[n] == EDITMASK_LITERAL )
                            {
                                if ( ImplCommaPointCharEqual( cChar, rLiteralMask[n] ) )
                                    i = n+1;

                                break;
                            }

                            n++;
                        }
                    }

                    nStrIndex++;
                    continue;
                }
            }
        }

        i++;
    }

    return aOutStr.makeStringAndClear();
}

static void ImplPatternMaxPos( std::u16string_view rStr, const OString& rEditMask,
                               sal_uInt16 nFormatFlags, bool bSameMask,
                               sal_Int32 nCursorPos, sal_Int32& rPos )
{

    // last position must not be longer than the contained string
    sal_Int32 nMaxPos = rStr.size();

    // if non empty literals are allowed ignore blanks at the end as well
    if ( bSameMask && !(nFormatFlags & PATTERN_FORMAT_EMPTYLITERALS) )
    {
        while ( nMaxPos )
        {
            if ( (rEditMask[nMaxPos-1] != EDITMASK_LITERAL) &&
                 (rStr[nMaxPos-1] != ' ') )
                break;
            nMaxPos--;
        }

        // if we are in front of a literal, continue search until first character after the literal
        sal_Int32 nTempPos = nMaxPos;
        while ( nTempPos < rEditMask.getLength() )
        {
            if ( rEditMask[nTempPos] != EDITMASK_LITERAL )
            {
                nMaxPos = nTempPos;
                break;
            }
            nTempPos++;
        }
    }

    if ( rPos > nMaxPos )
        rPos = nMaxPos;

    // character should not move left
    if ( rPos < nCursorPos )
        rPos = nCursorPos;
}

static OUString ImplPatternProcessStrictModify(const OUString& rText,
                                               const OString& rEditMask,
                                               std::u16string_view rLiteralMask,
                                               bool bSameMask)
{
    OUString aText(rText);

    // remove leading blanks
    if (bSameMask && !rEditMask.isEmpty())
    {
        sal_Int32 i = 0;
        sal_Int32 nMaxLen = aText.getLength();
        while ( i < nMaxLen )
        {
            if ( (rEditMask[i] != EDITMASK_LITERAL) &&
                 (aText[i] != ' ') )
                break;

            i++;
        }
        // keep all literal characters
        while ( i && (rEditMask[i] == EDITMASK_LITERAL) )
            i--;
        aText = aText.copy( i );
    }

    return ImplPatternReformat(aText, rEditMask, rLiteralMask, 0);
}

static void ImplPatternProcessStrictModify( Edit* pEdit,
                                            const OString& rEditMask,
                                            std::u16string_view rLiteralMask,
                                            bool bSameMask )
{
    OUString aText = pEdit->GetText();
    OUString aNewText = ImplPatternProcessStrictModify(aText,
                                                       rEditMask,
                                                       rLiteralMask,
                                                       bSameMask);

    if ( aNewText == aText )
        return;

    // adjust selection such that it remains at the end if it was there before
    Selection aSel = pEdit->GetSelection();
    sal_Int64 nMaxSel = std::max( aSel.Min(), aSel.Max() );
    if ( nMaxSel >= aText.getLength() )
    {
        sal_Int32 nMaxPos = aNewText.getLength();
        ImplPatternMaxPos(aNewText, rEditMask, 0, bSameMask, nMaxSel, nMaxPos);
        if ( aSel.Min() == aSel.Max() )
        {
            aSel.Min() = nMaxPos;
            aSel.Max() = aSel.Min();
        }
        else if ( aSel.Min() > aSel.Max() )
            aSel.Min() = nMaxPos;
        else
            aSel.Max() = nMaxPos;
    }
    pEdit->SetText( aNewText, aSel );
}

static void ImplPatternProcessStrictModify( weld::Entry& rEntry,
                                            const OString& rEditMask,
                                            std::u16string_view rLiteralMask,
                                            bool bSameMask )
{
    OUString aText = rEntry.get_text();
    OUString aNewText = ImplPatternProcessStrictModify(aText,
                                                       rEditMask,
                                                       rLiteralMask,
                                                       bSameMask);

    if (aNewText == aText)
        return;

    // adjust selection such that it remains at the end if it was there before
    int nStartPos, nEndPos;
    rEntry.get_selection_bounds(nStartPos, nEndPos);

    int nMaxSel = std::max(nStartPos, nEndPos);
    if (nMaxSel >= aText.getLength())
    {
        sal_Int32 nMaxPos = aNewText.getLength();
        ImplPatternMaxPos(aNewText, rEditMask, 0, bSameMask, nMaxSel, nMaxPos);
        if (nStartPos == nEndPos)
        {
            nStartPos = nMaxPos;
            nEndPos = nMaxPos;
        }
        else if (nStartPos > nMaxPos)
            nStartPos = nMaxPos;
        else
            nEndPos = nMaxPos;
    }
    rEntry.set_text(aNewText);
    rEntry.select_region(nStartPos, nEndPos);
}

static sal_Int32 ImplPatternLeftPos(std::string_view rEditMask, sal_Int32 nCursorPos)
{
    // search non-literal predecessor
    sal_Int32 nNewPos = nCursorPos;
    sal_Int32 nTempPos = nNewPos;
    while ( nTempPos )
    {
        if ( rEditMask[nTempPos-1] != EDITMASK_LITERAL )
        {
            nNewPos = nTempPos-1;
            break;
        }
        nTempPos--;
    }
    return nNewPos;
}

static sal_Int32 ImplPatternRightPos( std::u16string_view rStr, const OString& rEditMask,
                                       sal_uInt16 nFormatFlags, bool bSameMask,
                                       sal_Int32 nCursorPos )
{
    // search non-literal successor
    sal_Int32 nNewPos = nCursorPos;
    ;
    for(sal_Int32 nTempPos = nNewPos+1; nTempPos < rEditMask.getLength(); ++nTempPos )
    {
        if ( rEditMask[nTempPos] != EDITMASK_LITERAL )
        {
            nNewPos = nTempPos;
            break;
        }
    }
    ImplPatternMaxPos( rStr, rEditMask, nFormatFlags, bSameMask, nCursorPos, nNewPos );
    return nNewPos;
}

namespace
{
    class IEditImplementation
    {
    public:
        virtual ~IEditImplementation() {}

        virtual OUString GetText() const = 0;
        virtual void SetText(const OUString& rStr, const Selection& rSelection) = 0;

        virtual Selection GetSelection() const = 0;
        virtual void SetSelection(const Selection& rSelection) = 0;

        virtual bool IsInsertMode() const = 0;

        virtual void SetModified() = 0;
    };
}

static bool ImplPatternProcessKeyInput( IEditImplementation& rEdit, const KeyEvent&&nbsp;rKEvt,
                                        const OString& rEditMask,
                                        std::u16string_view rLiteralMask,
                                        bool bStrictFormat,
                                        bool bSameMask,
                                        bool& rbInKeyInput )
{
    if ( rEditMask.isEmpty() || !bStrictFormat )
        return false;

    sal_uInt16 nFormatFlags = 0;
    Selection   aOldSel     = rEdit.GetSelection();
    vcl::KeyCode aCode      = rKEvt.GetKeyCode();
    sal_Unicode cChar       = rKEvt.GetCharCode();
    sal_uInt16      nKeyCode    = aCode.GetCode();
    bool        bShift      = aCode.IsShift();
    sal_Int32  nCursorPos = static_cast<sal_Int32>(aOldSel.Max());
    sal_Int32  nNewPos;
    sal_Int32  nTempPos;

    if ( nKeyCode && !aCode.IsMod1() && !aCode.IsMod2() )
    {
        if ( nKeyCode == KEY_LEFT )
        {
            Selection aSel( ImplPatternLeftPos( rEditMask, nCursorPos ) );
            if ( bShift )
                aSel.Min() = aOldSel.Min();
            rEdit.SetSelection( aSel );
            return true;
        }
        else if ( nKeyCode == KEY_RIGHT )
        {
            // Use the start of selection as minimum; even a small position is allowed in case that
            // all was selected by the focus
            Selection aSel( aOldSel );
            aSel.Normalize();
            nCursorPos = aSel.Min();
            aSel.Max() = ImplPatternRightPos( rEdit.GetText(), rEditMask, nFormatFlags, bSameMask, nCursorPos );
            if ( bShift )
                aSel.Min() = aOldSel.Min();
            else
                aSel.Min() = aSel.Max();
            rEdit.SetSelection( aSel );
            return true;
        }
        else if ( nKeyCode == KEY_HOME )
        {
            // Home is the position of the first non-literal character
            nNewPos = 0;
            while ( (nNewPos < rEditMask.getLength()) &&
                    (rEditMask[nNewPos] == EDITMASK_LITERAL) )
                nNewPos++;

            // Home should not move to the right
            if ( nCursorPos < nNewPos )
                nNewPos = nCursorPos;
            Selection aSel( nNewPos );
            if ( bShift )
                aSel.Min() = aOldSel.Min();
            rEdit.SetSelection( aSel );
            return true;
        }
        else if ( nKeyCode == KEY_END )
        {
            // End is position of last non-literal character
            nNewPos = rEditMask.getLength();
            while ( nNewPos &&
                    (rEditMask[nNewPos-1] == EDITMASK_LITERAL) )
                nNewPos--;
            // Use the start of selection as minimum; even a small position is allowed in case that
            // all was selected by the focus
            Selection aSel( aOldSel );
            aSel.Normalize();
            nCursorPos = static_cast<sal_Int32>(aSel.Min());
            ImplPatternMaxPos( rEdit.GetText(), rEditMask, nFormatFlags, bSameMask, nCursorPos, nNewPos );
            aSel.Max() = nNewPos;
            if ( bShift )
                aSel.Min() = aOldSel.Min();
            else
                aSel.Min() = aSel.Max();
            rEdit.SetSelection( aSel );
            return true;
        }
        else if ( (nKeyCode == KEY_BACKSPACE) || (nKeyCode == KEY_DELETE) )
        {
            OUString          aOldStr( rEdit.GetText() );
            OUStringBuffer    aStr( aOldStr );
            Selection   aSel = aOldSel;

            aSel.Normalize();
            nNewPos = static_cast<sal_Int32>(aSel.Min());

             // if selection then delete it
            if ( aSel.Len() )
            {
                if ( bSameMask )
                    aStr.remove( static_cast<sal_Int32>(aSel.Min()), static_cast<sal_Int32>(aSel.Len()) );
                else
                {
                    std::u16string_view aRep = rLiteralMask.substr( static_cast<sal_Int32>(aSel.Min()), static_cast<sal_Int32>(aSel.Len()) );
                    aStr.remove( aSel.Min(), aRep.size() );
                    aStr.insert( aSel.Min(), aRep );
                }
            }
            else
            {
                if ( nKeyCode == KEY_BACKSPACE )
                {
                    nTempPos = nNewPos;
                    nNewPos = ImplPatternLeftPos( rEditMask, nTempPos );
                }
                else
                    nTempPos = ImplPatternRightPos( aStr, rEditMask, nFormatFlags, bSameMask, nNewPos );

                if ( nNewPos != nTempPos )
                {
                    if ( bSameMask )
                    {
                        if ( rEditMask[nNewPos] != EDITMASK_LITERAL )
                            aStr.remove( nNewPos, 1 );
                    }
                    else
                    {
                        aStr[nNewPos] = rLiteralMask[nNewPos];
                    }
                }
            }

            OUString sStr = aStr.makeStringAndClear();
            if ( aOldStr != sStr )
            {
                if ( bSameMask )
                    sStr = ImplPatternReformat( sStr, rEditMask, rLiteralMask, nFormatFlags );
                rbInKeyInput = true;
                rEdit.SetText( sStr, Selection( nNewPos ) );
                rEdit.SetModified();
                rbInKeyInput = false;
            }
            else
                rEdit.SetSelection( Selection( nNewPos ) );

            return true;
        }
        else if ( nKeyCode == KEY_INSERT )
        {
            // you can only set InsertMode for a PatternField if the
            // mask is equal at all input positions
            if ( !bSameMask )
            {
                return true;
            }
        }
    }

    if ( rKEvt.GetKeyCode().IsMod2() || (cChar < 32) || (cChar == 127) )
        return false;

    Selection aSel = aOldSel;
    aSel.Normalize();
    nNewPos = aSel.Min();

    if ( nNewPos < rEditMask.getLength() )
    {
        sal_Unicode cPattChar = ImplPatternChar( cChar, rEditMask[nNewPos] );
        if ( cPattChar )
            cChar = cPattChar;
        else
        {
            // If no valid character, check if the user wanted to jump to next literal. We do this
            // only if we're after a character, so that literals that were skipped automatically
            // do not influence the position anymore.
            if ( nNewPos &&
                 (rEditMask[nNewPos-1] != EDITMASK_LITERAL) &&
                 !aSel.Len() )
            {
                // search for next character not being a literal
                nTempPos = nNewPos;
                while ( nTempPos < rEditMask.getLength() )
                {
                    if ( rEditMask[nTempPos] == EDITMASK_LITERAL )
                    {
                        // only valid if no literal present
                        if ( (rEditMask[nTempPos+1] != EDITMASK_LITERAL ) &&
                             ImplCommaPointCharEqual( cChar, rLiteralMask[nTempPos] ) )
                        {
                            nTempPos++;
                            ImplPatternMaxPos( rEdit.GetText(), rEditMask, nFormatFlags, bSameMask, nNewPos, nTempPos );
                            if ( nTempPos > nNewPos )
                            {
                                rEdit.SetSelection( Selection( nTempPos ) );
                                return true;
                            }
                        }
                        break;
                    }
                    nTempPos++;
                }
            }

            cChar = 0;
        }
    }
    else
        cChar = 0;
    if ( cChar )
    {
        OUStringBuffer  aStr(rEdit.GetText());
        bool        bError = false;
        if ( bSameMask && rEdit.IsInsertMode() )
        {
            // crop spaces and literals at the end until current position
            sal_Int32 n = aStr.getLength();
            while ( n && (n > nNewPos) )
            {
                if ( (aStr[n-1] != ' ') &&
                     ((n > rEditMask.getLength()) || (rEditMask[n-1] != EDITMASK_LITERAL)) )
                    break;

                n--;
            }
            aStr.truncate( n );

            if ( aSel.Len() )
                aStr.remove( aSel.Min(), aSel.Len() );

            if ( aStr.getLength() < rEditMask.getLength() )
            {
                // possibly extend string until cursor position
                if ( aStr.getLength() < nNewPos )
                    aStr.append( rLiteralMask.substr(aStr.getLength(), nNewPos-aStr.getLength()) );
                if ( nNewPos < aStr.getLength() )
                    aStr.insert( cChar, nNewPos );
                else if ( nNewPos < rEditMask.getLength() )
                    aStr.append(cChar);
                aStr = ImplPatternReformat( aStr.toString(), rEditMask, rLiteralMask, nFormatFlags );
            }
            else
                bError = true;
        }
        else
        {
            if ( aSel.Len() )
            {
                // delete selection
                std::u16string_view aRep = rLiteralMask.substr( aSel.Min(), aSel.Len() );
                aStr.remove( aSel.Min(), aRep.size() );
                aStr.insert( aSel.Min(), aRep );
            }

            if ( nNewPos < aStr.getLength() )
                aStr[nNewPos] = cChar;
            else if ( nNewPos < rEditMask.getLength() )
                aStr.append(cChar);
        }

        if ( !bError )
        {
            rbInKeyInput = true;
            const OUString sStr = aStr.makeStringAndClear();
            Selection aNewSel( ImplPatternRightPos( sStr, rEditMask, nFormatFlags, bSameMask, nNewPos ) );
            rEdit.SetText( sStr, aNewSel );
            rEdit.SetModified();
            rbInKeyInput = false;
        }
    }

    return true;
}

namespace
{
    bool ImplSetMask(const OString& rEditMask, OUString& rLiteralMask)
    {
        bool bSameMask      = true;

        if (rEditMask.getLength() != rLiteralMask.getLength())
        {
            OUStringBuffer aBuf(rLiteralMask);
            if (rEditMask.getLength() < aBuf.getLength())
                aBuf.setLength(rEditMask.getLength());
            else
                comphelper::string::padToLength(aBuf, rEditMask.getLength(), ' ');
            rLiteralMask = aBuf.makeStringAndClear();
        }

        // Strict mode allows only the input mode if only equal characters are allowed as mask and if
        // only spaces are specified which are not allowed by the mask
        sal_Int32   i = 0;
        char    c = 0;
        while ( i < rEditMask.getLength() )
        {
            char cTemp = rEditMask[i];
            if ( cTemp != EDITMASK_LITERAL )
            {
                if ( (cTemp == EDITMASK_ALLCHAR) ||
                     (cTemp == EDITMASK_UPPERALLCHAR) ||
                     (cTemp == EDITMASK_NUMSPACE) )
                {
                    bSameMask = false;
                    break;
                }
                if ( i < rLiteralMask.getLength() )
                {
                    if ( rLiteralMask[i] != ' ' )
                    {
                        bSameMask = false;
                        break;
                    }
                }
                if ( !c )
                    c = cTemp;
                if ( cTemp != c )
                {
                    bSameMask = false;
                    break;
                }
            }
            i++;
        }

        return bSameMask;
    }
}

PatternFormatter::PatternFormatter(Edit* pEdit)
    : FormatterBase(pEdit)
{
    mbSameMask          = true;
    mbInPattKeyInput    = false;
}

PatternFormatter::~PatternFormatter()
{
}

void PatternFormatter::SetMask( const OString& rEditMask,
                                const OUString& rLiteralMask )
{
    m_aEditMask = rEditMask;
    maLiteralMask = rLiteralMask;
    mbSameMask = ImplSetMask(m_aEditMask, maLiteralMask);
    ReformatAll();
}

namespace
{
    class EntryImplementation : public IEditImplementation
    {
    public:
        EntryImplementation(weld::PatternFormatter& rFormatter)
            : m_rFormatter(rFormatter)
            , m_rEntry(rFormatter.get_widget())
        {
        }

        virtual OUString GetText() const override
        {
            return m_rEntry.get_text();
        }

        virtual void SetText(const OUString& rStr, const Selection& rSelection) override
        {
            m_rEntry.set_text(rStr);
            SetSelection(rSelection);
        }

        virtual Selection GetSelection() const override
        {
            int nStartPos, nEndPos;
            m_rEntry.get_selection_bounds(nStartPos, nEndPos);
            return Selection(nStartPos, nEndPos);
        }

        virtual void SetSelection(const Selection& rSelection) override
        {
            auto nMin = rSelection.Min();
            auto nMax = rSelection.Max();
            m_rEntry.select_region(nMin < 0 ? 0 : nMin, nMax == SELECTION_MAX ? -1 : nMax);
        }

        virtual bool IsInsertMode() const override
        {
            return !m_rEntry.get_overwrite_mode();
        }

        virtual void SetModified() override
        {
            m_rFormatter.Modify();
        }

    private:
        weld::PatternFormatter& m_rFormatter;
        weld::Entry& m_rEntry;
    };
}

namespace weld
{
    void PatternFormatter::SetStrictFormat(bool bStrict)
    {
        if (bStrict != m_bStrictFormat)
        {
            m_bStrictFormat = bStrict;
            if (m_bStrictFormat)
                ReformatAll();
        }
    }

    void PatternFormatter::SetMask(const OString& rEditMask,
                                   const OUString& rLiteralMask)
    {
        m_aEditMask = rEditMask;
        m_aLiteralMask = rLiteralMask;
        m_bSameMask = ImplSetMask(m_aEditMask, m_aLiteralMask);
        ReformatAll();
    }

    void PatternFormatter::ReformatAll()
    {
        m_rEntry.set_text(ImplPatternReformat(m_rEntry.get_text(), m_aEditMask, m_aLiteralMask, 0/*nFormatFlags*/));
        if (!m_bSameMask && m_bStrictFormat && m_rEntry.get_editable())
            m_rEntry.set_overwrite_mode(true);
    }

    void PatternFormatter::EntryGainFocus()
    {
        m_bReformat = false;
    }

    void PatternFormatter::EntryLostFocus()
    {
        if (m_bReformat)
            ReformatAll();
    }

    void PatternFormatter::Modify()
    {
        if (!m_bInPattKeyInput)
        {
            if (m_bStrictFormat)
                ImplPatternProcessStrictModify(m_rEntry, m_aEditMask, m_aLiteralMask, m_bSameMask);
            else
                m_bReformat = true;
        }
        m_aModifyHdl.Call(m_rEntry);
    }

    IMPL_LINK(PatternFormatter, KeyInputHdl, const KeyEvent&, rKEvt, bool)
    {
        if (m_aKeyPressHdl.Call(rKEvt))
            return true;
        if (rKEvt.GetKeyCode().IsMod2())
            return false;
        EntryImplementation aAdapt(*this);
        return ImplPatternProcessKeyInput(aAdapt, rKEvt, m_aEditMask, m_aLiteralMask,
                                          m_bStrictFormat,
                                          m_bSameMask, m_bInPattKeyInput);
    }
}

void PatternFormatter::SetString( const OUString& rStr )
{
    if ( GetField() )
    {
        GetField()->SetText( rStr );
        MarkToBeReformatted( false );
    }
}

OUString PatternFormatter::GetString() const
{
    if ( !GetField() )
        return OUString();
    else
        return ImplPatternReformat( GetField()->GetText(), m_aEditMask, maLiteralMask, 0/*nFormatFlags*/ );
}

void PatternFormatter::Reformat()
{
    if ( GetField() )
    {
        ImplSetText( ImplPatternReformat( GetField()->GetText(), m_aEditMask, maLiteralMask, 0/*nFormatFlags*/ ) );
        if ( !mbSameMask && IsStrictFormat() && !GetField()->IsReadOnly() )
            GetField()->SetInsertMode( false );
    }
}

PatternField::PatternField(vcl::Window* pParent, WinBits nWinStyle)
    : SpinField(pParent, nWinStyle)
    , PatternFormatter(this)
{
    Reformat();
}

void PatternField::dispose()
{
    ClearField();
    SpinField::dispose();
}

namespace
{
    class EditImplementation : public IEditImplementation
    {
    public:
        EditImplementation(Edit& rEdit)
            : m_rEdit(rEdit)
        {
        }

        virtual OUString GetText() const override
        {
            return m_rEdit.GetText();
        }

        virtual void SetText(const OUString& rStr, const Selection& rSelection) override
        {
            m_rEdit.SetText(rStr, rSelection);
        }

        virtual Selection GetSelection() const override
        {
            return m_rEdit.GetSelection();
        }

        virtual void SetSelection(const Selection& rSelection) override
        {
            m_rEdit.SetSelection(rSelection);
        }

        virtual bool IsInsertMode() const override
        {
            return m_rEdit.IsInsertMode();
        }

        virtual void SetModified() override
        {
            m_rEdit.SetModifyFlag();
            m_rEdit.Modify();
        }

    private:
        Edit& m_rEdit;
    };
}

bool PatternField::PreNotify( NotifyEvent& rNEvt )
{
    if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
    {
        EditImplementation aAdapt(*GetField());
        if ( ImplPatternProcessKeyInput( aAdapt, *rNEvt.GetKeyEvent(), GetEditMask(), GetLiteralMask(),
                                         IsStrictFormat(),
                                         ImplIsSameMask(), ImplGetInPattKeyInput() ) )
            return true;
    }

    return SpinField::PreNotify( rNEvt );
}

bool PatternField::EventNotify( NotifyEvent& rNEvt )
{
    if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
        MarkToBeReformatted( false );
    else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
    {
        if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
            Reformat();
    }

    return SpinField::EventNotify( rNEvt );
}

void PatternField::Modify()
{
    if ( !ImplGetInPattKeyInput() )
    {
        if ( IsStrictFormat() )
            ImplPatternProcessStrictModify( GetField(), GetEditMask(), GetLiteralMask(), ImplIsSameMask() );
        else
            MarkToBeReformatted( true );
    }

    SpinField::Modify();
}

PatternBox::PatternBox(vcl::Window* pParent, WinBits nWinStyle)
    : ComboBox( pParent, nWinStyle )
    , PatternFormatter(this)
{
    Reformat();
}

void PatternBox::dispose()
{
    ClearField();
    ComboBox::dispose();
}

bool PatternBox::PreNotify( NotifyEvent& rNEvt )
{
    if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
    {
        EditImplementation aAdapt(*GetField());
        if ( ImplPatternProcessKeyInput( aAdapt, *rNEvt.GetKeyEvent(), GetEditMask(), GetLiteralMask(),
                                         IsStrictFormat(),
                                         ImplIsSameMask(), ImplGetInPattKeyInput() ) )
            return true;
    }

    return ComboBox::PreNotify( rNEvt );
}

bool PatternBox::EventNotify( NotifyEvent& rNEvt )
{
    if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
        MarkToBeReformatted( false );
    else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
    {
        if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
            Reformat();
    }

    return ComboBox::EventNotify( rNEvt );
}

void PatternBox::Modify()
{
    if ( !ImplGetInPattKeyInput() )
    {
        if ( IsStrictFormat() )
            ImplPatternProcessStrictModify( GetField(), GetEditMask(), GetLiteralMask(), ImplIsSameMask() );
        else
            MarkToBeReformatted( true );
    }

    ComboBox::Modify();
}

void PatternBox::ReformatAll()
{
    OUString aStr;
    SetUpdateMode( false );
    const sal_Int32 nEntryCount = GetEntryCount();
    for ( sal_Int32 i=0; i < nEntryCount; ++i )
    {
        aStr = ImplPatternReformat( GetEntry( i ), GetEditMask(), GetLiteralMask(), 0/*nFormatFlags*/ );
        RemoveEntryAt(i);
        InsertEntry( aStr, i );
    }
    PatternFormatter::Reformat();
    SetUpdateMode( true );
}

static ExtDateFieldFormat ImplGetExtFormat( LongDateOrder eOld )
{
    switch( eOld )
    {
        case LongDateOrder::YDM:
        case LongDateOrder::DMY:    return ExtDateFieldFormat::ShortDDMMYY;
        case LongDateOrder::MDY:    return ExtDateFieldFormat::ShortMMDDYY;
        case LongDateOrder::YMD:
        default:                    return ExtDateFieldFormat::ShortYYMMDD;
    }
}

static sal_uInt16 ImplCutNumberFromString( OUString& rStr )
{
    sal_Int32 i1 = 0;
    while (i1 != rStr.getLength() && (rStr[i1] < '0' || rStr[i1] > '9')) {
        ++i1;
    }
    sal_Int32 i2 = i1;
    while (i2 != rStr.getLength() && rStr[i2] >= '0' && rStr[i2] <= '9') {
        ++i2;
    }
    sal_Int32 nValue = o3tl::toInt32(rStr.subView(i1, i2-i1));
    rStr = rStr.copy(std::min(i2+1, rStr.getLength()));
    return nValue;
}

static bool ImplCutMonthName( OUString& rStr, std::u16string_view _rLookupMonthName )
{
    sal_Int32 index = 0;
    rStr = rStr.replaceFirst(_rLookupMonthName, "", &index);
    return index >= 0;
}

static sal_uInt16 ImplGetMonthFromCalendarItem( OUString& rStr, const uno::Sequence< i18n::CalendarItem2 >& rMonths )
{
    const sal_uInt16 nMonths = rMonths.getLength();
    for (sal_uInt16 i=0; i < nMonths; ++i)
    {
        // long month name?
        if ( ImplCutMonthName( rStr, rMonths[i].FullName ) )
            return i+1;

        // short month name?
        if ( ImplCutMonthName( rStr, rMonths[i].AbbrevName ) )
            return i+1;
    }
    return 0;
}

static sal_uInt16 ImplCutMonthFromString( OUString& rStr, OUString& rCalendarName,
        const LocaleDataWrapper& rLocaleData, const CalendarWrapper& rCalendarWrapper )
{
    const OUString aDefaultCalendarName( rCalendarWrapper.getUniqueID());
    rCalendarName = aDefaultCalendarName;

    // Search for a month name of the loaded default calendar.
    const uno::Sequence< i18n::CalendarItem2 > aMonths = rCalendarWrapper.getMonths();
    sal_uInt16 nMonth = ImplGetMonthFromCalendarItem( rStr, aMonths);
    if (nMonth > 0)
        return nMonth;

    // And also possessive genitive and partitive month names.
    const uno::Sequence< i18n::CalendarItem2 > aGenitiveMonths = rCalendarWrapper.getGenitiveMonths();
    if (aGenitiveMonths != aMonths)
    {
        nMonth = ImplGetMonthFromCalendarItem( rStr, aGenitiveMonths);
        if (nMonth > 0)
            return nMonth;
    }
    const uno::Sequence< i18n::CalendarItem2 > aPartitiveMonths = rCalendarWrapper.getPartitiveMonths();
    if (aPartitiveMonths != aMonths)
    {
        nMonth = ImplGetMonthFromCalendarItem( rStr, aPartitiveMonths);
        if (nMonth > 0)
            return nMonth;
    }

    // Check if there are more calendars and try them if so, as the long date
    // format is obtained from the number formatter this is possible (e.g.
    // ar_DZ "[~hijri] ...")
    const uno::Sequence< i18n::Calendar2 > aCalendars =  rLocaleData.getAllCalendars();
    if (aCalendars.getLength() > 1)
    {
        for (const auto& rCalendar : aCalendars)
        {
            if (rCalendar.Name != aDefaultCalendarName)
            {
                rCalendarName = rCalendar.Name;

                nMonth = ImplGetMonthFromCalendarItem( rStr, rCalendar.Months);
                if (nMonth > 0)
                    return nMonth;

                if (rCalendar.Months != rCalendar.GenitiveMonths)
                {
                    nMonth = ImplGetMonthFromCalendarItem( rStr, rCalendar.GenitiveMonths);
                    if (nMonth > 0)
                        return nMonth;
                }

                if (rCalendar.Months != rCalendar.PartitiveMonths)
                {
                    nMonth = ImplGetMonthFromCalendarItem( rStr, rCalendar.PartitiveMonths);
                    if (nMonth > 0)
                        return nMonth;
                }

                rCalendarName = aDefaultCalendarName;
            }
        }
    }

    return ImplCutNumberFromString( rStr );
}

static OUString ImplGetDateSep( const LocaleDataWrapper& rLocaleDataWrapper, ExtDateFieldFormat eFormat )
{
    if ( ( eFormat == ExtDateFieldFormat::ShortYYMMDD_DIN5008 ) || ( eFormat == ExtDateFieldFormat::ShortYYYYMMDD_DIN5008 ) )
        return u"-"_ustr;
    else
        return rLocaleDataWrapper.getDateSep();
}

static bool ImplDateProcessKeyInput( const KeyEvent& rKEvt, ExtDateFieldFormat eFormat,
                                     const LocaleDataWrapper& rLocaleDataWrapper  )
{
    sal_Unicode cChar = rKEvt.GetCharCode();
    sal_uInt16 nGroup = rKEvt.GetKeyCode().GetGroup();
    return !((nGroup == KEYGROUP_FKEYS) ||
             (nGroup == KEYGROUP_CURSOR) ||
             (nGroup == KEYGROUP_MISC)||
             ((cChar >= '0') && (cChar <= '9')) ||
             (cChar == ImplGetDateSep( rLocaleDataWrapper, eFormat )[0]));
}

bool DateFormatter::TextToDate(const OUString& rStr, Date& rDate, ExtDateFieldFormat eDateOrder,
                               const LocaleDataWrapper& rLocaleDataWrapper, const CalendarWrapper& rCalendarWrapper)
{
    sal_uInt16 nDay = 0;
    sal_uInt16 nMonth = 0;
    sal_uInt16 nYear = 0;
    bool bError = false;
    OUString aStr( rStr );

    if ( eDateOrder == ExtDateFieldFormat::SystemLong )
    {
        OUString aCalendarName;
        LongDateOrder eFormat = rLocaleDataWrapper.getLongDateOrder();
        switch( eFormat )
        {
            case LongDateOrder::MDY:
                nMonth = ImplCutMonthFromString( aStr, aCalendarName, rLocaleDataWrapper, rCalendarWrapper );
                nDay = ImplCutNumberFromString( aStr );
                nYear  = ImplCutNumberFromString( aStr );
                break;
            case LongDateOrder::DMY:
                nDay = ImplCutNumberFromString( aStr );
                nMonth = ImplCutMonthFromString( aStr, aCalendarName, rLocaleDataWrapper, rCalendarWrapper );
                nYear  = ImplCutNumberFromString( aStr );
                break;
            case LongDateOrder::YDM:
                nYear  = ImplCutNumberFromString( aStr );
                nDay = ImplCutNumberFromString( aStr );
                nMonth = ImplCutMonthFromString( aStr, aCalendarName, rLocaleDataWrapper, rCalendarWrapper );
                break;
            case LongDateOrder::YMD:
            default:
                nYear = ImplCutNumberFromString( aStr );
                nMonth = ImplCutMonthFromString( aStr, aCalendarName, rLocaleDataWrapper, rCalendarWrapper );
                nDay  = ImplCutNumberFromString( aStr );
                break;
        }
        if (aCalendarName != "gregorian")
        {
            // Calendar widget is Gregorian, convert date.
            // Need full date.
            bError = !nDay || !nMonth || !nYear;
            if (!bError)
            {
                CalendarWrapper aCW( rLocaleDataWrapper.getComponentContext());
                aCW.loadCalendar( aCalendarName, rLocaleDataWrapper.getLoadedLanguageTag().getLocale());
                aCW.setDateTime(0.5);   // get rid of current time, set some day noon
                aCW.setValue( i18n::CalendarFieldIndex::DAY_OF_MONTH, nDay);
                aCW.setValue( i18n::CalendarFieldIndex::MONTH, nMonth - 1);
                aCW.setValue( i18n::CalendarFieldIndex::YEAR, nYear);
                bError = !aCW.isValid();
                if (!bError)
                {
                    Date aDate = aCW.getEpochStart() + aCW.getDateTime();
                    nYear = aDate.GetYear();
                    nMonth = aDate.GetMonth();
                    nDay = aDate.GetDay();
                }
            }
        }
    }
    else
    {
        bool bYear = true;

        // Check if year is present:
        OUString aDateSep = ImplGetDateSep( rLocaleDataWrapper, eDateOrder );
        sal_Int32 nSepPos = aStr.indexOf( aDateSep );
        if ( nSepPos < 0 )
            return false;
        nSepPos = aStr.indexOf( aDateSep, nSepPos+1 );
        if ( ( nSepPos < 0 ) || ( nSepPos == (aStr.getLength()-1) ) )
        {
            bYear = false;
            nYear = Date( Date::SYSTEM ).GetYearUnsigned();
        }

        const sal_Unicode* pBuf = aStr.getStr();
        ImplSkipDelimiters( pBuf );

        switch ( eDateOrder )
        {
            case ExtDateFieldFormat::ShortDDMMYY:
            case ExtDateFieldFormat::ShortDDMMYYYY:
            {
                nDay = ImplGetNum( pBuf, bError );
                ImplSkipDelimiters( pBuf );
                nMonth = ImplGetNum( pBuf, bError );
                ImplSkipDelimiters( pBuf );
                if ( bYear )
                    nYear = ImplGetNum( pBuf, bError );
            }
            break;
            case ExtDateFieldFormat::ShortMMDDYY:
            case ExtDateFieldFormat::ShortMMDDYYYY:
            {
                nMonth = ImplGetNum( pBuf, bError );
                ImplSkipDelimiters( pBuf );
                nDay = ImplGetNum( pBuf, bError );
                ImplSkipDelimiters( pBuf );
                if ( bYear )
                    nYear = ImplGetNum( pBuf, bError );
            }
            break;
            case ExtDateFieldFormat::ShortYYMMDD:
            case ExtDateFieldFormat::ShortYYYYMMDD:
            case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
            case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
            {
                if ( bYear )
                    nYear = ImplGetNum( pBuf, bError );
                ImplSkipDelimiters( pBuf );
                nMonth = ImplGetNum( pBuf, bError );
                ImplSkipDelimiters( pBuf );
                nDay = ImplGetNum( pBuf, bError );
            }
            break;

            default:
            {
                OSL_FAIL( "DateOrder???" );
            }
        }
    }

    if ( bError || !nDay || !nMonth )
        return false;

    Date aNewDate( nDay, nMonth, nYear );
    DateFormatter::ExpandCentury( aNewDate, officecfg::Office::Common::DateFormat::TwoDigitYear::get() );
    if ( aNewDate.IsValidDate() )
    {
        rDate = aNewDate;
        return true;
    }
    return false;
}

void DateFormatter::ImplDateReformat( const OUString& rStr, OUString& rOutStr )
{
    Date aDate( Date::EMPTY );
    if (!TextToDate(rStr, aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper()))
        return;

    Date aTempDate = aDate;
    if ( aTempDate > GetMax() )
        aTempDate = GetMax();
    else if ( aTempDate < GetMin() )
        aTempDate = GetMin();

    rOutStr = ImplGetDateAsText( aTempDate );
}

namespace
{
    ExtDateFieldFormat ResolveSystemFormat(ExtDateFieldFormat eDateFormat, const LocaleDataWrapper& rLocaleData)
    {
        if (eDateFormat <= ExtDateFieldFormat::SystemShortYYYY)
        {
            bool bShowCentury = (eDateFormat == ExtDateFieldFormat::SystemShortYYYY);
            switch (rLocaleData.getDateOrder())
            {
                case DateOrder::DMY:
                    eDateFormat = bShowCentury ? ExtDateFieldFormat::ShortDDMMYYYY : ExtDateFieldFormat::ShortDDMMYY;
                    break;
                case DateOrder::MDY:
                    eDateFormat = bShowCentury ? ExtDateFieldFormat::ShortMMDDYYYY : ExtDateFieldFormat::ShortMMDDYY;
                    break;
                default:
                    eDateFormat = bShowCentury ? ExtDateFieldFormat::ShortYYYYMMDD : ExtDateFieldFormat::ShortYYMMDD;
            }
        }
        return eDateFormat;
    }
}

OUString DateFormatter::FormatDate(const Date& rDate, ExtDateFieldFormat eExtFormat,
                                   const LocaleDataWrapper& rLocaleData,
                                   const Formatter::StaticFormatter& rStaticFormatter)
{
    bool bShowCentury = false;
    switch (eExtFormat)
    {
        case ExtDateFieldFormat::SystemShortYYYY:
        case ExtDateFieldFormat::SystemLong:
        case ExtDateFieldFormat::ShortDDMMYYYY:
        case ExtDateFieldFormat::ShortMMDDYYYY:
        case ExtDateFieldFormat::ShortYYYYMMDD:
        case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
        {
            bShowCentury = true;
        }
        break;
        default:
        {
            bShowCentury = false;
        }
    }

    if ( !bShowCentury )
    {
        // Check if I have to use force showing the century
        sal_uInt16 nTwoDigitYearStart = officecfg::Office::Common::DateFormat::TwoDigitYear::get();
        sal_uInt16 nYear = rDate.GetYearUnsigned();

        // If year is not in double digit range
        if ( (nYear < nTwoDigitYearStart) || (nYear >= nTwoDigitYearStart+100) )
            bShowCentury = true;
    }

    sal_Unicode aBuf[128];
    sal_Unicode* pBuf = aBuf;

    eExtFormat = ResolveSystemFormat(eExtFormat, rLocaleData);

    OUString aDateSep = ImplGetDateSep( rLocaleData, eExtFormat );
    sal_uInt16 nDay = rDate.GetDay();
    sal_uInt16 nMonth = rDate.GetMonth();
    sal_Int16 nYear = rDate.GetYear();
    sal_uInt16 nYearLen = bShowCentury ? 4 : 2;

    if ( !bShowCentury )
        nYear %= 100;

    switch (eExtFormat)
    {
        case ExtDateFieldFormat::SystemLong:
        {
            SvNumberFormatter* pFormatter = rStaticFormatter;
            const LanguageTag aFormatterLang( pFormatter->GetLanguageTag());
            const sal_uInt32 nIndex = pFormatter->GetFormatIndex( NF_DATE_SYSTEM_LONG,
                    rLocaleData.getLanguageTag().getLanguageType(false));
            OUString aStr;
            const Color* pCol;
            pFormatter->GetOutputString( rDate - pFormatter->GetNullDate(), nIndex, aStr, &pCol);
            // Reset to what other uses may expect.
            pFormatter->ChangeIntl( aFormatterLang.getLanguageType(false));
            return aStr;
        }
        case ExtDateFieldFormat::ShortDDMMYY:
        case ExtDateFieldFormat::ShortDDMMYYYY:
        {
            pBuf = ImplAddNum( pBuf, nDay, 2 );
            pBuf = ImplAddString( pBuf, aDateSep );
            pBuf = ImplAddNum( pBuf, nMonth, 2 );
            pBuf = ImplAddString( pBuf, aDateSep );
            pBuf = ImplAddSNum( pBuf, nYear, nYearLen );
        }
        break;
        case ExtDateFieldFormat::ShortMMDDYY:
        case ExtDateFieldFormat::ShortMMDDYYYY:
        {
            pBuf = ImplAddNum( pBuf, nMonth, 2 );
            pBuf = ImplAddString( pBuf, aDateSep );
            pBuf = ImplAddNum( pBuf, nDay, 2 );
            pBuf = ImplAddString( pBuf, aDateSep );
            pBuf = ImplAddSNum( pBuf, nYear, nYearLen );
        }
        break;
        case ExtDateFieldFormat::ShortYYMMDD:
        case ExtDateFieldFormat::ShortYYYYMMDD:
        case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
        case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
        {
            pBuf = ImplAddSNum( pBuf, nYear, nYearLen );
            pBuf = ImplAddString( pBuf, aDateSep );
            pBuf = ImplAddNum( pBuf, nMonth, 2 );
            pBuf = ImplAddString( pBuf, aDateSep );
            pBuf = ImplAddNum( pBuf, nDay, 2 );
        }
        break;
        default:
        {
            OSL_FAIL( "DateOrder???" );
        }
    }

    return OUString(aBuf, pBuf-aBuf);
}

OUString DateFormatter::ImplGetDateAsText( const Date& rDate ) const
{
    return DateFormatter::FormatDate(rDate, GetExtDateFormat(), ImplGetLocaleDataWrapper(), maStaticFormatter);
}

static void ImplDateIncrementDay( Date& rDate, bool bUp )
{
    DateFormatter::ExpandCentury( rDate );
    rDate.AddDays( bUp ? 1 : -1 );
}

static void ImplDateIncrementMonth( Date& rDate, bool bUp )
{
    DateFormatter::ExpandCentury( rDate );
    rDate.AddMonths( bUp ? 1 : -1 );
}

static void ImplDateIncrementYear( Date& rDate, bool bUp )
{
    DateFormatter::ExpandCentury( rDate );
    rDate.AddYears( bUp ? 1 : -1 );
}

bool DateFormatter::ImplAllowMalformedInput() const
{
    return !IsEnforceValidValue();
}

int DateFormatter::GetDateArea(ExtDateFieldFormat& eFormat, std::u16string_view rText, int nCursor, const LocaleDataWrapper& rLocaleDataWrapper)
{
    sal_Int8 nDateArea = 0;

    if ( eFormat == ExtDateFieldFormat::SystemLong )
    {
        eFormat = ImplGetExtFormat(rLocaleDataWrapper.getLongDateOrder());
        nDateArea = 1;
    }
    else
    {
        // search area
        size_t nPos = 0;
        OUString aDateSep = ImplGetDateSep(rLocaleDataWrapper, eFormat);
        for ( sal_Int8 i = 1; i <= 3; i++ )
        {
            nPos = rText.find( aDateSep, nPos );
            if (nPos == std::u16string_view::npos || static_cast<sal_Int32>(nPos) >= nCursor)
            {
                nDateArea = i;
                break;
            }
            else
                nPos++;
        }
    }

    return nDateArea;
}

void DateField::ImplDateSpinArea( bool bUp )
{
    // increment days if all is selected
    if ( !GetField() )
        return;

    Date aDate( GetDate() );
    Selection aSelection = GetField()->GetSelection();
    aSelection.Normalize();
    OUString aText( GetText() );
    if ( static_cast<sal_Int32>(aSelection.Len()) == aText.getLength() )
        ImplDateIncrementDay( aDate, bUp );
    else
    {
        ExtDateFieldFormat eFormat = GetExtDateFormat( true );
        sal_Int8 nDateArea = GetDateArea(eFormat, aText, aSelection.Max(), ImplGetLocaleDataWrapper());

        switch( eFormat )
        {
            case ExtDateFieldFormat::ShortMMDDYY:
            case ExtDateFieldFormat::ShortMMDDYYYY:
            switch( nDateArea )
            {
                case 1: ImplDateIncrementMonth( aDate, bUp );
                        break;
                case 2: ImplDateIncrementDay( aDate, bUp );
                        break;
                case 3: ImplDateIncrementYear( aDate, bUp );
                        break;
            }
            break;
            case ExtDateFieldFormat::ShortDDMMYY:
            case ExtDateFieldFormat::ShortDDMMYYYY:
            switch( nDateArea )
            {
                case 1: ImplDateIncrementDay( aDate, bUp );
                        break;
                case 2: ImplDateIncrementMonth( aDate, bUp );
                        break;
                case 3: ImplDateIncrementYear( aDate, bUp );
                        break;
            }
            break;
            case ExtDateFieldFormat::ShortYYMMDD:
            case ExtDateFieldFormat::ShortYYYYMMDD:
            case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
            case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
            switch( nDateArea )
            {
                case 1: ImplDateIncrementYear( aDate, bUp );
                        break;
                case 2: ImplDateIncrementMonth( aDate, bUp );
                        break;
                case 3: ImplDateIncrementDay( aDate, bUp );
                        break;
            }
            break;
            default:
                OSL_FAIL( "invalid conversion" );
                break;
        }
    }

    ImplNewFieldValue( aDate );
}

DateFormatter::DateFormatter(Edit* pEdit)
    : FormatterBase(pEdit)
    , maFieldDate(0)
    , maLastDate(0)
    , maMin(1, 1, 1900)
    , maMax(31, 12, 2200)
    , mbLongFormat(false)
    , mbShowDateCentury(true)
    , mnExtDateFormat(ExtDateFieldFormat::SystemShort)
    , mbEnforceValidValue(true)
{
}

DateFormatter::~DateFormatter()
{
}

CalendarWrapper& DateFormatter::GetCalendarWrapper() const
{
    if (!mxCalendarWrapper)
    {
        const_cast<DateFormatter*>(this)->mxCalendarWrapper.reset( new CalendarWrapper( comphelper::getProcessComponentContext() ) );
        mxCalendarWrapper->loadDefaultCalendar( GetLocale() );
    }

    return *mxCalendarWrapper;
}

void DateFormatter::SetExtDateFormat( ExtDateFieldFormat eFormat )
{
    mnExtDateFormat = eFormat;
    ReformatAll();
}

ExtDateFieldFormat DateFormatter::GetExtDateFormat( bool bResolveSystemFormat ) const
{
    ExtDateFieldFormat eDateFormat = mnExtDateFormat;

    if (bResolveSystemFormat)
        eDateFormat = ResolveSystemFormat(eDateFormat, ImplGetLocaleDataWrapper());

    return eDateFormat;
}

void DateFormatter::ReformatAll()
{
    Reformat();
}

void DateFormatter::SetMin( const Date& rNewMin )
{
    maMin = rNewMin;
    if ( !IsEmptyFieldValue() )
        ReformatAll();
}

void DateFormatter::SetMax( const Date& rNewMax )
{
    maMax = rNewMax;
    if ( !IsEmptyFieldValue() )
        ReformatAll();
}

void DateFormatter::SetLongFormat( bool bLong )
{
    mbLongFormat = bLong;

    // #91913# Remove LongFormat and DateShowCentury - redundant
    if ( bLong )
    {
        SetExtDateFormat( ExtDateFieldFormat::SystemLong );
    }
    else
    {
        if( mnExtDateFormat == ExtDateFieldFormat::SystemLong )
            SetExtDateFormat( ExtDateFieldFormat::SystemShort );
    }

    ReformatAll();
}

namespace
{
    ExtDateFieldFormat ChangeDateCentury(ExtDateFieldFormat eExtDateFormat, bool bShowDateCentury)
    {
        // #91913# Remove LongFormat and DateShowCentury - redundant
        if (bShowDateCentury)
        {
            switch (eExtDateFormat)
            {
                case ExtDateFieldFormat::SystemShort:
                case ExtDateFieldFormat::SystemShortYY:
                    eExtDateFormat = ExtDateFieldFormat::SystemShortYYYY;  break;
                case ExtDateFieldFormat::ShortDDMMYY:
                    eExtDateFormat = ExtDateFieldFormat::ShortDDMMYYYY;     break;
                case ExtDateFieldFormat::ShortMMDDYY:
                    eExtDateFormat = ExtDateFieldFormat::ShortMMDDYYYY;     break;
                case ExtDateFieldFormat::ShortYYMMDD:
                    eExtDateFormat = ExtDateFieldFormat::ShortYYYYMMDD;     break;
                case ExtDateFieldFormat::ShortYYMMDD_DIN5008:
                    eExtDateFormat = ExtDateFieldFormat::ShortYYYYMMDD_DIN5008; break;
                default:
                    ;
            }
        }
        else
        {
            switch (eExtDateFormat)
            {
                case ExtDateFieldFormat::SystemShort:
                case ExtDateFieldFormat::SystemShortYYYY:
                    eExtDateFormat = ExtDateFieldFormat::SystemShortYY;    break;
                case ExtDateFieldFormat::ShortDDMMYYYY:
                    eExtDateFormat = ExtDateFieldFormat::ShortDDMMYY;       break;
                case ExtDateFieldFormat::ShortMMDDYYYY:
                    eExtDateFormat = ExtDateFieldFormat::ShortMMDDYY;       break;
                case ExtDateFieldFormat::ShortYYYYMMDD:
                    eExtDateFormat = ExtDateFieldFormat::ShortYYMMDD;       break;
                case ExtDateFieldFormat::ShortYYYYMMDD_DIN5008:
                    eExtDateFormat = ExtDateFieldFormat::ShortYYMMDD_DIN5008;  break;
                default:
                    ;
            }
        }

        return eExtDateFormat;
    }
}

void DateFormatter::SetShowDateCentury( bool bShowDateCentury )
{
    mbShowDateCentury = bShowDateCentury;

    SetExtDateFormat(ChangeDateCentury(GetExtDateFormat(), bShowDateCentury));

    ReformatAll();
}

void DateFormatter::SetDate( const Date& rNewDate )
{
    ImplSetUserDate( rNewDate );
    maFieldDate = maLastDate;
    maLastDate = GetDate();
}

void DateFormatter::ImplSetUserDate( const Date& rNewDate, Selection const * pNewSelection )
{
    Date aNewDate = rNewDate;
    if ( aNewDate > maMax )
        aNewDate = maMax;
    else if ( aNewDate < maMin )
        aNewDate = maMin;
    maLastDate = aNewDate;

    if ( GetField() )
        ImplSetText( ImplGetDateAsText( aNewDate ), pNewSelection );
}

void DateFormatter::ImplNewFieldValue( const Date& rDate )
{
    if ( !GetField() )
        return;

    Selection aSelection = GetField()->GetSelection();
    aSelection.Normalize();
    OUString aText = GetField()->GetText();

    // If selected until the end then keep it that way
    if ( static_cast<sal_Int32>(aSelection.Max()) == aText.getLength() )
    {
        if ( !aSelection.Len() )
            aSelection.Min() = SELECTION_MAX;
        aSelection.Max() = SELECTION_MAX;
    }

    Date aOldLastDate  = maLastDate;
    ImplSetUserDate( rDate, &aSelection );
    maLastDate = aOldLastDate;

    // Modify at Edit is only set at KeyInput
    if ( GetField()->GetText() != aText )
    {
        GetField()->SetModifyFlag();
        GetField()->Modify();
    }
}

Date DateFormatter::GetDate() const
{
    Date aDate( Date::EMPTY );

    if ( GetField() )
    {
        if (TextToDate(GetField()->GetText(), aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper()))
        {
            if ( aDate > maMax )
                aDate = maMax;
            else if ( aDate < maMin )
                aDate = maMin;
        }
        else
        {
            // !!! We should find out why dates are treated differently than other fields (see
            // also bug: 52384)

            if ( !ImplAllowMalformedInput() )
            {
                if ( maLastDate.GetDate() )
                    aDate = maLastDate;
                else if ( !IsEmptyFieldValueEnabled() )
                    aDate = Date( Date::SYSTEM );
            }
            else
                aDate = Date( Date::EMPTY ); // set invalid date
        }
    }

    return aDate;
}

void DateFormatter::SetEmptyDate()
{
    FormatterBase::SetEmptyFieldValue();
}

bool DateFormatter::IsEmptyDate() const
{
    bool bEmpty = FormatterBase::IsEmptyFieldValue();

    if ( GetField() && MustBeReformatted() && IsEmptyFieldValueEnabled() )
    {
        if ( GetField()->GetText().isEmpty() )
        {
            bEmpty = true;
        }
        else if ( !maLastDate.GetDate() )
        {
            Date aDate( Date::EMPTY );
            bEmpty = !TextToDate(GetField()->GetText(), aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper());
        }
    }
    return bEmpty;
}

void DateFormatter::Reformat()
{
    if ( !GetField() )
        return;

    if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
        return;

    OUString aStr;
    ImplDateReformat( GetField()->GetText(), aStr );

    if ( !aStr.isEmpty() )
    {
        ImplSetText( aStr );
        (void)TextToDate(aStr, maLastDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper());
    }
    else
    {
        if ( maLastDate.GetDate() )
            SetDate( maLastDate );
        else if ( !IsEmptyFieldValueEnabled() )
            SetDate( Date( Date::SYSTEM ) );
        else
        {
            ImplSetText( OUString() );
            SetEmptyFieldValueData( true );
        }
    }
}

void DateFormatter::ExpandCentury( Date& rDate )
{
    ExpandCentury(rDate, officecfg::Office::Common::DateFormat::TwoDigitYear::get());
}

void DateFormatter::ExpandCentury( Date& rDate, sal_uInt16 nTwoDigitYearStart )
{
    sal_Int16 nDateYear = rDate.GetYear();
    if ( 0 <= nDateYear && nDateYear < 100 )
    {
        sal_uInt16 nCentury = nTwoDigitYearStart / 100;
        if ( nDateYear < (nTwoDigitYearStart % 100) )
            nCentury++;
        rDate.SetYear( nDateYear + (nCentury*100) );
    }
}

DateField::DateField( vcl::Window* pParent, WinBits nWinStyle ) :
    SpinField(pParent, nWinStyle, WindowType::DATEFIELD),
    DateFormatter(this),
    maFirst( GetMin() ),
    maLast( GetMax() )
{
    SetText( ImplGetLocaleDataWrapper().getDate( ImplGetFieldDate() ) );
    Reformat();
    ResetLastDate();
}

void DateField::dispose()
{
    ClearField();
    SpinField::dispose();
}

bool DateField::PreNotify( NotifyEvent& rNEvt )
{
    if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && IsStrictFormat() &&
         ( GetExtDateFormat() != ExtDateFieldFormat::SystemLong ) &&
         !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
    {
        if ( ImplDateProcessKeyInput( *rNEvt.GetKeyEvent(), GetExtDateFormat( true ), ImplGetLocaleDataWrapper() ) )
            return true;
    }

    return SpinField::PreNotify( rNEvt );
}

bool DateField::EventNotify( NotifyEvent& rNEvt )
{
    if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
        MarkToBeReformatted( false );
    else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
    {
        if ( MustBeReformatted() )
        {
            // !!! We should find out why dates are treated differently than other fields (see
            // also bug: 52384)

            bool bTextLen = !GetText().isEmpty();
            if ( bTextLen || !IsEmptyFieldValueEnabled() )
            {
                if ( !ImplAllowMalformedInput() )
                    Reformat();
                else
                {
                    Date aDate( 0, 0, 0 );
                    if (TextToDate(GetText(), aDate, GetExtDateFormat(true), ImplGetLocaleDataWrapper(), GetCalendarWrapper()))
                        // even with strict text analysis, our text is a valid date -> do a complete
                        // reformat
                        Reformat();
                }
            }
            else
            {
                ResetLastDate();
                SetEmptyFieldValueData( true );
            }
        }
    }

    return SpinField::EventNotify( rNEvt );
}

void DateField::DataChanged( const DataChangedEvent& rDCEvt )
{
    SpinField::DataChanged( rDCEvt );

    if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & (AllSettingsFlags::LOCALE|AllSettingsFlags::MISC)) )
    {
        if (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE)
            ImplResetLocaleDataWrapper();
        ReformatAll();
    }
}

void DateField::Modify()
{
    MarkToBeReformatted( true );
    SpinField::Modify();
}

void DateField::Up()
{
    ImplDateSpinArea( true );
    SpinField::Up();
}

void DateField::Down()
{
    ImplDateSpinArea( false );
    SpinField::Down();
}

void DateField::First()
{
    ImplNewFieldValue( maFirst );
    SpinField::First();
}

void DateField::Last()
{
    ImplNewFieldValue( maLast );
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=91 H=96 G=93

¤ Dauer der Verarbeitung: 0.36 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.