    /* -*- 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 <rangelst.hxx>

    #include <sfx2/dispatch.hxx>
    #include <svl/stritem.hxx>
    #include <vcl/weld.hxx>
    #include <unotools/charclass.hxx>
    #include <stdlib.h>

    #include <areasdlg.hxx>
    #include <rangenam.hxx>
    #include <reffact.hxx>
    #include <tabvwsh.hxx>
    #include <docsh.hxx>
    #include <globstr.hrc>
    #include <scresid.hxx>
    #include <compiler.hxx>
    #include <markdata.hxx>

    // List box positions for print range (PR)
    enum {
        SC_AREASDLG_PR_ENTIRE  = 1,
        SC_AREASDLG_PR_USER    = 2,
        SC_AREASDLG_PR_SELECT  = 3
    };

    // List box positions for repeat ranges (RR)
    enum {
        SC_AREASDLG_RR_NONE    = 0,
        SC_AREASDLG_RR_USER    = 1,
        SC_AREASDLG_RR_OFFSET  = 2
    };

    #define HDL(hdl)            LINK( this, ScPrintAreasDlg, hdl )
    namespace
    {
        void ERRORBOX(weld::Window* pParent, const char* rId)
        {
            std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
                                                      VclMessageType::Warning, VclButtonsType::Ok,
                                                      ScResId(rId)));
            xBox->run();
        }
    }

    // global functions (->at the end of the file):

    static bool lcl_CheckRepeatString( const OUString& rStr, const ScDocument* pDoc, bool bIsRow, ScRange* pRange );
    static void lcl_GetRepeatRangeString( const ScRange* pRange, const ScDocument* pDoc, bool bIsRow, OUString& rStr );

    #if 0
    // this method is useful when debugging address flags.
    static void printAddressFlags(ScRefFlags nFlag)
    {
        if ((nFlag & ScRefFlags::COL_ABS      ) == ScRefFlags::COL_ABS      ) printf("ScRefFlags::COL_ABS      \n");
        if ((nFlag & ScRefFlags::ROW_ABS      ) == ScRefFlags::ROW_ABS      ) printf("ScRefFlags::ROW_ABS      \n");
        if ((nFlag & ScRefFlags::TAB_ABS      ) == ScRefFlags::TAB_ABS      ) printf("ScRefFlags::TAB_ABS      \n");
        if ((nFlag & ScRefFlags::TAB_3D       ) == ScRefFlags::TAB_3D       ) printf("ScRefFlags::TAB_3D       \n");
        if ((nFlag & ScRefFlags::COL2_ABS     ) == ScRefFlags::COL2_ABS     ) printf("ScRefFlags::COL2_ABS     \n");
        if ((nFlag & ScRefFlags::ROW2_ABS     ) == ScRefFlags::ROW2_ABS     ) printf("ScRefFlags::ROW2_ABS     \n");
        if ((nFlag & ScRefFlags::TAB2_ABS     ) == ScRefFlags::TAB2_ABS     ) printf("ScRefFlags::TAB2_ABS     \n");
        if ((nFlag & ScRefFlags::TAB2_3D      ) == ScRefFlags::TAB2_3D      ) printf("ScRefFlags::TAB2_3D      \n");
        if ((nFlag & ScRefFlags::ROW_VALID    ) == ScRefFlags::ROW_VALID    ) printf("ScRefFlags::ROW_VALID    \n");
        if ((nFlag & ScRefFlags::COL_VALID    ) == ScRefFlags::COL_VALID    ) printf("ScRefFlags::COL_VALID    \n");
        if ((nFlag & ScRefFlags::TAB_VALID    ) == ScRefFlags::TAB_VALID    ) printf("ScRefFlags::TAB_VALID    \n");
        if ((nFlag & ScRefFlags::FORCE_DOC    ) == ScRefFlags::FORCE_DOC    ) printf("ScRefFlags::FORCE_DOC    \n");
        if ((nFlag & ScRefFlags::ROW2_VALID   ) == ScRefFlags::ROW2_VALID   ) printf("ScRefFlags::ROW2_VALID   \n");
        if ((nFlag & ScRefFlags::COL2_VALID   ) == ScRefFlags::COL2_VALID   ) printf("ScRefFlags::COL2_VALID   \n");
        if ((nFlag & ScRefFlags::TAB2_VALID   ) == ScRefFlags::TAB2_VALID   ) printf("ScRefFlags::TAB2_VALID   \n");
        if ((nFlag & ScRefFlags::VALID        ) == ScRefFlags::VALID        ) printf("ScRefFlags::VALID        \n");
        if ((nFlag & ScRefFlags::ADDR_ABS     ) == ScRefFlags::ADDR_ABS     ) printf("ScRefFlags::ADDR_ABS     \n");
        if ((nFlag & ScRefFlags::RANGE_ABS    ) == ScRefFlags::RANGE_ABS    ) printf("ScRefFlags::RANGE_ABS    \n");
        if ((nFlag & ScRefFlags::ADDR_ABS_3D  ) == ScRefFlags::ADDR_ABS_3D  ) printf("ScRefFlags::ADDR_ABS_3D  \n");
        if ((nFlag & ScRefFlags::RANGE_ABS_3D ) == ScRefFlags::RANGE_ABS_3D ) printf("ScRefFlags::RANGE_ABS_3D \n");
    }
    #endif

    //  class ScPrintAreasDlg

    ScPrintAreasDlg::ScPrintAreasDlg( SfxBindings* pB, SfxChildWindow* pCW, vcl::Window* pParent )
        : ScAnyRefDlg(pB, pCW, pParent, "PrintAreasDialog", "modules/scalc/ui/printareasdialog.ui")
        , bDlgLostFocus(false)
        , pDoc(nullptr)
        , pViewData(nullptr)
        , nCurTab(0)
    {
        get(pLbPrintArea,"lbprintarea");
        get(pEdPrintArea,"edprintarea");
        pEdPrintArea->SetReferences(this, get<VclFrame>("printframe")->get_label_widget());
        pRefInputEdit = pEdPrintArea;
        get(pRbPrintArea,"rbprintarea");
        pRbPrintArea->SetReferences(this, pEdPrintArea);

        get(pLbRepeatRow,"lbrepeatrow");
        get(pEdRepeatRow,"edrepeatrow");
        pEdRepeatRow->SetReferences(this, get<VclFrame>("rowframe")->get_label_widget());
        get(pRbRepeatRow,"rbrepeatrow");
        pRbRepeatRow->SetReferences(this, pEdRepeatRow);

        get(pLbRepeatCol,"lbrepeatcol");
        get(pEdRepeatCol,"edrepeatcol");
        pEdRepeatCol->SetReferences(this, get<VclFrame>("colframe")->get_label_widget());
        get(pRbRepeatCol,"rbrepeatcol");
        pRbRepeatCol->SetReferences(this, pEdRepeatCol);

        get(pBtnOk,"ok");
        get(pBtnCancel,"cancel");

        ScTabViewShell* pScViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current()  );
        ScDocShell*     pScDocSh  = dynamic_cast<ScDocShell*>(     SfxObjectShell::Current()  );

        OSL_ENSURE( pScDocSh, "Current DocumentShell not found :-(" );

        pDoc = &pScDocSh->GetDocument();

        if ( pScViewSh )
        {
            pViewData = &pScViewSh->GetViewData();
            nCurTab   = pViewData->GetTabNo();
        }

        Impl_Reset();

        //@BugID 54702 Enable/Disable only in base class
        //SFX_APPWINDOW->Enable();
    }

    ScPrintAreasDlg::~ScPrintAreasDlg()
    {
        disposeOnce();
    }

    void ScPrintAreasDlg::dispose()
    {
        // clean extra data of ListBox entries
        ListBox* aLb[3] = { pLbPrintArea, pLbRepeatRow, pLbRepeatCol };

        for (ListBox* pBox : aLb)
        {
            const sal_Int32 nCount = pBox->GetEntryCount();
            for ( sal_Int32 j=0; j<nCount; j++ )
                delete static_cast<OUString*>(pBox->GetEntryData(j));
        }
        pLbPrintArea.clear();
        pEdPrintArea.clear();
        pRbPrintArea.clear();
        pLbRepeatRow.clear();
        pEdRepeatRow.clear();
        pRbRepeatRow.clear();
        pLbRepeatCol.clear();
        pEdRepeatCol.clear();
        pRbRepeatCol.clear();
        pBtnOk.clear();
        pBtnCancel.clear();
        pRefInputEdit.clear();
        ScAnyRefDlg::dispose();
    }

    bool ScPrintAreasDlg::Close()
    {
        return DoClose( ScPrintAreasDlgWrapper::GetChildWindowId() );
    }

    bool ScPrintAreasDlg::IsTableLocked() const
    {
        //  Printing areas are per table, therefore it makes no sense,
        //  to switch the table during input

        return true;
    }

    void ScPrintAreasDlg::SetReference( const ScRange& rRef, ScDocument* /* pDoc */ )
    {
        if ( pRefInputEdit )
        {
            if ( rRef.aStart != rRef.aEnd )
                RefInputStart( pRefInputEdit );

            OUString  aStr;
            const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();

            if ( pEdPrintArea == pRefInputEdit )
            {
                aStr = rRef.Format(ScRefFlags::RANGE_ABS, pDoc, eConv);
                OUString aVal = pEdPrintArea->GetText();
                Selection aSel = pEdPrintArea->GetSelection();
                aSel.Justify();
                aVal = aVal.replaceAt( aSel.Min(), aSel.Len(), aStr );
                Selection aNewSel( aSel.Min(), aSel.Min()+aStr.getLength() );
                pEdPrintArea->SetRefString( aVal );
                pEdPrintArea->SetSelection( aNewSel );
            }
            else
            {
                bool bRow = ( pEdRepeatRow == pRefInputEdit );
                lcl_GetRepeatRangeString(&rRef, pDoc, bRow, aStr);
                pRefInputEdit->SetRefString( aStr );
            }
            Impl_ModifyHdl( *pRefInputEdit );
        }
    }

    void ScPrintAreasDlg::AddRefEntry()
    {
        if ( pRefInputEdit == pEdPrintArea )
        {
            const sal_Unicode sep = ScCompiler::GetNativeSymbolChar(ocSep);
            OUString aVal = pEdPrintArea->GetText() + OUStringLiteral1(sep);
            pEdPrintArea->SetText(aVal);

            sal_Int32 nLen = aVal.getLength();
            pEdPrintArea->SetSelection( Selection( nLen, nLen ) );

            Impl_ModifyHdl( *pEdPrintArea );
        }
    }

    void ScPrintAreasDlg::Deactivate()
    {
        bDlgLostFocus = true;
    }

    void ScPrintAreasDlg::SetActive()
    {
        if ( bDlgLostFocus )
        {
            bDlgLostFocus = false;

            if ( pRefInputEdit )
            {
                pRefInputEdit->GrabFocus();
                Impl_ModifyHdl( *pRefInputEdit );
            }
        }
        else
            GrabFocus();

        RefInputDone();
    }

    void ScPrintAreasDlg::Impl_Reset()
    {
        OUString        aStrRange;
        const ScRange*  pRepeatColRange = pDoc->GetRepeatColRange( nCurTab );
        const ScRange*  pRepeatRowRange = pDoc->GetRepeatRowRange( nCurTab );

        pEdPrintArea->SetModifyHdl   ( HDL(Impl_ModifyHdl) );
        pEdRepeatRow->SetModifyHdl   ( HDL(Impl_ModifyHdl) );
        pEdRepeatCol->SetModifyHdl   ( HDL(Impl_ModifyHdl) );
        pEdPrintArea->SetGetFocusHdl( HDL(Impl_GetFocusHdl) );
        pEdRepeatRow->SetGetFocusHdl( HDL(Impl_GetFocusHdl) );
        pEdRepeatCol->SetGetFocusHdl( HDL(Impl_GetFocusHdl) );
        pLbPrintArea->SetGetFocusHdl( HDL(Impl_GetFocusHdl) );
        pLbRepeatRow->SetGetFocusHdl( HDL(Impl_GetFocusHdl) );
        pLbRepeatCol->SetGetFocusHdl( HDL(Impl_GetFocusHdl) );
        pLbPrintArea->SetSelectHdl   ( HDL(Impl_SelectHdl) );
        pLbRepeatRow->SetSelectHdl   ( HDL(Impl_SelectHdl) );
        pLbRepeatCol->SetSelectHdl   ( HDL(Impl_SelectHdl) );
        pBtnOk->      SetClickHdl    ( HDL(Impl_BtnHdl)    );
        pBtnCancel->  SetClickHdl    ( HDL(Impl_BtnHdl)    );

        Impl_FillLists();

        // printing area

        aStrRange.clear();
        const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
        const sal_Unicode sep = ScCompiler::GetNativeSymbolChar(ocSep);
        sal_uInt16 nRangeCount = pDoc->GetPrintRangeCount( nCurTab );
        for (sal_uInt16 i=0; i<nRangeCount; i++)
        {
            const ScRange* pPrintRange = pDoc->GetPrintRange( nCurTab, i );
            if (pPrintRange)
            {
                if ( !aStrRange.isEmpty() )
                    aStrRange += OUStringLiteral1(sep);
                aStrRange += pPrintRange->Format(ScRefFlags::RANGE_ABS, pDoc, eConv);
            }
        }
        pEdPrintArea->SetText( aStrRange );

        // repeat row

        lcl_GetRepeatRangeString(pRepeatRowRange, pDoc, true, aStrRange);
        pEdRepeatRow->SetText( aStrRange );

        // repeat column

        lcl_GetRepeatRangeString(pRepeatColRange, pDoc, false, aStrRange);
        pEdRepeatCol->SetText( aStrRange );

        Impl_ModifyHdl( *pEdPrintArea );
        Impl_ModifyHdl( *pEdRepeatRow );
        Impl_ModifyHdl( *pEdRepeatCol );
        if( pDoc->IsPrintEntireSheet( nCurTab ) )
            pLbPrintArea->SelectEntryPos( SC_AREASDLG_PR_ENTIRE );

        pEdPrintArea->SaveValue();   // save for FillItemSet():
        pEdRepeatRow->SaveValue();
        pEdRepeatCol->SaveValue();
    }

    bool ScPrintAreasDlg::Impl_GetItem( const Edit* pEd, SfxStringItem& rItem )
    {
        OUString  aRangeStr = pEd->GetText();
        bool bDataChanged = pEd->IsValueChangedFromSaved();

        if ( !aRangeStr.isEmpty() && pEdPrintArea != pEd )
        {
            ScRange aRange;
            const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
            lcl_CheckRepeatString(aRangeStr, pDoc, pEdRepeatRow == pEd, &aRange);
            aRangeStr = aRange.Format(ScRefFlags::RANGE_ABS, pDoc, eConv);
        }

        rItem.SetValue( aRangeStr );

        return bDataChanged;
    }

    bool ScPrintAreasDlg::Impl_CheckRefStrings()
    {
        bool        bOk = false;
        OUString      aStrPrintArea   = pEdPrintArea->GetText();
        OUString      aStrRepeatRow   = pEdRepeatRow->GetText();
        OUString      aStrRepeatCol   = pEdRepeatCol->GetText();

        bool bPrintAreaOk = true;
        if ( !aStrPrintArea.isEmpty() )
        {
            const ScRefFlags nValidAddr  = ScRefFlags::VALID | ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID;
            const ScRefFlags nValidRange = nValidAddr | ScRefFlags::ROW2_VALID | ScRefFlags::COL2_VALID;
            const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
            const sal_Unicode sep  = ScCompiler::GetNativeSymbolChar(ocSep);

            ScAddress aAddr;
            ScRange aRange;
            for ( sal_Int32 nIdx = 0; nIdx >= 0; )
            {
                const OUString aOne = aStrPrintArea.getToken(0, sep, nIdx);
                ScRefFlags nResult = aRange.Parse( aOne, pDoc, eConv );
                if ((nResult & nValidRange) != nValidRange)
                {
                    ScRefFlags nAddrResult = aAddr.Parse( aOne, pDoc, eConv );
                    if ((nAddrResult & nValidAddr) != nValidAddr)
                    {
                        bPrintAreaOk = false;
                        break;
                    }
                }
            }
        }

        bool bRepeatRowOk = aStrRepeatRow.isEmpty();
        if ( !bRepeatRowOk )
            bRepeatRowOk = lcl_CheckRepeatString(aStrRepeatRow, pDoc, true, nullptr);

        bool bRepeatColOk = aStrRepeatCol.isEmpty();
        if ( !bRepeatColOk )
            bRepeatColOk = lcl_CheckRepeatString(aStrRepeatCol, pDoc, false, nullptr);

        // error messages

        bOk = (bPrintAreaOk && bRepeatRowOk && bRepeatColOk);

        if ( !bOk )
        {
            Edit* pEd = nullptr;

                 if ( !bPrintAreaOk ) pEd = pEdPrintArea;
            else if ( !bRepeatRowOk ) pEd = pEdRepeatRow;
            else if ( !bRepeatColOk ) pEd = pEdRepeatCol;

            ERRORBOX(GetFrameWeld(), STR_INVALID_TABREF);

            OSL_ASSERT(pEd);

            if (pEd)
                pEd->GrabFocus();
        }

        return bOk;
    }

    void ScPrintAreasDlg::Impl_FillLists()
    {

        // Get selection and remember String in PrintArea-ListBox

        ScRange  aRange;
        OUString aStrRange;
        bool bSimple = true;

        if ( pViewData )
            bSimple = (pViewData->GetSimpleArea( aRange ) == SC_MARK_SIMPLE);

        formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();

        if ( bSimple )
            aStrRange = aRange.Format(ScRefFlags::RANGE_ABS, pDoc, eConv);
        else
        {
            ScRangeListRef aList( new ScRangeList );
            pViewData->GetMarkData().FillRangeListWithMarks( aList.get(), false );
            aList->Format(aStrRange, ScRefFlags::RANGE_ABS, pDoc, eConv);
        }

        pLbPrintArea->SetEntryData( SC_AREASDLG_PR_SELECT, new OUString( aStrRange ) );

        // Get ranges and remember in ListBoxen

        ScRangeName* pRangeNames = pDoc->GetRangeName();

        if (!pRangeNames || pRangeNames->empty())
            // No range names to process.
            return;

        ScRangeName::const_iterator itr = pRangeNames->begin(), itrEnd = pRangeNames->end();
        for (; itr != itrEnd; ++itr)
        {
            if (!itr->second->HasType(ScRangeData::Type::AbsArea   )
                && !itr->second->HasType(ScRangeData::Type::RefArea)
                && !itr->second->HasType(ScRangeData::Type::AbsPos ))
                continue;

            OUString aName = itr->second->GetName();
            OUString aSymbol;
            itr->second->GetSymbol(aSymbol);
            if (aRange.ParseAny(aSymbol, pDoc, eConv) & ScRefFlags::VALID)
            {
                if (itr->second->HasType(ScRangeData::Type::PrintArea))
                {
                    aSymbol = aRange.Format(ScRefFlags::RANGE_ABS, pDoc, eConv);
                    pLbPrintArea->SetEntryData(
                        pLbPrintArea->InsertEntry(aName),
                        new OUString(aSymbol) );
                }

                if (itr->second->HasType(ScRangeData::Type::RowHeader))
                {
                    lcl_GetRepeatRangeString(&aRange, pDoc, true, aSymbol);
                    pLbRepeatRow->SetEntryData(
                        pLbRepeatRow->InsertEntry(aName),
                        new OUString(aSymbol) );
                }

                if (itr->second->HasType(ScRangeData::Type::ColHeader))
                {
                    lcl_GetRepeatRangeString(&aRange, pDoc, false, aSymbol);
                    pLbRepeatCol->SetEntryData(
                        pLbRepeatCol->InsertEntry(aName),
                        new OUString(aSymbol));
                }
            }
        }
    }

    // Handler:

    IMPL_LINK( ScPrintAreasDlg, Impl_BtnHdl, Button*, pBtn, void )
    {
        if ( pBtnOk == pBtn )
        {
            if ( Impl_CheckRefStrings() )
            {
                OUString        aStr;
                SfxStringItem   aPrintArea( SID_CHANGE_PRINTAREA, aStr );
                SfxStringItem   aRepeatRow( FN_PARAM_2, aStr );
                SfxStringItem   aRepeatCol( FN_PARAM_3, aStr );

                // Printing area changed?

                // first try the list box, if "Entire sheet" is selected
                bool bEntireSheet = (pLbPrintArea->GetSelectedEntryPos() == SC_AREASDLG_PR_ENTIRE);
                SfxBoolItem aEntireSheet( FN_PARAM_4, bEntireSheet );

                bool bDataChanged = bEntireSheet != pDoc->IsPrintEntireSheet( nCurTab );
                if( !bEntireSheet )
                {
                    // if new list box selection is not "Entire sheet", get the edit field contents
                    bDataChanged |= Impl_GetItem( pEdPrintArea, aPrintArea );
                }

                // Repeat row changed?

                bDataChanged |= Impl_GetItem( pEdRepeatRow, aRepeatRow );

                // Repeat column changed?

                bDataChanged |= Impl_GetItem( pEdRepeatCol, aRepeatCol );

                if ( bDataChanged )
                {
                    SetDispatcherLock( false );
                    SwitchToDocument();
                    GetBindings().GetDispatcher()->ExecuteList(SID_CHANGE_PRINTAREA,
                          SfxCallMode::SLOT | SfxCallMode::RECORD,
                          { &aPrintArea, &aRepeatRow, &aRepeatCol, &aEntireSheet });
                }

                Close();
            }
        }
        else if ( pBtnCancel == pBtn )
            Close();
    }

    IMPL_LINK( ScPrintAreasDlg, Impl_GetFocusHdl, Control&, rCtrl, void )
    {
        if ( &rCtrl ==static_cast<Control *>(pEdPrintArea) ||
             &rCtrl ==static_cast<Control *>(pEdRepeatRow) ||
             &rCtrl ==static_cast<Control *>(pEdRepeatCol))
        {
             pRefInputEdit = static_cast<formula::RefEdit*>(&rCtrl);
        }
        else if ( &rCtrl ==static_cast<Control *>(pLbPrintArea))
        {
            pRefInputEdit = pEdPrintArea;
        }
        else if ( &rCtrl ==static_cast<Control *>(pLbRepeatRow))
        {
            pRefInputEdit = pEdRepeatRow;
        }
        else if ( &rCtrl ==static_cast<Control *>(pLbRepeatCol))
        {
            pRefInputEdit = pEdRepeatCol;
        }
    }

    IMPL_LINK( ScPrintAreasDlg, Impl_SelectHdl, ListBox&, rLb, void )
    {
        ListBox* pLb = &rLb;
        const sal_Int32 nSelPos = pLb->GetSelectedEntryPos();
        Edit* pEd = nullptr;

        // list box positions of specific entries, default to "repeat row/column" list boxes
        sal_Int32 nAllSheetPos = SC_AREASDLG_RR_NONE;
        sal_Int32 nUserDefPos = SC_AREASDLG_RR_USER;
        sal_Int32 nFirstCustomPos = SC_AREASDLG_RR_OFFSET;

        // find edit field for list box, and list box positions
        if( pLb == pLbPrintArea )
        {
            pEd = pEdPrintArea;
            nAllSheetPos = SC_AREASDLG_PR_ENTIRE;
            nUserDefPos = SC_AREASDLG_PR_USER;
            nFirstCustomPos = SC_AREASDLG_PR_SELECT;    // "Selection" and following
        }
        else if( pLb == pLbRepeatCol )
            pEd = pEdRepeatCol;
        else if( pLb == pLbRepeatRow )
            pEd = pEdRepeatRow;
        else
            return;

        // fill edit field according to list box selection
        if( (nSelPos == 0) || (nSelPos == nAllSheetPos) )
            pEd->SetText( EMPTY_OUSTRING );
        else if( nSelPos == nUserDefPos && !pLb->IsTravelSelect() && pEd->GetText().isEmpty())
            pLb->SelectEntryPos( 0 );
        else if( nSelPos >= nFirstCustomPos )
            pEd->SetText( *static_cast< OUString* >( pLb->GetEntryData( nSelPos ) ) );
    }

    IMPL_LINK( ScPrintAreasDlg, Impl_ModifyHdl, Edit&, rEd, void )
    {
        ListBox* pLb = nullptr;

        // list box positions of specific entries, default to "repeat row/column" list boxes
        sal_Int32 nUserDefPos = SC_AREASDLG_RR_USER;
        sal_Int32 nFirstCustomPos = SC_AREASDLG_RR_OFFSET;

        if( &rEd == pEdPrintArea )
        {
            pLb = pLbPrintArea;
            nUserDefPos = SC_AREASDLG_PR_USER;
            nFirstCustomPos = SC_AREASDLG_PR_SELECT;    // "Selection" and following
        }
        else if( &rEd == pEdRepeatCol )
            pLb = pLbRepeatCol;
        else if( &rEd == pEdRepeatRow )
            pLb = pLbRepeatRow;
        else
            return;

        // set list box selection according to edit field
        const sal_Int32 nEntryCount = pLb->GetEntryCount();
        OUString aStrEd( rEd.GetText() );
        OUString aEdUpper = aStrEd.toAsciiUpperCase();

        if ( (nEntryCount > nFirstCustomPos) && !aStrEd.isEmpty() )
        {
            bool    bFound  = false;
            sal_Int32 i;

            for ( i=nFirstCustomPos; i<nEntryCount && !bFound; i++ )
            {
                OUString* pSymbol = static_cast<OUString*>(pLb->GetEntryData( i ));
                bFound  = ( (*pSymbol)  ==aStrEd || (*pSymbol) == aEdUpper );
            }

            pLb->SelectEntryPos( bFound ? i-1 : nUserDefPos );
        }
        else
            pLb->SelectEntryPos( !aStrEd.isEmpty() ? nUserDefPos : 0 );
    }

    // global functions:

    // TODO: It might make sense to move these functions to address.?xx. -kohei

    static bool lcl_CheckOne_OOO( const OUString& rStr, bool bIsRow, SCCOLROW& rVal )
    {
        // Allowed syntax for rStr:
        // Row: [$]1-MAXTAB
        // Col: [$]A-IV

        OUString    aStr    = rStr;
        sal_Int32   nLen    = aStr.getLength();
        SCCOLROW    nNum    = 0;
        bool    bStrOk  = ( nLen > 0 ) && ( bIsRow ? ( nLen < 6 ) : ( nLen < 4 ) );

        if ( bStrOk )
        {
            if ( '$' == aStr[0] )
                aStr = aStr.copy( 1 );

            if ( bIsRow )
            {
                bStrOk = CharClass::isAsciiNumeric(aStr);

                if ( bStrOk )
                {
                    sal_Int32 n = aStr.toInt32();

                    bStrOk = (n > 0) && ( n <= MAXROWCOUNT );
                    if ( bStrOk )
                        nNum = static_cast<SCCOLROW>(n - 1);
                }
            }
            else
            {
                SCCOL nCol = 0;
                bStrOk = ::AlphaToCol( nCol, aStr);
                nNum = nCol;
            }
        }

        if ( bStrOk )
            rVal = nNum;

        return bStrOk;
    }

    static bool lcl_CheckOne_XL_A1( const OUString& rStr, bool bIsRow, SCCOLROW& rVal )
    {
        // XL A1 style is identical to OOO one for print range formats.
        return lcl_CheckOne_OOO(rStr, bIsRow, rVal);
    }

    static bool lcl_CheckOne_XL_R1C1( const OUString& rStr, bool bIsRow, SCCOLROW& rVal )
    {
        sal_Int32 nLen = rStr.getLength();
        if (nLen <= 1)
            // There must be at least two characters.
            return false;

        const sal_Unicode preUpper = bIsRow ? 'R' : 'C';
        const sal_Unicode preLower = bIsRow ? 'r' : 'c';
        if (rStr[0] != preUpper && rStr[0] != preLower)
            return false;

        OUString aNumStr = rStr.copy(1);
        if (!CharClass::isAsciiNumeric(aNumStr))
            return false;

        sal_Int32 nNum = aNumStr.toInt32();

        if (nNum <= 0)
            return false;

        if ((bIsRow && nNum > MAXROWCOUNT) || (!bIsRow && nNum > MAXCOLCOUNT))
            return false;

        rVal = static_cast<SCCOLROW>(nNum-1);
        return true;
    }

    static bool lcl_CheckRepeatOne( const OUString& rStr, formula::FormulaGrammar::AddressConvention eConv, bool bIsRow, SCCOLROW& rVal )
    {
        switch (eConv)
        {
            case formula::FormulaGrammar::CONV_OOO:
                return lcl_CheckOne_OOO(rStr, bIsRow, rVal);
            case formula::FormulaGrammar::CONV_XL_A1:
                return lcl_CheckOne_XL_A1(rStr, bIsRow, rVal);
            case formula::FormulaGrammar::CONV_XL_R1C1:
                return lcl_CheckOne_XL_R1C1(rStr, bIsRow, rVal);
            default:
            {
                // added to avoid warnings
            }
        }
        return false;
    }

    static bool lcl_CheckRepeatString( const OUString& rStr, const ScDocument* pDoc, bool bIsRow, ScRange* pRange )
    {
        // Row: [valid row] rsep [valid row]
        // Col: [valid col] rsep [valid col]

        const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
        const sal_Unicode rsep = ScCompiler::GetNativeSymbolChar(ocRange);

        if (pRange)
        {
            // initialize the range value.
            pRange->aStart.SetCol(0);
            pRange->aStart.SetRow(0);
            pRange->aEnd.SetCol(0);
            pRange->aEnd.SetRow(0);
        }

        OUString aBuf;
        SCCOLROW nVal = 0;
        sal_Int32 nLen = rStr.getLength();
        bool bEndPos = false;
        for( sal_Int32 i = 0; i < nLen; ++i )
        {
            const sal_Unicode c = rStr[i];
            if (c == rsep)
            {
                if (bEndPos)
                    // We aren't supposed to have more than one range separator.
                    return false;

                // range separator
                if (aBuf.isEmpty())
                    return false;

                bool bRes = lcl_CheckRepeatOne(aBuf, eConv, bIsRow, nVal);
                if (!bRes)
                    return false;

                if (pRange)
                {
                    if (bIsRow)
                    {
                        pRange->aStart.SetRow(static_cast<SCROW>(nVal));
                        pRange->aEnd.SetRow(static_cast<SCROW>(nVal));
                    }
                    else
                    {
                        pRange->aStart.SetCol(static_cast<SCCOL>(nVal));
                        pRange->aEnd.SetCol(static_cast<SCCOL>(nVal));
                    }
                }

                aBuf.clear();
                bEndPos = true;
            }
            else
                aBuf += OUStringLiteral1(c);
        }

        if (!aBuf.isEmpty())
        {
            bool bRes = lcl_CheckRepeatOne(aBuf, eConv, bIsRow, nVal);
            if (!bRes)
                return false;

            if (pRange)
            {
                if (bIsRow)
                {
                    if (!bEndPos)
                        pRange->aStart.SetRow(static_cast<SCROW>(nVal));
                    pRange->aEnd.SetRow(static_cast<SCROW>(nVal));
                }
                else
                {
                    if (!bEndPos)
                        pRange->aStart.SetCol(static_cast<SCCOL>(nVal));
                    pRange->aEnd.SetCol(static_cast<SCCOL>(nVal));
                }
            }
        }

        return true;
    }

    static void lcl_GetRepeatRangeString( const ScRange* pRange, const ScDocument* pDoc, bool bIsRow, OUString& rStr )
    {
        rStr.clear();
        if (!pRange)
            return;

        const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
        const ScAddress& rStart = pRange->aStart;
        const ScAddress& rEnd   = pRange->aEnd;

        const ScRefFlags nFmt = bIsRow
                                ? (ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS)
                                : (ScRefFlags::COL_VALID | ScRefFlags::COL_ABS);
        rStr += rStart.Format(nFmt, pDoc, eConv);
        if ((bIsRow && rStart.Row() != rEnd.Row()) || (!bIsRow && rStart.Col() != rEnd.Col()))
        {
            rStr += ScCompiler::GetNativeSymbol(ocRange);
            rStr += rEnd.Format(nFmt, pDoc, eConv);
        }
    }

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V522 There might be dereferencing of a potential null pointer 'pScDocSh'.