Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  documen8.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 <scitems.hxx>
#include <comphelper/fileformat.h>
#include <comphelper/processfactory.hxx>
#include <officecfg/Office/Common.hxx>
#include <tools/urlobj.hxx>
#include <editeng/frmdiritem.hxx>
#include <editeng/langitem.hxx>
#include <sfx2/linkmgr.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/printer.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/viewsh.hxx>
#include <svl/flagitem.hxx>
#include <svl/intitem.hxx>
#include <svl/numformat.hxx>
#include <svl/zformat.hxx>
#include <svl/ctloptions.hxx>
#include <unotools/transliterationwrapper.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>

#include <vcl/svapp.hxx>
#include <vcl/virdev.hxx>
#include <vcl/weld.hxx>
#include <vcl/TaskStopwatch.hxx>

#include <inputopt.hxx>
#include <global.hxx>
#include <table.hxx>
#include <column.hxx>
#include <poolhelp.hxx>
#include <docpool.hxx>
#include <docsh.hxx>
#include <stlpool.hxx>
#include <stlsheet.hxx>
#include <docoptio.hxx>
#include <viewopti.hxx>
#include <scextopt.hxx>
#include <rechead.hxx>
#include <ddelink.hxx>
#include <scmatrix.hxx>
#include <arealink.hxx>
#include <patattr.hxx>
#include <editutil.hxx>
#include <progress.hxx>
#include <document.hxx>
#include <chartlis.hxx>
#include <chartlock.hxx>
#include <refupdat.hxx>
#include <markdata.hxx>
#include <scmod.hxx>
#include <externalrefmgr.hxx>
#include <globstr.hrc>
#include <strings.hrc>
#include <sc.hrc>
#include <charthelper.hxx>
#include <macromgr.hxx>
#include <docuno.hxx>
#include <scresid.hxx>
#include <columniterator.hxx>
#include <globalnames.hxx>
#include <stringutil.hxx>
#include <documentlinkmgr.hxx>
#include <tokenarray.hxx>
#include <recursionhelper.hxx>

#include <memory>
#include <utility>

using namespace com::sun::star;

namespace {

sal_uInt16 getScaleValue(SfxStyleSheetBase& rStyle, sal_uInt16 nWhich)
{
    return static_cast<const SfxUInt16Item&>(rStyle.GetItemSet().Get(nWhich)).GetValue();
}

}

void ScDocument::ImplCreateOptions()
{
    pDocOptions.reset( new ScDocOptions() );
    pViewOptions.reset( new ScViewOptions() );
}

void ScDocument::ImplDeleteOptions()
{
    pDocOptions.reset();
    pViewOptions.reset();
    pExtDocOptions.reset();
}

SfxPrinter* ScDocument::GetPrinter(bool bCreateIfNotExist)
{
    if (!mpPrinter && bCreateIfNotExist && mxPoolHelper)
    {
        auto pSet = std::make_unique<SfxItemSet>(
            SfxItemSet::makeFixedSfxItemSet<SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN,
                                            SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC,
                                            SID_PRINT_SELECTEDSHEET, SID_PRINT_SELECTEDSHEET,
                                            SID_SCPRINTOPTIONS, SID_SCPRINTOPTIONS>(*mxPoolHelper->GetDocPool()));
        SfxPrinterChangeFlags nFlags = SfxPrinterChangeFlags::NONE;
        if (officecfg::Office::Common::Print::Warning::PaperOrientation::get())
            nFlags |= SfxPrinterChangeFlags::CHG_ORIENTATION;
        if (officecfg::Office::Common::Print::Warning::PaperSize::get())
            nFlags |= SfxPrinterChangeFlags::CHG_SIZE;
        pSet->Put( SfxFlagItem( SID_PRINTER_CHANGESTODOC, static_cast<int>(nFlags) ) );
        pSet->Put( SfxBoolItem( SID_PRINTER_NOTFOUND_WARN, officecfg::Office::Common::Print::Warning::NotFound::get() ) );

        mpPrinter = VclPtr<SfxPrinter>::Create( std::move(pSet) );
        mpPrinter->SetMapMode(MapMode(MapUnit::Map100thMM));
        UpdateDrawPrinter();
        mpPrinter->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
    }

    return mpPrinter;
}

void ScDocument::SetPrinter( VclPtr<SfxPrinter> const & pNewPrinter )
{
    if ( pNewPrinter == mpPrinter.get() )
    {
        //  #i6706# SetPrinter is called with the same printer again if
        //  the JobSetup has changed. In that case just call UpdateDrawPrinter
        //  (SetRefDevice for drawing layer) because of changed text sizes.
        UpdateDrawPrinter();
    }
    else
    {
        ScopedVclPtr<SfxPrinter> xKeepAlive( mpPrinter );
        mpPrinter = pNewPrinter;
        UpdateDrawPrinter();
        mpPrinter->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
    }
    InvalidateTextWidth(nullptr, nullptr, false);     // in both cases
}

void ScDocument::SetPrintOptions()
{
    if ( !mpPrinter ) GetPrinter(); // this sets mpPrinter
    OSL_ENSURE( mpPrinter, "Error in printer creation :-/" );

    if ( !mpPrinter )
        return;

    SfxItemSet aOptSet( mpPrinter->GetOptions() );

    SfxPrinterChangeFlags nFlags = SfxPrinterChangeFlags::NONE;
    if (officecfg::Office::Common::Print::Warning::PaperOrientation::get())
        nFlags |= SfxPrinterChangeFlags::CHG_ORIENTATION;
    if (officecfg::Office::Common::Print::Warning::PaperSize::get())
        nFlags |= SfxPrinterChangeFlags::CHG_SIZE;
    aOptSet.Put( SfxFlagItem( SID_PRINTER_CHANGESTODOC, static_cast<int>(nFlags) ) );
    aOptSet.Put( SfxBoolItem( SID_PRINTER_NOTFOUND_WARN, officecfg::Office::Common::Print::Warning::NotFound::get() ) );

    mpPrinter->SetOptions( aOptSet );
}

VirtualDevice* ScDocument::GetVirtualDevice_100th_mm()
{
    if (!mpVirtualDevice_100th_mm)
    {
#ifdef IOS
        mpVirtualDevice_100th_mm = VclPtr<VirtualDevice>::Create(DeviceFormat::GRAYSCALE);
#else
        mpVirtualDevice_100th_mm = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA);
#endif
        mpVirtualDevice_100th_mm->SetReferenceDevice(VirtualDevice::RefDevMode::MSO1);
        MapMode aMapMode( mpVirtualDevice_100th_mm->GetMapMode() );
        aMapMode.SetMapUnit( MapUnit::Map100thMM );
        mpVirtualDevice_100th_mm->SetMapMode( aMapMode );
    }
    return mpVirtualDevice_100th_mm;
}

OutputDevice* ScDocument::GetRefDevice(bool bForceVirtDev)
{
    // Create printer like ref device, see Writer...
    OutputDevice* pRefDevice = nullptr;
    if (!bForceVirtDev && ScModule::get()->GetInputOptions().GetTextWysiwyg())
    {
        pRefDevice = GetPrinter();
        SAL_WARN_IF(!pRefDevice, "sc""unable to get a printer, fallback to virdev");
    }
    if (!pRefDevice)
        pRefDevice = GetVirtualDevice_100th_mm();
    return pRefDevice;
}

void ScDocument::ModifyStyleSheet( SfxStyleSheetBase& rStyleSheet,
                                   const SfxItemSet&  rChanges )
{
    SfxItemSet& rSet = rStyleSheet.GetItemSet();

    switch ( rStyleSheet.GetFamily() )
    {
        case SfxStyleFamily::Page:
            {
                const sal_uInt16 nOldScale = getScaleValue(rStyleSheet, ATTR_PAGE_SCALE);
                const sal_uInt16 nOldScaleToPages = getScaleValue(rStyleSheet, ATTR_PAGE_SCALETOPAGES);
                rSet.Put( rChanges );
                const sal_uInt16 nNewScale        = getScaleValue(rStyleSheet, ATTR_PAGE_SCALE);
                const sal_uInt16 nNewScaleToPages = getScaleValue(rStyleSheet, ATTR_PAGE_SCALETOPAGES);

                if ( (nOldScale != nNewScale) || (nOldScaleToPages != nNewScaleToPages) )
                    InvalidateTextWidth( rStyleSheet.GetName() );

                if( SvtCTLOptions::IsCTLFontEnabled() )
                {
                    if( rChanges.GetItemState(ATTR_WRITINGDIR ) == SfxItemState::SET )
                        ScChartHelper::DoUpdateAllCharts( *this );
                }
            }
            break;

        case SfxStyleFamily::Para:
            {
                bool bNumFormatChanged;
                if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
                        rSet, rChanges ) )
                    InvalidateTextWidth( nullptr, nullptr, bNumFormatChanged );

                for (SCTAB nTab=0; nTab<=MAXTAB; ++nTab)
                    if (maTabs[nTab])
                        maTabs[nTab]->SetStreamValid( false );

                sal_uInt32 nOldFormat =
                    rSet.Get( ATTR_VALUE_FORMAT ).GetValue();
                sal_uInt32 nNewFormat =
                    rChanges.Get( ATTR_VALUE_FORMAT ).GetValue();
                LanguageType eNewLang, eOldLang;
                eNewLang = eOldLang = LANGUAGE_DONTKNOW;
                if ( nNewFormat != nOldFormat )
                {
                    SvNumberFormatter* pFormatter = GetFormatTable();
                    eOldLang = pFormatter->GetEntry( nOldFormat )->GetLanguage();
                    eNewLang = pFormatter->GetEntry( nNewFormat )->GetLanguage();
                }

                // Explanation to Items in rChanges:
                //  Set Item        - take over change
                //  Dontcare        - Set Default
                //  Default         - No change
                // ("no change" is not possible with PutExtended, thus the loop)
                for (sal_uInt16 nWhich = ATTR_PATTERN_START; nWhich <= ATTR_PATTERN_END; nWhich++)
                {
                    const SfxPoolItem* pItem;
                    SfxItemState eState = rChanges.GetItemState( nWhich, false, &pItem );
                    if ( eState == SfxItemState::SET )
                        rSet.Put( *pItem );
                    else if ( eState == SfxItemState::INVALID )
                        rSet.ClearItem( nWhich );
                    // when Default nothing
                }

                if ( eNewLang != eOldLang )
                    rSet.Put(
                        SvxLanguageItem( eNewLang, ATTR_LANGUAGE_FORMAT ) );
            }
            break;
        default:
        {
            // added to avoid warnings
        }
    }
}

void ScDocument::CopyStdStylesFrom( const ScDocument& rSrcDoc )
{
    // number format exchange list has to be handled here, too
    NumFmtMergeHandler aNumFmtMergeHdl(*this, rSrcDoc);
    mxPoolHelper->GetStylePool()->CopyStdStylesFrom( rSrcDoc.mxPoolHelper->GetStylePool() );
}

void ScDocument::InvalidateTextWidth( std::u16string_view rStyleName )
{
    const SCTAB nCount = GetTableCount();
    for ( SCTAB i=0; i<nCount && maTabs[i]; i++ )
        if ( maTabs[i]->GetPageStyle() == rStyleName )
            InvalidateTextWidth( i );
}

void ScDocument::InvalidateTextWidth( SCTAB nTab )
{
    ScAddress aAdrFrom( 0,    0,        nTab );
    ScAddress aAdrTo  ( MaxCol(), MaxRow(), nTab );
    InvalidateTextWidth( &aAdrFrom, &aAdrTo, false );
}

bool ScDocument::IsPageStyleInUse( std::u16string_view rStrPageStyle, SCTAB* pInTab )
{
    bool         bInUse = false;
    const SCTAB nCount = GetTableCount();
    SCTAB i;

    for ( i = 0; !bInUse && i < nCount && maTabs[i]; i++ )
        bInUse = ( maTabs[i]->GetPageStyle() == rStrPageStyle );

    if ( pInTab )
        *pInTab = i-1;

    return bInUse;
}

bool ScDocument::RemovePageStyleInUse( std::u16string_view rStyle )
{
    bool bWasInUse = false;
    const SCTAB nCount = GetTableCount();

    for ( SCTAB i=0; i<nCount && maTabs[i]; i++ )
        if ( maTabs[i]->GetPageStyle() == rStyle )
        {
            bWasInUse = true;
            maTabs[i]->SetPageStyle( ScResId(STR_STYLENAME_STANDARD) );
        }

    return bWasInUse;
}

bool ScDocument::RenamePageStyleInUse( std::u16string_view rOld, const OUString& rNew )
{
    bool bWasInUse = false;
    const SCTAB nCount = GetTableCount();

    for ( SCTAB i=0; i<nCount && maTabs[i]; i++ )
        if ( maTabs[i]->GetPageStyle() == rOld )
        {
            bWasInUse = true;
            maTabs[i]->SetPageStyle( rNew );
        }

    return bWasInUse;
}

EEHorizontalTextDirection ScDocument::GetEditTextDirection(SCTAB nTab) const
{
    EEHorizontalTextDirection eRet = EEHorizontalTextDirection::Default;

    OUString aStyleName = GetPageStyle( nTab );
    SfxStyleSheetBase* pStyle = mxPoolHelper->GetStylePool()->Find( aStyleName, SfxStyleFamily::Page );
    if ( pStyle )
    {
        SfxItemSet& rStyleSet = pStyle->GetItemSet();
        SvxFrameDirection eDirection =
            rStyleSet.Get( ATTR_WRITINGDIR ).GetValue();

        if ( eDirection == SvxFrameDirection::Horizontal_LR_TB )
            eRet = EEHorizontalTextDirection::L2R;
        else if ( eDirection == SvxFrameDirection::Horizontal_RL_TB )
            eRet = EEHorizontalTextDirection::R2L;
        // else (invalid for EditEngine): keep "default"
    }

    return eRet;
}

ScMacroManager* ScDocument::GetMacroManager()
{
    if (!mpMacroMgr)
        mpMacroMgr.reset(new ScMacroManager(*this));
    return mpMacroMgr.get();
}

void ScDocument::FillMatrix(
    ScMatrix& rMat, SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, svl::SharedStringPool* pPool ) const
{
    const ScTable* pTab = FetchTable(nTab);
    if (!pTab)
        return;

    if (nCol1 > nCol2 || nRow1 > nRow2)
        return;

    SCSIZE nC, nR;
    rMat.GetDimensions(nC, nR);
    if (static_cast<SCROW>(nR) != nRow2 - nRow1 + 1 || static_cast<SCCOL>(nC) != nCol2 - nCol1 + 1)
        return;

    pTab->FillMatrix(rMat, nCol1, nRow1, nCol2, nRow2, pPool);
}

void ScDocument::SetFormulaResults( const ScAddress& rTopPos, const double* pResults, size_t nLen )
{
    ScTable* pTab = FetchTable(rTopPos.Tab());
    if (!pTab)
        return;

    pTab->SetFormulaResults(rTopPos.Col(), rTopPos.Row(), pResults, nLen);
}

void ScDocument::CalculateInColumnInThread( ScInterpreterContext& rContext, const ScRange& rCalcRange, unsigned nThisThread, unsigned nThreadsTotal)
{
    ScTable* pTab = FetchTable(rCalcRange.aStart.Tab());
    if (!pTab)
        return;

    assert(IsThreadedGroupCalcInProgress());

    maThreadSpecific.pContext = &rContext;
    pTab->CalculateInColumnInThread(rContext, rCalcRange.aStart.Col(), rCalcRange.aEnd.Col(), rCalcRange.aStart.Row(), rCalcRange.aEnd.Row(), nThisThread, nThreadsTotal);

    assert(IsThreadedGroupCalcInProgress());
    maThreadSpecific.pContext = nullptr;
    // If any of the thread_local data would cause problems if they stay around for too long
    // (and e.g. outlive the ScDocument), clean them up here, they cannot be cleaned up
    // later from the main thread.
    if(maThreadSpecific.xRecursionHelper)
        maThreadSpecific.xRecursionHelper->Clear();
}

void ScDocument::HandleStuffAfterParallelCalculation( SCCOL nColStart, SCCOL nColEnd, SCROW nRow, size_t nLen, SCTAB nTab, ScInterpreter* pInterpreter )
{
    assert(!IsThreadedGroupCalcInProgress());
    forconst DelayedSetNumberFormat& data : GetNonThreadedContext().maDelayedSetNumberFormat)
        SetNumberFormat( ScAddress( data.mCol, data.mRow, nTab ), data.mnNumberFormat );
    GetNonThreadedContext().maDelayedSetNumberFormat.clear();

    ScTable* pTab = FetchTable(nTab);
    if (!pTab)
        return;

    pTab->HandleStuffAfterParallelCalculation(nColStart, nColEnd, nRow, nLen, pInterpreter);
}

void ScDocument::InvalidateTextWidth( const ScAddress* pAdrFrom, const ScAddress* pAdrTo,
                                      bool bNumFormatChanged )
{
    bool bBroadcast = (bNumFormatChanged && GetDocOptions().IsCalcAsShown() && !IsImportingXML() && !IsClipboard());
    if ( pAdrFrom && !pAdrTo )
    {
        const SCTAB nTab = pAdrFrom->Tab();

        if (nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
            maTabs[nTab]->InvalidateTextWidth( pAdrFrom, nullptr, bNumFormatChanged, bBroadcast );
    }
    else
    {
        const SCTAB nTabStart = pAdrFrom ? pAdrFrom->Tab() : 0;
        const SCTAB nTabEnd   = pAdrTo   ? pAdrTo->Tab()   : MAXTAB;

        for ( SCTAB nTab=nTabStart; nTab<=nTabEnd && nTab < static_cast<SCTAB>(maTabs.size()); nTab++ )
            if ( maTabs[nTab] )
                maTabs[nTab]->InvalidateTextWidth( pAdrFrom, pAdrTo, bNumFormatChanged, bBroadcast );
    }
}

#define CALCMAX                 1000    // Calculations

namespace {

class IdleCalcTextWidthScope : public TaskStopwatch
{
    ScDocument& mrDoc;
    ScAddress& mrCalcPos;
    MapMode maOldMapMode;
    ScStyleSheetPool* mpStylePool;
    bool mbNeedMore;
    bool mbProgress;

public:
    IdleCalcTextWidthScope(ScDocument& rDoc, ScAddress& rCalcPos) :
        mrDoc(rDoc),
        mrCalcPos(rCalcPos),
        mpStylePool(rDoc.GetStyleSheetPool()),
        mbNeedMore(false),
        mbProgress(false)
    {
        mrDoc.EnableIdle(false);
    }

    ~IdleCalcTextWidthScope() COVERITY_NOEXCEPT_FALSE
    {
        SfxPrinter* pDev = mrDoc.GetPrinter();
        if (pDev)
            pDev->SetMapMode(maOldMapMode);

        if (mbProgress)
            ScProgress::DeleteInterpretProgress();

        mrDoc.EnableIdle(true);
    }

    SCTAB Tab() const { return mrCalcPos.Tab(); }
    SCCOL Col() const { return mrCalcPos.Col(); }
    SCROW Row() const { return mrCalcPos.Row(); }

    void setTab(SCTAB nTab) { mrCalcPos.SetTab(nTab); }
    void setCol(SCCOL nCol) { mrCalcPos.SetCol(nCol); }
    void setRow(SCROW nRow) { mrCalcPos.SetRow(nRow); }

    void incTab() { mrCalcPos.IncTab(); }
    void incCol(SCCOL nInc) { mrCalcPos.IncCol(nInc); }

    void setOldMapMode(const MapMode& rOldMapMode) { maOldMapMode = rOldMapMode; }

    void setNeedMore(bool b) { mbNeedMore = b; }
    bool getNeedMore() const { return mbNeedMore; }

    void createProgressBar()
    {
        ScProgress::CreateInterpretProgress(&mrDoc, false);
        mbProgress = true;
    }

    bool hasProgressBar() const { return mbProgress; }

    ScStyleSheetPool* getStylePool() { return mpStylePool; }
};

}

bool ScDocument::IdleCalcTextWidth()            // true = try next again
{
    // #i75610# if a printer hasn't been set or created yet, don't create one for this
    if (!mbIdleEnabled || IsInLinkUpdate() || GetPrinter(false) == nullptr)
        return false;

    IdleCalcTextWidthScope aScope(*this, aCurTextWidthCalcPos);

    if (!ValidRow(aScope.Row()))
    {
        aScope.setRow(0);
        aScope.incCol(-1);
    }

    if (aScope.Col() < 0)
    {
        aScope.setCol(MaxCol());
        aScope.incTab();
    }

    if (!HasTable(aScope.Tab()))
        aScope.setTab(0);

    ScTable* pTab = maTabs[aScope.Tab()].get();
    ScStyleSheet* pStyle = static_cast<ScStyleSheet*>(aScope.getStylePool()->Find(pTab->aPageStyle, SfxStyleFamily::Page));
    OSL_ENSURE( pStyle, "Missing StyleSheet :-/" );

    if (!pStyle || getScaleValue(*pStyle, ATTR_PAGE_SCALETOPAGES) == 0)
    {
        // Move to the next sheet as the current one has scale-to-pages set,
        // and bail out.
        aScope.incTab();
        return false;
    }

    sal_uInt16 nZoom = getScaleValue(*pStyle, ATTR_PAGE_SCALE);
    Fraction aZoomFract(nZoom, 100);

    aScope.setCol(pTab->ClampToAllocatedColumns(aScope.Col()));
    // Start at specified cell position (nCol, nRow, nTab).
    ScColumn* pCol  = &pTab->aCol[aScope.Col()];
    std::optional<ScColumnTextWidthIterator> pColIter(std::in_place, *this, *pCol, aScope.Row(), MaxRow());

    OutputDevice* pDev = nullptr;
    sal_uInt16 nRestart = 0;
    sal_uInt16 nCount = 0;
    while ( (nZoom > 0) && (nCount < CALCMAX) && (nRestart < 2) )
    {
        if (pColIter->hasCell())
        {
            // More cell in this column.
            SCROW nRow = pColIter->getPos();
            aScope.setRow(nRow);

            if (pColIter->getValue() == TEXTWIDTH_DIRTY)
            {
                // Calculate text width for this cell.
                double nPPTX = 0.0;
                double nPPTY = 0.0;
                if (!pDev)
                {
                    pDev = GetPrinter();
                    assert(pDev);
                    aScope.setOldMapMode(pDev->GetMapMode());
                    pDev->SetMapMode(MapMode(MapUnit::MapPixel)); // Important for GetNeededSize

                    Point aPix1000 = pDev->LogicToPixel(Point(1000,1000), MapMode(MapUnit::MapTwip));
                    nPPTX = aPix1000.X() / 1000.0;
                    nPPTY = aPix1000.Y() / 1000.0;
                }

                if (!aScope.hasProgressBar() && pCol->IsFormulaDirty(nRow))
                    aScope.createProgressBar();

                sal_uInt16 nNewWidth = static_cast<sal_uInt16>(GetNeededSize(
                    aScope.Col(), aScope.Row(), aScope.Tab(),
                    pDev, nPPTX, nPPTY, aZoomFract,aZoomFract, truetrue));   // bTotalSize

                pColIter->setValue(nNewWidth);
                aScope.setNeedMore(true);
            }
            pColIter->next();
        }
        else
        {
            // No more cell in this column.  Move to the left column and start at row 0.

            bool bNewTab = false;

            aScope.setRow(0);
            aScope.incCol(-1);

            if (aScope.Col() < 0)
            {
                // No more column to the left.  Move to the right-most column of the next sheet.
                aScope.setCol(MaxCol());
                aScope.incTab();
                bNewTab = true;
            }

            if (!HasTable(aScope.Tab()))
            {
                // Sheet doesn't exist at specified sheet position.  Restart at sheet 0.
                aScope.setTab(0);
                nRestart++;
                bNewTab = true;
            }

            if ( nRestart < 2 )
            {
                if ( bNewTab )
                {
                    pTab = maTabs[aScope.Tab()].get();
                    aScope.setCol(pTab->ClampToAllocatedColumns(aScope.Col()));
                    pStyle = static_cast<ScStyleSheet*>(aScope.getStylePool()->Find(
                        pTab->aPageStyle, SfxStyleFamily::Page));

                    if ( pStyle )
                    {
                        // Check if the scale-to-pages setting is set. If
                        // set, we exit the loop.  If not, get the page
                        // scale factor of the new sheet.
                        if (getScaleValue(*pStyle, ATTR_PAGE_SCALETOPAGES) == 0)
                        {
                            nZoom = getScaleValue(*pStyle, ATTR_PAGE_SCALE);
                            aZoomFract = Fraction(nZoom, 100);
                        }
                        else
                            nZoom = 0;
                    }
                    else
                    {
                        OSL_FAIL( "Missing StyleSheet :-/" );
                    }
                }

                if ( nZoom > 0 )
                {
                    pCol  = &pTab->aCol[aScope.Col()];
                    pColIter.emplace(*this, *pCol, aScope.Row(), MaxRow());
                }
                else
                {
                    aScope.incTab(); // Move to the next sheet as the current one has scale-to-pages set.
                    return false;
                }
            }
        }

        ++nCount;

        if (!aScope.continueIter())
            break;
    }

    return aScope.getNeedMore();
}

void ScDocument::RepaintRange( const ScRange& rRange )
{
    if ( bIsVisible && mpShell )
    {
        ScModelObj* pModel = mpShell->GetModel();
        if ( pModel )
            pModel->RepaintRange( rRange );     // locked repaints are checked there
    }
}

void ScDocument::RepaintRange( const ScRangeList& rRange )
{
    if ( bIsVisible && mpShell )
    {
        ScModelObj* pModel = mpShell->GetModel();
        if ( pModel )
            pModel->RepaintRange( rRange );     // locked repaints are checked there
    }
}

void ScDocument::SaveDdeLinks(SvStream& rStream) const
{
    //  when 4.0-Export, remove all with mode != DEFAULT
    bool bExport40 = ( rStream.GetVersion() <= SOFFICE_FILEFORMAT_40 );

    const ::sfx2::SvBaseLinks& rLinks = GetLinkManager()->GetLinks();
    sal_uInt16 nCount = rLinks.size();

    // Count them first

    sal_uInt16 nDdeCount = 0;
    sal_uInt16 i;
    for (i=0; i<nCount; i++)
    {
        ::sfx2::SvBaseLink* pBase = rLinks[i].get();
        if (ScDdeLink* pLink = dynamic_cast<ScDdeLink*>(pBase))
            if ( !bExport40 || pLink->GetMode() == SC_DDE_DEFAULT )
                ++nDdeCount;
    }

    //  Header

    ScMultipleWriteHeader aHdr( rStream );
    rStream.WriteUInt16( nDdeCount );

    // Save links

    for (i=0; i<nCount; i++)
    {
        ::sfx2::SvBaseLink* pBase = rLinks[i].get();
        if (ScDdeLink* pLink = dynamic_cast<ScDdeLink*>(pBase))
        {
            if ( !bExport40 || pLink->GetMode() == SC_DDE_DEFAULT )
                pLink->Store( rStream, aHdr );
        }
    }
}

void ScDocument::LoadDdeLinks(SvStream& rStream)
{
    sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(bAutoCalc);
    if (!pMgr)
        return;

    ScMultipleReadHeader aHdr( rStream );

    sal_uInt16 nCount(0);
    rStream.ReadUInt16( nCount );

    const rtl_TextEncoding eCharSet = rStream.GetStreamCharSet();
    const size_t nMinStringSize = eCharSet == RTL_TEXTENCODING_UNICODE ? sizeof(sal_uInt32) : sizeof(sal_uInt16);
    const size_t nMinRecordSize = 1 + nMinStringSize*3;
    const size_t nMaxRecords = rStream.remainingSize() / nMinRecordSize;
    if (nCount > nMaxRecords)
    {
        SAL_WARN("sc""Parsing error: " << nMaxRecords <<
                 " max possible entries, but " << nCount << " claimed, truncating");
        nCount = nMaxRecords;
    }

    for (sal_uInt16 i=0; i<nCount; ++i)
    {
        ScDdeLink* pLink = new ScDdeLink( *this, rStream, aHdr );
        pMgr->InsertDDELink(pLink, pLink->GetAppl(), pLink->GetTopic(), pLink->GetItem());
    }
}

void ScDocument::SetInLinkUpdate(bool bSet)
{
    //  called from TableLink and AreaLink

    OSL_ENSURE( bInLinkUpdate != bSet, "SetInLinkUpdate twice" );
    bInLinkUpdate = bSet;
}

bool ScDocument::IsInLinkUpdate() const
{
    return bInLinkUpdate || IsInDdeLinkUpdate();
}

void ScDocument::UpdateExternalRefLinks(weld::Window* pWin)
{
    if (!pExternalRefMgr)
        return;

    sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(bAutoCalc);
    if (!pMgr)
        return;

    const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
    sal_uInt16 nCount = rLinks.size();

    bool bAny = false;

    // Collect all the external ref links first.
    std::vector<ScExternalRefLink*> aRefLinks;
    for (sal_uInt16 i = 0; i < nCount; ++i)
    {
        ::sfx2::SvBaseLink* pBase = rLinks[i].get();
        ScExternalRefLink* pRefLink = dynamic_cast<ScExternalRefLink*>(pBase);
        if (pRefLink)
            aRefLinks.push_back(pRefLink);
    }

    weld::WaitObject aWaitSwitch(pWin);

    pExternalRefMgr->enableDocTimer(false);
    ScProgress aProgress(GetDocumentShell(), ScResId(SCSTR_UPDATE_EXTDOCS), aRefLinks.size(), true);
    for (size_t i = 0, n = aRefLinks.size(); i < n; ++i)
    {
        aProgress.SetState(i+1);

        ScExternalRefLink* pRefLink = aRefLinks[i];
        if (pRefLink->Update())
        {
            bAny = true;
            continue;
        }

        // Update failed.  Notify the user.

        OUString aFile;
        sfx2::LinkManager::GetDisplayNames(pRefLink, nullptr, &aFile);
        // Decode encoded URL for display friendliness.
        INetURLObject aUrl(aFile,INetURLObject::EncodeMechanism::WasEncoded);
        aFile = aUrl.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous);

        OUString sMessage = ScResId(SCSTR_EXTDOC_NOT_LOADED) +
            "\n\n" +
            aFile;
        std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin,
                                                  VclMessageType::Warning, VclButtonsType::Ok,
                                                  sMessage));
        xBox->run();
    }

    pExternalRefMgr->enableDocTimer(true);

    if (!bAny)
        return;

    TrackFormulas();
    mpShell->Broadcast( SfxHint(SfxHintId::ScDataChanged) );

    // #i101960# set document modified, as in TrackTimeHdl for DDE links
    if (!mpShell->IsModified())
    {
        mpShell->SetModified();
        SfxBindings* pBindings = GetViewBindings();
        if (pBindings)
        {
            pBindings->Invalidate( SID_SAVEDOC );
            pBindings->Invalidate( SID_DOC_MODIFIED );
        }
    }
}

void ScDocument::CopyDdeLinks( ScDocument& rDestDoc ) const
{
    if (bIsClip)        // Create from Stream
    {
        if (pClipData)
        {
            pClipData->Seek(0);
            rDestDoc.LoadDdeLinks(*pClipData);
        }

        return;
    }

    const sfx2::LinkManager* pMgr = GetDocLinkManager().getExistingLinkManager();
    if (!pMgr)
        return;

    sfx2::LinkManager* pDestMgr = rDestDoc.GetDocLinkManager().getLinkManager(rDestDoc.bAutoCalc);
    if (!pDestMgr)
        return;

    const sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
    for (const auto & rLink : rLinks)
    {
        const sfx2::SvBaseLink* pBase = rLink.get();
        if (const ScDdeLink* p = dynamic_cast<const ScDdeLink*>(pBase))
        {
            ScDdeLink* pNew = new ScDdeLink(rDestDoc, *p);
            pDestMgr->InsertDDELink(
                pNew, pNew->GetAppl(), pNew->GetTopic(), pNew->GetItem());
        }
    }
}

namespace {

/** Tries to find the specified DDE link.
    @param pnDdePos  (out-param) if not 0, the index of the DDE link is returned here
                     (does not include other links from link manager).
    @return  The DDE link, if it exists, otherwise 0. */

ScDdeLink* lclGetDdeLink(
        const sfx2::LinkManager* pLinkManager,
        std::u16string_view rAppl, std::u16string_view rTopic, std::u16string_view rItem, sal_uInt8 nMode,
        size_t* pnDdePos = nullptr )
{
    if( pLinkManager )
    {
        const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks();
        if( pnDdePos ) *pnDdePos = 0;
        forconst auto& nLinks : rLinks )
        {
            if( ScDdeLink* pDdeLink = dynamic_cast<ScDdeLink*>( nLinks.get() )  )
            {
                if( (pDdeLink->GetAppl() == rAppl) &&
                    (pDdeLink->GetTopic() == rTopic) &&
                    (pDdeLink->GetItem() == rItem) &&
                    ((nMode == SC_DDE_IGNOREMODE) || (nMode == pDdeLink->GetMode())) )
                    return pDdeLink;
                if( pnDdePos ) ++*pnDdePos;
            }
        }
    }
    return nullptr;
}

/** Returns a pointer to the specified DDE link.
    @param nDdePos  Index of the DDE link (does not include other links from link manager).
    @return  The DDE link, if it exists, otherwise 0. */

ScDdeLink* lclGetDdeLink( const sfx2::LinkManager* pLinkManager, size_t nDdePos )
{
    if( pLinkManager )
    {
        size_t nDdeIndex = 0;       // counts only the DDE links
        forconst auto& pLink : pLinkManager->GetLinks() )
        {
            if( ScDdeLink* pDdeLink = dynamic_cast<ScDdeLink*>( pLink.get() )  )
            {
                if( nDdeIndex == nDdePos )
                    return pDdeLink;
                ++nDdeIndex;
            }
        }
    }
    return nullptr;
}

// namespace

bool ScDocument::FindDdeLink( std::u16string_view rAppl, std::u16string_view rTopic, std::u16string_view rItem,
        sal_uInt8 nMode, size_t& rnDdePos )
{
    return lclGetDdeLink( GetLinkManager(), rAppl, rTopic, rItem, nMode, &rnDdePos ) != nullptr;
}

bool ScDocument::GetDdeLinkData( size_t nDdePos, OUString& rAppl, OUString& rTopic, OUString& rItem ) const
{
    ifconst ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos ) )
    {
        rAppl  = pDdeLink->GetAppl();
        rTopic = pDdeLink->GetTopic();
        rItem  = pDdeLink->GetItem();
        return true;
    }
    return false;
}

bool ScDocument::GetDdeLinkMode( size_t nDdePos, sal_uInt8& rnMode ) const
{
    ifconst ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos ) )
    {
        rnMode = pDdeLink->GetMode();
        return true;
    }
    return false;
}

const ScMatrix* ScDocument::GetDdeLinkResultMatrix( size_t nDdePos ) const
{
    const ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos );
    return pDdeLink ? pDdeLink->GetResult() : nullptr;
}

bool ScDocument::CreateDdeLink( const OUString& rAppl, const OUString& rTopic, const OUString& rItem, sal_uInt8 nMode, const ScMatrixRef& pResults )
{
    /*  Create a DDE link without updating it (i.e. for Excel import), to prevent
        unwanted connections. First try to find existing link. Set result array
        on existing and new links. */

    //TODO: store DDE links additionally at document (for efficiency)?
    OSL_ENSURE( nMode != SC_DDE_IGNOREMODE, "ScDocument::CreateDdeLink - SC_DDE_IGNOREMODE not allowed here" );

    sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(bAutoCalc);
    if (!pMgr)
        return false;

    if (nMode != SC_DDE_IGNOREMODE)
    {
        ScDdeLink* pDdeLink = lclGetDdeLink(pMgr, rAppl, rTopic, rItem, nMode);
        if( !pDdeLink )
        {
            // create a new DDE link, but without TryUpdate
            pDdeLink = new ScDdeLink( *this, rAppl, rTopic, rItem, nMode );
            pMgr->InsertDDELink(pDdeLink, rAppl, rTopic, rItem);
        }

        // insert link results
        if( pResults )
            pDdeLink->SetResult( pResults );

        return true;
    }
    return false;
}

bool ScDocument::SetDdeLinkResultMatrix( size_t nDdePos, const ScMatrixRef& pResults )
{
    if( ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos ) )
    {
        pDdeLink->SetResult( pResults );
        return true;
    }
    return false;
}

bool ScDocument::HasAreaLinks() const
{
    const sfx2::LinkManager* pMgr = GetDocLinkManager().getExistingLinkManager();
    if (!pMgr)
        return false;

    const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
    sal_uInt16 nCount = rLinks.size();
    for (sal_uInt16 i=0; i<nCount; i++)
        if (nullptr != dynamic_cast<const ScAreaLink* >(rLinks[i].get()))
            return true;

    return false;
}

void ScDocument::UpdateAreaLinks()
{
    sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(false);
    if (!pMgr)
        return;

    const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
    // Note: SvBaseLink::Update can remove entries after the current one
    for (size_t i = 0; i < rLinks.size(); ++i)
    {
        ::sfx2::SvBaseLink* pBase = rLinks[i].get();
        if (dynamic_cast<const ScAreaLink*>( pBase) !=  nullptr)
            pBase->Update();
    }
}

void ScDocument::DeleteAreaLinksOnTab( SCTAB nTab )
{
    sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(false);
    if (!pMgr)
        return;

    const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
    sfx2::SvBaseLinks::size_type nPos = 0;
    while ( nPos < rLinks.size() )
    {
        const ::sfx2::SvBaseLink* pBase = rLinks[nPos].get();
        const ScAreaLink* pLink = dynamic_cast<const ScAreaLink*>(pBase);
        if (pLink && pLink->GetDestArea().aStart.Tab() == nTab)
            pMgr->Remove(nPos);
        else
            ++nPos;
    }
}

void ScDocument::UpdateRefAreaLinks( UpdateRefMode eUpdateRefMode,
                             const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
{
    sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(false);
    if (!pMgr)
        return;

    bool bAnyUpdate = false;

    const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
    sal_uInt16 nCount = rLinks.size();
    for (sal_uInt16 i=0; i<nCount; i++)
    {
        ::sfx2::SvBaseLink* pBase = rLinks[i].get();
        if (ScAreaLink* pLink = dynamic_cast<ScAreaLink*>(pBase))
        {
            ScRange aOutRange = pLink->GetDestArea();

            SCCOL nCol1 = aOutRange.aStart.Col();
            SCROW nRow1 = aOutRange.aStart.Row();
            SCTAB nTab1 = aOutRange.aStart.Tab();
            SCCOL nCol2 = aOutRange.aEnd.Col();
            SCROW nRow2 = aOutRange.aEnd.Row();
            SCTAB nTab2 = aOutRange.aEnd.Tab();

            ScRefUpdateRes eRes =
                ScRefUpdate::Update( *this, eUpdateRefMode,
                    rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
                    rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), nDx, nDy, nDz,
                    nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
            if ( eRes != UR_NOTHING )
            {
                pLink->SetDestArea( ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ) );
                bAnyUpdate = true;
            }
        }
    }

    if ( !bAnyUpdate )
        return;

    // #i52120# Look for duplicates (after updating all positions).
    // If several links start at the same cell, the one with the lower index is removed
    // (file format specifies only one link definition for a cell).

    sal_uInt16 nFirstIndex = 0;
    while ( nFirstIndex < nCount )
    {
        bool bFound = false;
        ::sfx2::SvBaseLink* pFirst = rLinks[nFirstIndex].get();
        if (ScAreaLink* pFirstLink = dynamic_cast<ScAreaLink*>(pFirst))
        {
            ScAddress aFirstPos = pFirstLink->GetDestArea().aStart;
            for ( sal_uInt16 nSecondIndex = nFirstIndex + 1; nSecondIndex < nCount && !bFound; ++nSecondIndex )
            {
                ::sfx2::SvBaseLink* pSecond = rLinks[nSecondIndex].get();
                ScAreaLink* pSecondLink = dynamic_cast<ScAreaLink*>(pSecond);
                if (pSecondLink && pSecondLink->GetDestArea().aStart == aFirstPos)
                {
                    // remove the first link, exit the inner loop, don't increment nFirstIndex
                    pMgr->Remove(pFirst);
                    nCount = rLinks.size();
                    bFound = true;
                }
            }
        }
        if (!bFound)
            ++nFirstIndex;
    }
}

void ScDocument::CheckLinkFormulaNeedingCheck( const ScTokenArray& rCode )
{
    if (HasLinkFormulaNeedingCheck())
        return;

    // Prefer RPN over tokenized formula if available.
    if (rCode.GetCodeLen())
    {
        if (rCode.HasOpCodeRPN(ocDde) || rCode.HasOpCodeRPN(ocWebservice))
            SetLinkFormulaNeedingCheck(true);
    }
    else if (rCode.GetLen())
    {
        if (rCode.HasOpCode(ocDde) || rCode.HasOpCode(ocWebservice))
            SetLinkFormulaNeedingCheck(true);
    }
    else
    {
        // Possible with named expression without expression like Excel
        // internal print ranges, obscure user define names, ... formula error
        // cells without formula ...
        SAL_WARN("sc.core","ScDocument::CheckLinkFormulaNeedingCheck - called with empty ScTokenArray");
    }
}

// TimerDelays etc.
void ScDocument::KeyInput()
{
    if ( pChartListenerCollection->hasListeners() )
        pChartListenerCollection->StartTimer();
    if (apTemporaryChartLock)
        apTemporaryChartLock->StartOrContinueLocking();
}

SfxBindings* ScDocument::GetViewBindings()
{
    //  used to invalidate slots after changes to this document

    if ( !mpShell )
        return nullptr;        // no ObjShell -> no view

    //  first check current view
    SfxViewFrame* pViewFrame = SfxViewFrame::Current();
    if ( pViewFrame && pViewFrame->GetObjectShell() != mpShell )     // wrong document?
        pViewFrame = nullptr;

    //  otherwise use first view for this doc
    if ( !pViewFrame )
        pViewFrame = SfxViewFrame::GetFirst( mpShell );

    if (pViewFrame)
        return &pViewFrame->GetBindings();
    else
        return nullptr;
}

void ScDocument::TransliterateText( const ScMarkData& rMultiMark, TransliterationFlags nType )
{
    OSL_ENSURE( rMultiMark.IsMultiMarked(), "TransliterateText: no selection" );

    utl::TransliterationWrapper aTransliterationWrapper( comphelper::getProcessComponentContext(), nType );
    bool bConsiderLanguage = aTransliterationWrapper.needLanguageForTheMode();
    LanguageType nLanguage = LANGUAGE_SYSTEM;

    std::unique_ptr<ScEditEngineDefaulter> pEngine;        // not using mpEditEngine member because of defaults

    SCTAB nCount = GetTableCount();
    for (const SCTAB& nTab : rMultiMark)
    {
        if (nTab >= nCount)
            break;

        if ( maTabs[nTab] )
        {
            SCCOL nCol = 0;
            SCROW nRow = 0;

            bool bFound = rMultiMark.IsCellMarked( nCol, nRow );
            if (!bFound)
                bFound = GetNextMarkedCell( nCol, nRow, nTab, rMultiMark );

            while (bFound)
            {
                ScRefCellValue aCell(*this, ScAddress(nCol, nRow, nTab));

                // fdo#32786 TITLE_CASE/SENTENCE_CASE need the extra handling in EditEngine (loop over words/sentences).
                // Still use TransliterationWrapper directly for text cells with other transliteration types,
                // for performance reasons.
                if (aCell.getType() == CELLTYPE_EDIT ||
                    (aCell.getType() == CELLTYPE_STRING &&
                     ( nType == TransliterationFlags::SENTENCE_CASE || nType == TransliterationFlags::TITLE_CASE)))
                {
                    if (!pEngine)
                        pEngine.reset(new ScFieldEditEngine(this, GetEnginePool(), GetEditPool()));

                    // defaults from cell attributes must be set so right language is used
                    const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab );
                    SfxItemSet aDefaults( pEngine->GetEmptyItemSet() );
                    if ( ScStyleSheet* pPreviewStyle = GetPreviewCellStyle( nCol, nRow, nTab ) )
                    {
                        ScPatternAttr aPreviewPattern( *pPattern );
                        aPreviewPattern.SetStyleSheet(pPreviewStyle);
                        aPreviewPattern.FillEditItemSet(&aDefaults);
                    }
                    else
                    {
                        SfxItemSet* pFontSet = GetPreviewFont( nCol, nRow, nTab );
                        pPattern->FillEditItemSet(&aDefaults, pFontSet);
                    }
                    pEngine->SetDefaults(std::move(aDefaults));
                    if (aCell.getType() == CELLTYPE_STRING)
                        pEngine->SetTextCurrentDefaults(aCell.getSharedString()->getString());
                    else if (aCell.getEditText())
                        pEngine->SetTextCurrentDefaults(*aCell.getEditText());

                    pEngine->ClearModifyFlag();

                    sal_Int32 nLastPar = pEngine->GetParagraphCount();
                    if (nLastPar)
                        --nLastPar;
                    sal_Int32 nTxtLen = pEngine->GetTextLen(nLastPar);
                    ESelection aSelAll( 0, 0, nLastPar, nTxtLen );

                    pEngine->TransliterateText( aSelAll, nType );

                    if ( pEngine->IsModified() )
                    {
                        ScEditAttrTester aTester( pEngine.get() );
                        if ( aTester.NeedsObject() )
                        {
                            // remove defaults (paragraph attributes) before creating text object
                            pEngine->SetDefaults(pEngine->GetEmptyItemSet());

                            // The cell will take ownership of the text object instance.
                            SetEditText(ScAddress(nCol,nRow,nTab), pEngine->CreateTextObject());
                        }
                        else
                        {
                            ScSetStringParam aParam;
                            aParam.setTextInput();
                            SetString(ScAddress(nCol,nRow,nTab), pEngine->GetText(), &aParam);
                        }
                    }
                }

                else if (aCell.getType() == CELLTYPE_STRING)
                {
                    OUString aOldStr = aCell.getSharedString()->getString();
                    sal_Int32 nOldLen = aOldStr.getLength();

                    if ( bConsiderLanguage )
                    {
                        SvtScriptType nScript = GetStringScriptType( aOldStr );        //TODO: cell script type?
                        sal_uInt16 nWhich = ( nScript == SvtScriptType::ASIAN ) ? ATTR_CJK_FONT_LANGUAGE :
                                        ( ( nScript == SvtScriptType::COMPLEX ) ? ATTR_CTL_FONT_LANGUAGE :
                                                                                ATTR_FONT_LANGUAGE );
                        nLanguage = static_cast<const SvxLanguageItem*>(GetAttr( nCol, nRow, nTab, nWhich ))->GetValue();
                    }

                    uno::Sequence<sal_Int32> aOffsets;
                    OUString aNewStr = aTransliterationWrapper.transliterate( aOldStr, nLanguage, 0, nOldLen, &aOffsets );

                    if ( aNewStr != aOldStr )
                    {
                        ScSetStringParam aParam;
                        aParam.setTextInput();
                        SetString(ScAddress(nCol,nRow,nTab), aNewStr, &aParam);
                    }
                }
                bFound = GetNextMarkedCell( nCol, nRow, nTab, rMultiMark );
            }
        }
    }
}

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

Messung V0.5
C=96 H=47 G=75

¤ Dauer der Verarbeitung: 0.13 Sekunden  ¤

*© 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge