/* -*- 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 <comphelper/string.hxx>
#include <sfx2/dispatch.hxx>
#include <svl/stritem.hxx>
#include <vcl/msgbox.hxx>
#include <unotools/charclass.hxx>
#include <stdlib.h>

#include <areasdlg.hxx>
#include <scresid.hxx>
#include <rangenam.hxx>
#include <reffact.hxx>
#include <tabvwsh.hxx>
#include <docsh.hxx>
#include <globstr.hrc>
#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 )
#define ERRORBOX(nId)       ScopedVclPtrInstance<MessageDialog>(this, ScGlobal::GetRscString(nId))->Execute()

// 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();
    OUString aOne;
    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);
            aOne = pPrintRange->Format(ScRefFlags::RANGE_ABS, pDoc, eConv);
            aStrRange += aOne;
        }
    }
    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;
        sal_Int32 nSepCount = comphelper::string::getTokenCount(aStrPrintArea, sep);
        for ( sal_Int32 i = 0; i < nSepCount && bPrintAreaOk; ++i )
        {
            OUString aOne = aStrPrintArea.getToken(i, sep);
            ScRefFlags nResult = aRange.Parse( aOne, pDoc, eConv );
            if ((nResult & nValidRange) != nValidRange)
            {
                ScRefFlags nAddrResult = aAddr.Parse( aOne, pDoc, eConv );
                if ((nAddrResult & nValidAddr) != nValidAddr)
                    bPrintAreaOk = false;
            }
        }
    }

    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( 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: */
