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

Quelle  csvgrid.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 <comphelper/lok.hxx>
#include <csvgrid.hxx>
#include <csvtablebox.hxx>

#include <algorithm>
#include <memory>

#include <svtools/colorcfg.hxx>
#include <sal/macros.h>
#include <tools/poly.hxx>
#include <scmod.hxx>
#include <asciiopt.hxx>
#include <impex.hxx>
#include <AccessibleCsvControl.hxx>

// *** edit engine ***
#include <editeng/eeitem.hxx>
#include <vcl/commandevent.hxx>
#include <vcl/event.hxx>
#include <vcl/settings.hxx>
#include <vcl/svapp.hxx>
#include <vcl/virdev.hxx>
#include <vcl/weldutils.hxx>

#include <editeng/colritem.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/fontitem.hxx>
#include <editeng/wghtitem.hxx>
#include <editeng/postitem.hxx>
#include <editeng/langitem.hxx>
#include <svl/itempool.hxx>
#include <svl/itemset.hxx>
#include <editutil.hxx>
// *** edit engine ***

namespace {

struct Func_SetType
{
    sal_Int32                   mnType;
    explicit                    Func_SetType( sal_Int32 nType ) : mnType( nType ) {}
    void                 operator()( ScCsvColState& rState ) const
        { rState.mnType = mnType; }
};

struct Func_Select
{
    bool                        mbSelect;
    explicit                    Func_Select( bool bSelect ) : mbSelect( bSelect ) {}
    void                 operator()( ScCsvColState& rState ) const
        { rState.Select( mbSelect ); }
};

}

ScCsvGrid::ScCsvGrid(const ScCsvLayoutData& rData, std::unique_ptr<weld::Menu> xPopup, ScCsvTableBox* pTableBox)
    : ScCsvControl(rData)
    , mpTableBox(pTableBox)
    , mpBackgrDev( VclPtr<VirtualDevice>::Create() )
    , mpGridDev( VclPtr<VirtualDevice>::Create() )
    , mxPopup(std::move(xPopup))
    , mpColorConfig( nullptr )
    , mpEditEngine( new ScEditEngineDefaulter( EditEngine::CreatePool().get(), true ) )
    , maColStates( 1 )
    , maTypeNames( 1 )
    , mnFirstImpLine( 0 )
    , mnRecentSelCol( CSV_COLUMN_INVALID )
    , mnMTCurrCol( SAL_MAX_UINT32 )
    , mbTracking( false )
    , mbMTSelecting( false )
{
    mpEditEngine->SetRefDevice( mpBackgrDev.get() );
    mpEditEngine->SetRefMapMode( MapMode( MapUnit::MapPixel ) );
    maEdEngSize = mpEditEngine->GetPaperSize();
}

void ScCsvGrid::SetDrawingArea(weld::DrawingArea* pDrawingArea)
{
    OutputDevice& rRefDevice = pDrawingArea->get_ref_device();
    maHeaderFont = Application::GetSettings().GetStyleSettings().GetAppFont();

    // expand the point size of the desired font to the equivalent pixel size
    weld::SetPointFont(rRefDevice, maHeaderFont);
    maHeaderFont = rRefDevice.GetFont();

    // Because this is an always LeftToRight layout widget the initial size of
    // this widget needs to be smaller than the size of the parent scrolling
    // window (ScCsvTableBox ctor) because in RTL mode the alignment is against
    // the right edge of the parent, and if larger than the scrolling window
    // the left edge will be lost. If this widget is smaller than the scrolling
    // window it is stretched to fit the parent and the problem doesn't arise.
    Size aInitialSize(10, 10);
    if (comphelper::LibreOfficeKit::isActive())
        aInitialSize = Size(-1, 150);
    ScCsvControl::SetDrawingArea(pDrawingArea);
    pDrawingArea->set_size_request(aInitialSize.Width(), aInitialSize.Height());
    SetOutputSizePixel(aInitialSize);

    EnableRTL( false ); // RTL

    InitFonts();
    ImplClearSplits();
}

ScCsvGrid::~ScCsvGrid()
{
    OSL_ENSURE(mpColorConfig, "the object hasn't been initialized properly");
    if (mpColorConfig)
        mpColorConfig->RemoveListener(this);
    mpBackgrDev.disposeAndClear();
    mpGridDev.disposeAndClear();
}

void
ScCsvGrid::Init()
{
    OSL_PRECOND(!mpColorConfig, "the object has already been initialized");
    mpColorConfig = &ScModule::get()->GetColorConfig();
    InitColors();
    mpColorConfig->AddListener(this);
}

// common grid handling -------------------------------------------------------

void ScCsvGrid::UpdateLayoutData()
{
    DisableRepaint();
    OutputDevice& rRefDevice = GetDrawingArea()->get_ref_device();
    rRefDevice.SetFont(maMonoFont);
    Execute(CSVCMD_SETCHARWIDTH, rRefDevice.GetTextWidth(OUString('X')));
    Execute(CSVCMD_SETLINEHEIGHT, rRefDevice.GetTextHeight() + 1);
    rRefDevice.SetFont(maHeaderFont);
    Execute(CSVCMD_SETHDRHEIGHT, rRefDevice.GetTextHeight() + 1);
    UpdateOffsetX();
    EnableRepaint();
}

void ScCsvGrid::UpdateOffsetX()
{
    sal_Int32 nLastLine = GetLastVisLine() + 1;
    sal_Int32 nDigits = 2;
    for (;;)
    {
        nLastLine /= 10;
        if (!nLastLine)
            break;
        ++nDigits;
    }
    nDigits = std::max( nDigits, sal_Int32( 3 ) );
    Execute(CSVCMD_SETHDRWIDTH, GetDrawingArea()->get_approximate_digit_width() * nDigits);
}

void ScCsvGrid::ApplyLayout( const ScCsvLayoutData& rOldData )
{
    ScCsvDiff nDiff = GetLayoutData().GetDiff( rOldData );
    if( nDiff == ScCsvDiff::Equal ) return;

    DisableRepaint();

    if( nDiff & ScCsvDiff::RulerCursor )
    {
        ImplInvertCursor( rOldData.mnPosCursor );
        ImplInvertCursor( GetRulerCursorPos() );
    }

    if( nDiff & ScCsvDiff::PosCount )
    {
        if( GetPosCount() < rOldData.mnPosCount )
        {
            SelectAll( false );
            maSplits.RemoveRange( GetPosCount(), rOldData.mnPosCount );
        }
        else
            maSplits.Remove( rOldData.mnPosCount );
        maSplits.Insert( GetPosCount() );
        maColStates.resize( maSplits.Count() - 1 );
    }

    if( nDiff & ScCsvDiff::LineOffset )
    {
        Execute( CSVCMD_UPDATECELLTEXTS );
        UpdateOffsetX();
    }

    ScCsvDiff nHVDiff = nDiff & (ScCsvDiff::HorizontalMask | ScCsvDiff::VerticalMask);
    if( nHVDiff == ScCsvDiff::PosOffset )
        ImplDrawHorzScrolled( rOldData.mnPosOffset );
    else if( nHVDiff != ScCsvDiff::Equal )
        InvalidateGfx();

    EnableRepaint();

    if( nDiff & (ScCsvDiff::PosOffset | ScCsvDiff::LineOffset) )
        AccSendVisibleEvent();
}

void ScCsvGrid::SetFirstImportedLine( sal_Int32 nLine )
{
    ImplDrawFirstLineSep( false );
    mnFirstImpLine = nLine;
    ImplDrawFirstLineSep( true );
    ImplDrawGridDev();
    Repaint();
}

sal_Int32 ScCsvGrid::GetNoScrollCol( sal_Int32 nPos ) const
{
    sal_Int32 nNewPos = nPos;
    if( nNewPos != CSV_POS_INVALID )
    {
        if( nNewPos < GetFirstVisPos() + CSV_SCROLL_DIST )
        {
            sal_Int32 nScroll = (GetFirstVisPos() > 0) ? CSV_SCROLL_DIST : 0;
            nNewPos = GetFirstVisPos() + nScroll;
        }
        else if( nNewPos > GetLastVisPos() - CSV_SCROLL_DIST - 1 )
        {
            sal_Int32 nScroll = (GetFirstVisPos() < GetMaxPosOffset()) ? CSV_SCROLL_DIST : 0;
            nNewPos = GetLastVisPos() - nScroll - 1;
        }
    }
    return nNewPos;
}

void ScCsvGrid::InitColors()
{
    OSL_PRECOND(mpColorConfig, "the object hasn't been initialized properly");
    if ( !mpColorConfig )
        return;
    maBackColor = mpColorConfig->GetColorValue( ::svtools::DOCCOLOR ).nColor;
    maGridColor = mpColorConfig->GetColorValue( ::svtools::CALCGRID ).nColor;
    maGridPBColor = mpColorConfig->GetColorValue( ::svtools::CALCPAGEBREAK ).nColor;
    maAppBackColor = mpColorConfig->GetColorValue( ::svtools::APPBACKGROUND ).nColor;
    maTextColor = mpColorConfig->GetColorValue( ::svtools::FONTCOLOR, false ).nColor;

    // tdf#147386 If Automatic font color is used, then check background color and use Black/White as font color
    if ( maTextColor == COL_AUTO )
    {
        if ( maBackColor.IsDark() )
            maTextColor = COL_WHITE;
        else
            maTextColor = COL_BLACK;
    }

    const StyleSettings& rSett = Application::GetSettings().GetStyleSettings();
    maHeaderBackColor = rSett.GetFaceColor();
    maHeaderGridColor = rSett.GetDarkShadowColor();
    maHeaderTextColor = rSett.GetButtonTextColor();
    maSelectColor = rSett.GetActiveColor();

    InvalidateGfx();
}

void ScCsvGrid::InitFonts()
{
    maMonoFont = OutputDevice::GetDefaultFont( DefaultFontType::FIXED, LANGUAGE_ENGLISH_US, GetDefaultFontFlags::NONE );
    maMonoFont.SetFontSize( Size( maMonoFont.GetFontSize().Width(), maHeaderFont.GetFontSize().Height() ) );

    /* *** Set edit engine defaults ***
        maMonoFont for Latin script, smaller default font for Asian and Complex script. */


    // get default fonts
    SvxFontItem aLatinItem( EE_CHAR_FONTINFO );
    SvxFontItem aAsianItem( EE_CHAR_FONTINFO_CJK );
    SvxFontItem aComplexItem( EE_CHAR_FONTINFO_CTL );
    ::GetDefaultFonts( aLatinItem, aAsianItem, aComplexItem );

    // create item set for defaults
    SfxItemSet aDefSet(mpEditEngine->GetEmptyItemSet());
    EditEngine::SetFontInfoInItemSet(aDefSet, maMonoFont);
    aDefSet.Put(aAsianItem);
    aDefSet.Put(aComplexItem);

    // set Asian/Complex font size to height of character in Latin font
    sal_uLong nFontHt = static_cast< sal_uLong >( maMonoFont.GetFontSize().Height() );
    aDefSet.Put(SvxFontHeightItem(nFontHt, 100, EE_CHAR_FONTHEIGHT_CJK));
    aDefSet.Put(SvxFontHeightItem(nFontHt, 100, EE_CHAR_FONTHEIGHT_CTL));

    // copy other items from default font
    const SfxPoolItem& rWeightItem = aDefSet.Get(EE_CHAR_WEIGHT);
    std::unique_ptr<SfxPoolItem> pNewItem(rWeightItem.Clone());
    pNewItem->SetWhich(EE_CHAR_WEIGHT_CJK);
    aDefSet.Put(*pNewItem);
    pNewItem->SetWhich(EE_CHAR_WEIGHT_CTL);
    aDefSet.Put(*pNewItem);
    const SfxPoolItem& rItalicItem = aDefSet.Get(EE_CHAR_ITALIC);
    pNewItem.reset(rItalicItem.Clone());
    pNewItem->SetWhich(EE_CHAR_ITALIC_CJK);
    aDefSet.Put(*pNewItem);
    pNewItem->SetWhich(EE_CHAR_ITALIC_CTL);
    aDefSet.Put(*pNewItem);
    const SfxPoolItem& rLangItem = aDefSet.Get(EE_CHAR_LANGUAGE);
    pNewItem.reset(rLangItem.Clone());
    pNewItem->SetWhich(EE_CHAR_LANGUAGE_CJK);
    aDefSet.Put(*pNewItem);
    pNewItem->SetWhich(EE_CHAR_LANGUAGE_CTL);
    aDefSet.Put(*pNewItem);

    mpEditEngine->SetDefaults(std::move(aDefSet));
    InvalidateGfx();
}

void ScCsvGrid::InitSizeData()
{
    maWinSize = GetOutputSizePixel();
    mpBackgrDev->SetOutputSizePixel( maWinSize );
    mpGridDev->SetOutputSizePixel( maWinSize );
    InvalidateGfx();
}

// split handling -------------------------------------------------------------

void ScCsvGrid::InsertSplit( sal_Int32 nPos )
{
    if( ImplInsertSplit( nPos ) )
    {
        DisableRepaint();
        Execute( CSVCMD_EXPORTCOLUMNTYPE );
        Execute( CSVCMD_UPDATECELLTEXTS );
        sal_uInt32 nColIx = GetColumnFromPos( nPos );
        ImplDrawColumn( nColIx - 1 );
        ImplDrawColumn( nColIx );
        ValidateGfx();  // performance: do not redraw all columns
        EnableRepaint();
    }
}

void ScCsvGrid::RemoveSplit( sal_Int32 nPos )
{
    if( ImplRemoveSplit( nPos ) )
    {
        DisableRepaint();
        Execute( CSVCMD_EXPORTCOLUMNTYPE );
        Execute( CSVCMD_UPDATECELLTEXTS );
        ImplDrawColumn( GetColumnFromPos( nPos ) );
        ValidateGfx();  // performance: do not redraw all columns
        EnableRepaint();
    }
}

void ScCsvGrid::MoveSplit( sal_Int32 nPos, sal_Int32 nNewPos )
{
    sal_uInt32 nColIx = GetColumnFromPos( nPos );
    if( nColIx == CSV_COLUMN_INVALID )
        return;

    DisableRepaint();
    if( (GetColumnPos( nColIx - 1 ) < nNewPos) && (nNewPos < GetColumnPos( nColIx + 1 )) )
    {
        // move a split in the range between 2 others -> keep selection state of both columns
        maSplits.Remove( nPos );
        maSplits.Insert( nNewPos );
        Execute( CSVCMD_UPDATECELLTEXTS );
        ImplDrawColumn( nColIx - 1 );
        ImplDrawColumn( nColIx );
        ValidateGfx();  // performance: do not redraw all columns
        AccSendTableUpdateEvent( nColIx - 1, nColIx );
    }
    else
    {
        ImplRemoveSplit( nPos );
        ImplInsertSplit( nNewPos );
        Execute( CSVCMD_EXPORTCOLUMNTYPE );
        Execute( CSVCMD_UPDATECELLTEXTS );
    }
    EnableRepaint();
}

void ScCsvGrid::RemoveAllSplits()
{
    DisableRepaint();
    ImplClearSplits();
    Execute( CSVCMD_EXPORTCOLUMNTYPE );
    Execute( CSVCMD_UPDATECELLTEXTS );
    EnableRepaint();
}

void ScCsvGrid::SetSplits( const ScCsvSplits& rSplits )
{
    DisableRepaint();
    ImplClearSplits();
    sal_uInt32 nCount = rSplits.Count();
    for( sal_uInt32 nIx = 0; nIx < nCount; ++nIx )
        maSplits.Insert( rSplits[ nIx ] );
    maColStates.clear();
    maColStates.resize( maSplits.Count() - 1 );
    Execute( CSVCMD_EXPORTCOLUMNTYPE );
    Execute( CSVCMD_UPDATECELLTEXTS );
    EnableRepaint();
}

bool ScCsvGrid::ImplInsertSplit( sal_Int32 nPos )
{
    sal_uInt32 nColIx = GetColumnFromPos( nPos );
    bool bRet = (nColIx < GetColumnCount()) && maSplits.Insert( nPos );
    if( bRet )
    {
        ScCsvColState aState( GetColumnType( nColIx ) );
        aState.Select( IsSelected( nColIx ) && IsSelected( nColIx + 1 ) );
        maColStates.insert( maColStates.begin() + nColIx + 1, aState );
        AccSendInsertColumnEvent( nColIx + 1, nColIx + 1 );
        AccSendTableUpdateEvent( nColIx, nColIx );
    }
    return bRet;
}

bool ScCsvGrid::ImplRemoveSplit( sal_Int32 nPos )
{
    bool bRet = maSplits.Remove( nPos );
    if( bRet )
    {
        sal_uInt32 nColIx = GetColumnFromPos( nPos );
        bool bSel = IsSelected( nColIx ) || IsSelected( nColIx + 1 );
        maColStates.erase( maColStates.begin() + nColIx + 1 );
        maColStates[ nColIx ].Select( bSel );
        AccSendRemoveColumnEvent( nColIx + 1, nColIx + 1 );
        AccSendTableUpdateEvent( nColIx, nColIx );
    }
    return bRet;
}

void ScCsvGrid::ImplClearSplits()
{
    sal_uInt32 nColumns = GetColumnCount();
    maSplits.Clear();
    maSplits.Insert( 0 );
    maSplits.Insert( GetPosCount() );
    maColStates.resize( 1 );
    InvalidateGfx();
    AccSendRemoveColumnEvent( 1, nColumns - 1 );
}

// columns/column types -------------------------------------------------------

sal_uInt32 ScCsvGrid::GetFirstVisColumn() const
{
    return GetColumnFromPos( GetFirstVisPos() );
}

sal_uInt32 ScCsvGrid::GetLastVisColumn() const
{
    return GetColumnFromPos( std::min( GetLastVisPos(), GetPosCount() ) - 1 );
}

bool ScCsvGrid::IsValidColumn( sal_uInt32 nColIndex ) const
{
    return nColIndex < GetColumnCount();
}

bool ScCsvGrid::IsVisibleColumn( sal_uInt32 nColIndex ) const
{
    return  IsValidColumn( nColIndex ) &&
            (GetColumnPos( nColIndex ) < GetLastVisPos()) &&
            (GetFirstVisPos() < GetColumnPos( nColIndex + 1 ));
}

sal_Int32 ScCsvGrid::GetColumnX( sal_uInt32 nColIndex ) const
{
    return GetX( GetColumnPos( nColIndex ) );
}

sal_uInt32 ScCsvGrid::GetColumnFromX( sal_Int32 nX ) const
{
    sal_Int32 nPos = (nX - GetFirstX()) / GetCharWidth() + GetFirstVisPos();
    return ((GetFirstVisPos() <= nPos) && (nPos <= GetLastVisPos())) ?
        GetColumnFromPos( nPos ) : CSV_COLUMN_INVALID;
}

sal_uInt32 ScCsvGrid::GetColumnFromPos( sal_Int32 nPos ) const
{
    return maSplits.UpperBound( nPos );
}

sal_Int32 ScCsvGrid::GetColumnWidth( sal_uInt32 nColIndex ) const
{
    return IsValidColumn( nColIndex ) ? (GetColumnPos( nColIndex + 1 ) - GetColumnPos( nColIndex )) : 0;
}

void ScCsvGrid::SetColumnStates( ScCsvColStateVec&& rStates )
{
    maColStates = std::move(rStates);
    maColStates.resize( maSplits.Count() - 1 );
    Execute( CSVCMD_EXPORTCOLUMNTYPE );
    AccSendTableUpdateEvent( 0, GetColumnCount(), false );
    AccSendSelectionEvent();
}

sal_Int32 ScCsvGrid::GetColumnType( sal_uInt32 nColIndex ) const
{
    return IsValidColumn( nColIndex ) ? maColStates[ nColIndex ].mnType : CSV_TYPE_NOSELECTION;
}

void ScCsvGrid::SetColumnType( sal_uInt32 nColIndex, sal_Int32 nColType )
{
    if( IsValidColumn( nColIndex ) )
    {
        maColStates[ nColIndex ].mnType = nColType;
        AccSendTableUpdateEvent( nColIndex, nColIndex, false );
    }
}

sal_Int32 ScCsvGrid::GetSelColumnType() const
{
    sal_uInt32 nColIx = GetFirstSelected();
    if( nColIx == CSV_COLUMN_INVALID )
        return CSV_TYPE_NOSELECTION;

    sal_Int32 nType = GetColumnType( nColIx );
    while( (nColIx != CSV_COLUMN_INVALID) && (nType != CSV_TYPE_MULTI) )
    {
        if( nType != GetColumnType( nColIx ) )
            nType = CSV_TYPE_MULTI;
        nColIx = GetNextSelected( nColIx );
    }
    return nType;
}

void ScCsvGrid::SetSelColumnType( sal_Int32 nType )
{
    if( (nType != CSV_TYPE_MULTI) && (nType != CSV_TYPE_NOSELECTION) )
    {
        for( sal_uInt32 nColIx = GetFirstSelected(); nColIx != CSV_COLUMN_INVALID; nColIx = GetNextSelected( nColIx ) )
            SetColumnType( nColIx, nType );
        Repaint( true );
        Execute( CSVCMD_EXPORTCOLUMNTYPE );
    }
}

void ScCsvGrid::SetTypeNames( std::vector<OUString>&& rTypeNames )
{
    OSL_ENSURE( !rTypeNames.empty(), "ScCsvGrid::SetTypeNames - vector is empty" );
    maTypeNames = std::move(rTypeNames);
    Repaint( true );

    mxPopup->clear();
    sal_uInt32 nCount = maTypeNames.size();
    for (sal_uInt32 nIx = 0; nIx < nCount; ++nIx)
        mxPopup->append(OUString::number(nIx), maTypeNames[nIx]);

    ::std::for_each( maColStates.begin(), maColStates.end(), Func_SetType( CSV_TYPE_DEFAULT ) );
}

OUString ScCsvGrid::GetColumnTypeName( sal_uInt32 nColIndex ) const
{
    sal_uInt32 nTypeIx = static_cast< sal_uInt32 >( GetColumnType( nColIndex ) );
    return (nTypeIx < maTypeNames.size()) ? maTypeNames[ nTypeIx ] : OUString();
}

static sal_uInt8 lcl_GetExtColumnType( sal_Int32 nIntType )
{
    static const sal_uInt8 pExtTypes[] =
        { SC_COL_STANDARD, SC_COL_TEXT, SC_COL_DMY, SC_COL_MDY, SC_COL_YMD, SC_COL_ENGLISH, SC_COL_SKIP };
    static const sal_Int32 nExtTypeCount = SAL_N_ELEMENTS(pExtTypes);
    return pExtTypes[ ((0 <= nIntType) && (nIntType < nExtTypeCount)) ? nIntType : 0 ];
}

void ScCsvGrid::FillColumnDataSep( ScAsciiOptions& rOptions ) const
{
    sal_uInt32 nCount = GetColumnCount();
    ScCsvExpDataVec aDataVec;

    for( sal_uInt32 nColIx = 0; nColIx < nCount; ++nColIx )
    {
        if( GetColumnType( nColIx ) != CSV_TYPE_DEFAULT )
            // 1-based column index
            aDataVec.emplace_back(
                static_cast< sal_Int32 >( nColIx + 1 ),
                lcl_GetExtColumnType( GetColumnType( nColIx ) ) );
    }
    rOptions.SetColumnInfo( aDataVec );
}

void ScCsvGrid::FillColumnDataFix( ScAsciiOptions& rOptions ) const
{
    sal_uInt32 nCount = std::min( GetColumnCount(), static_cast<sal_uInt32>(MAXCOLCOUNT) );
    ScCsvExpDataVec aDataVec( nCount + 1 );

    for( sal_uInt32 nColIx = 0; nColIx < nCount; ++nColIx )
    {
        ScCsvExpData& rData = aDataVec[ nColIx ];
        rData.mnIndex = GetColumnPos( nColIx );
        rData.mnType = lcl_GetExtColumnType( GetColumnType( nColIx ) );
    }
    aDataVec[ nCount ].mnIndex = SAL_MAX_INT32;
    aDataVec[ nCount ].mnType = SC_COL_SKIP;
    rOptions.SetColumnInfo( aDataVec );
}

void ScCsvGrid::ScrollVertRel( ScMoveMode eDir )
{
    sal_Int32 nLine = GetFirstVisLine();
    switch( eDir )
    {
        case MOVE_PREV:     --nLine;                        break;
        case MOVE_NEXT:     ++nLine;                        break;
        case MOVE_FIRST:    nLine = 0;                      break;
        case MOVE_LAST:     nLine = GetMaxLineOffset();     break;
        case MOVE_PREVPAGE: nLine -= GetVisLineCount() - 2; break;
        case MOVE_NEXTPAGE: nLine += GetVisLineCount() - 2; break;
        default:
        {
            // added to avoid warnings
        }
    }
    Execute( CSVCMD_SETLINEOFFSET, nLine );
}

void ScCsvGrid::ExecutePopup( const Point& rPos )
{
    OUString sItemId = mxPopup->popup_at_rect(GetDrawingArea(), tools::Rectangle(rPos, Size(1, 1)));
    if (!sItemId.isEmpty())   // empty = cancelled
        Execute(CSVCMD_SETCOLUMNTYPE, sItemId.toInt32());
}

// selection handling ---------------------------------------------------------

bool ScCsvGrid::IsSelected( sal_uInt32 nColIndex ) const
{
    return IsValidColumn( nColIndex ) && maColStates[ nColIndex ].IsSelected();
}

sal_uInt32 ScCsvGrid::GetFirstSelected() const
{
    return IsSelected( 0 ) ? 0 : GetNextSelected( 0 );
}

sal_uInt32 ScCsvGrid::GetNextSelected( sal_uInt32 nFromIndex ) const
{
    sal_uInt32 nColCount = GetColumnCount();
    for( sal_uInt32 nColIx = nFromIndex + 1; nColIx < nColCount; ++nColIx )
        if( IsSelected( nColIx ) )
            return nColIx;
    return CSV_COLUMN_INVALID;
}

void ScCsvGrid::Select( sal_uInt32 nColIndex, bool bSelect )
{
    if( IsValidColumn( nColIndex ) )
    {
        maColStates[ nColIndex ].Select( bSelect );
        ImplDrawColumnSelection( nColIndex );
        Repaint();
        Execute( CSVCMD_EXPORTCOLUMNTYPE );
        if( bSelect )
            mnRecentSelCol = nColIndex;
        AccSendSelectionEvent();
    }
}

void ScCsvGrid::ToggleSelect( sal_uInt32 nColIndex )
{
    Select( nColIndex, !IsSelected( nColIndex ) );
}

void ScCsvGrid::SelectRange( sal_uInt32 nColIndex1, sal_uInt32 nColIndex2, bool bSelect )
{
    if( nColIndex1 == CSV_COLUMN_INVALID )
        Select( nColIndex2 );
    else if( nColIndex2 == CSV_COLUMN_INVALID )
        Select( nColIndex1 );
    else if( nColIndex1 > nColIndex2 )
    {
        SelectRange( nColIndex2, nColIndex1, bSelect );
        if( bSelect )
            mnRecentSelCol = nColIndex1;
    }
    else if( IsValidColumn( nColIndex1 ) && IsValidColumn( nColIndex2 ) )
    {
        for( sal_uInt32 nColIx = nColIndex1; nColIx <= nColIndex2; ++nColIx )
        {
            maColStates[ nColIx ].Select( bSelect );
            ImplDrawColumnSelection( nColIx );
        }
        Repaint();
        Execute( CSVCMD_EXPORTCOLUMNTYPE );
        if( bSelect )
            mnRecentSelCol = nColIndex1;
        AccSendSelectionEvent();
    }
}

void ScCsvGrid::SelectAll( bool bSelect )
{
    SelectRange( 0, GetColumnCount() - 1, bSelect );
}

void ScCsvGrid::MoveCursor( sal_uInt32 nColIndex )
{
    DisableRepaint();
    if( IsValidColumn( nColIndex ) )
    {
        sal_Int32 nPosBeg = GetColumnPos( nColIndex );
        sal_Int32 nPosEnd = GetColumnPos( nColIndex + 1 );
        sal_Int32 nMinPos = std::max( nPosBeg - CSV_SCROLL_DIST, sal_Int32( 0 ) );
        sal_Int32 nMaxPos = std::min( nPosEnd - GetVisPosCount() + CSV_SCROLL_DIST + sal_Int32( 1 ), nMinPos );
        if( nPosBeg - CSV_SCROLL_DIST + 1 <= GetFirstVisPos() )
            Execute( CSVCMD_SETPOSOFFSET, nMinPos );
        else if( nPosEnd + CSV_SCROLL_DIST >= GetLastVisPos() )
            Execute( CSVCMD_SETPOSOFFSET, nMaxPos );
    }
    Execute( CSVCMD_MOVEGRIDCURSOR, GetColumnPos( nColIndex ) );
    EnableRepaint();
}

void ScCsvGrid::MoveCursorRel( ScMoveMode eDir )
{
    if( GetFocusColumn() == CSV_COLUMN_INVALID )
        return;

    switch( eDir )
    {
        case MOVE_FIRST:
            MoveCursor( 0 );
        break;
        case MOVE_LAST:
            MoveCursor( GetColumnCount() - 1 );
        break;
        case MOVE_PREV:
            if( GetFocusColumn() > 0 )
                MoveCursor( GetFocusColumn() - 1 );
        break;
        case MOVE_NEXT:
            if( GetFocusColumn() < GetColumnCount() - 1 )
                MoveCursor( GetFocusColumn() + 1 );
        break;
        default:
        {
            // added to avoid warnings
        }
    }
}

void ScCsvGrid::ImplClearSelection()
{
    ::std::for_each( maColStates.begin(), maColStates.end(), Func_Select( false ) );
    ImplDrawGridDev();
}

void ScCsvGrid::DoSelectAction( sal_uInt32 nColIndex, sal_uInt16 nModifier )
{
    if( !(nModifier & KEY_MOD1) )
        ImplClearSelection();
    if( nModifier & KEY_SHIFT )             // SHIFT always expands
        SelectRange( mnRecentSelCol, nColIndex );
    else if( !(nModifier & KEY_MOD1) )      // no SHIFT/CTRL always selects 1 column
        Select( nColIndex );
    else if( mbTracking )                 // CTRL in tracking does not toggle
        Select( nColIndex, mbMTSelecting );
    else                                    // CTRL only toggles
        ToggleSelect( nColIndex );
    Execute( CSVCMD_MOVEGRIDCURSOR, GetColumnPos( nColIndex ) );
}

// cell contents --------------------------------------------------------------

void ScCsvGrid::ImplSetTextLineSep(
        sal_Int32 nLine, const OUString& rTextLine,
        const OUString& rSepChars, sal_Unicode cTextSep, bool bMergeSep, bool bRemoveSpace )
{
    if( nLine < GetFirstVisLine() ) return;

    sal_uInt32 nLineIx = nLine - GetFirstVisLine();
    while( maTexts.size() <= nLineIx )
        maTexts.emplace_back( );
    std::vector<OUString>& rStrVec = maTexts[ nLineIx ];
    rStrVec.clear();

    // scan for separators
    OUString aCellText;
    const sal_Unicode* pSepChars = rSepChars.getStr();
    const sal_Unicode* pChar = rTextLine.getStr();
    sal_uInt32 nColIx = 0;

    while( *pChar && (nColIx < sal::static_int_cast<sal_uInt32>(CSV_MAXCOLCOUNT)) )
    {
        // scan for next cell text
        bool bIsQuoted = false;
        bool bOverflowCell = false;
        pChar = ScImportExport::ScanNextFieldFromString( pChar, aCellText,
                cTextSep, pSepChars, bMergeSep, bIsQuoted, bOverflowCell, bRemoveSpace );
        /* TODO: signal overflow somewhere in UI */

        // update column width
        sal_Int32 nWidth = std::max( CSV_MINCOLWIDTH, ScImportExport::CountVisualWidth( aCellText ) + 1 );
        if( IsValidColumn( nColIx ) )
        {
            // expand existing column
            sal_Int32 nDiff = nWidth - GetColumnWidth( nColIx );
            if( nDiff > 0 )
            {
                Execute( CSVCMD_SETPOSCOUNT, GetPosCount() + nDiff );
                for( sal_uInt32 nSplitIx = GetColumnCount() - 1; nSplitIx > nColIx; --nSplitIx )
                {
                    sal_Int32 nPos = maSplits[ nSplitIx ];
                    maSplits.Remove( nPos );
                    maSplits.Insert( nPos + nDiff );
                }
            }
        }
        else
        {
            // append new column
            sal_Int32 nLastPos = GetPosCount();
            Execute( CSVCMD_SETPOSCOUNT, nLastPos + nWidth );
            ImplInsertSplit( nLastPos );
        }

        if( aCellText.getLength() <= CSV_MAXSTRLEN )
            rStrVec.push_back( aCellText );
        else
            rStrVec.push_back( aCellText.copy( 0, CSV_MAXSTRLEN ) );
        ++nColIx;
    }
    InvalidateGfx();
}

void ScCsvGrid::ImplSetTextLineFix( sal_Int32 nLine, std::u16string_view rTextLine )
{
    if( nLine < GetFirstVisLine() ) return;

    sal_Int32 nWidth = ScImportExport::CountVisualWidth( rTextLine );
    if( nWidth > GetPosCount() )
        Execute( CSVCMD_SETPOSCOUNT, nWidth );

    sal_uInt32 nLineIx = nLine - GetFirstVisLine();
    while( maTexts.size() <= nLineIx )
        maTexts.emplace_back( );

    std::vector<OUString>& rStrVec = maTexts[ nLineIx ];
    rStrVec.clear();
    sal_uInt32 nColCount = GetColumnCount();
    sal_Int32 nStrLen = rTextLine.size();
    sal_Int32 nStrIx = 0;
    for( sal_uInt32 nColIx = 0; (nColIx < nColCount) && (nStrIx < nStrLen); ++nColIx )
    {
        sal_Int32 nColWidth = GetColumnWidth( nColIx );
        sal_Int32 nLastIx = nStrIx;
        ScImportExport::CountVisualWidth( rTextLine, nLastIx, nColWidth );
        sal_Int32 nLen = std::min( CSV_MAXSTRLEN, nLastIx - nStrIx );
        rStrVec.push_back( OUString(rTextLine.substr( nStrIx, nLen )) );
        nStrIx = nStrIx + nLen;
    }
    InvalidateGfx();
}

OUString ScCsvGrid::GetCellText( sal_uInt32 nColIndex, sal_Int32 nLine ) const
{
    if( nLine < GetFirstVisLine() ) return OUString();

    sal_uInt32 nLineIx = nLine - GetFirstVisLine();
    if( nLineIx >= maTexts.size() ) return OUString();

    const std::vector<OUString>& rStrVec = maTexts[ nLineIx ];
    if( nColIndex >= rStrVec.size() ) return OUString();

    return rStrVec[ nColIndex ];
}

// event handling -------------------------------------------------------------

void ScCsvGrid::Resize()
{
    mpTableBox->InitControls();

    ScCsvControl::Resize();
    InitSizeData();
    Execute( CSVCMD_UPDATECELLTEXTS );
}

void ScCsvGrid::GetFocus()
{
    ScCsvControl::GetFocus();
    Execute( CSVCMD_MOVEGRIDCURSOR, GetNoScrollCol( GetGridCursorPos() ) );
    Repaint();
}

void ScCsvGrid::LoseFocus()
{
    ScCsvControl::LoseFocus();
    Repaint();
}

bool ScCsvGrid::MouseButtonDown( const MouseEvent& rMEvt )
{
    DisableRepaint();
    if( !HasFocus() )
        GrabFocus();

    Point aPos( rMEvt.GetPosPixel() );
    sal_uInt32 nColIx = GetColumnFromX( aPos.X() );

    if( rMEvt.IsLeft() )
    {
        if( (GetFirstX() > aPos.X()) || (aPos.X() > GetLastX()) )   // in header column
        {
            if( aPos.Y() <= GetHdrHeight() )
                SelectAll();
        }
        else if( IsValidColumn( nColIx ) )
        {
            DoSelectAction( nColIx, rMEvt.GetModifier() );
            mnMTCurrCol = nColIx;
            mbMTSelecting = IsSelected( nColIx );
            mbTracking = true;
        }
    }
    EnableRepaint();
    return true;
}

bool ScCsvGrid::MouseButtonUp( const MouseEvent& )
{
    mbTracking = false;
    return true;
}

bool ScCsvGrid::MouseMove( const MouseEvent& rMEvt )
{
    if (!mbTracking)
        return true;

    DisableRepaint();

    sal_Int32 nPos = (rMEvt.GetPosPixel().X() - GetFirstX()) / GetCharWidth() + GetFirstVisPos();
    // on mouse tracking: keep position valid
    nPos = std::clamp( nPos, sal_Int32(0), GetPosCount() - 1 );
    Execute( CSVCMD_MAKEPOSVISIBLE, nPos );

    sal_uInt32 nColIx = GetColumnFromPos( nPos );
    if( mnMTCurrCol != nColIx )
    {
        DoSelectAction( nColIx, rMEvt.GetModifier() );
        mnMTCurrCol = nColIx;
    }
    EnableRepaint();

    return true;
}

bool ScCsvGrid::KeyInput( const KeyEvent& rKEvt )
{
    const vcl::KeyCode& rKCode = rKEvt.GetKeyCode();
    sal_uInt16 nCode = rKCode.GetCode();
    bool bShift = rKCode.IsShift();
    bool bMod1 = rKCode.IsMod1();

    if( !rKCode.IsMod2() )
    {
        ScMoveMode eHDir = GetHorzDirection( nCode, !bMod1 );
        ScMoveMode eVDir = GetVertDirection( nCode, bMod1 );

        if( eHDir != MOVE_NONE )
        {
            DisableRepaint();
            MoveCursorRel( eHDir );
            if( !bMod1 )
                ImplClearSelection();
            if( bShift )
                SelectRange( mnRecentSelCol, GetFocusColumn() );
            else if( !bMod1 )
                Select( GetFocusColumn() );
            EnableRepaint();
        }
        else if( eVDir != MOVE_NONE )
            ScrollVertRel( eVDir );
        else if( nCode == KEY_SPACE )
        {
            if( !bMod1 )
                ImplClearSelection();
            if( bShift )
                SelectRange( mnRecentSelCol, GetFocusColumn() );
            else if( bMod1 )
                ToggleSelect( GetFocusColumn() );
            else
                Select( GetFocusColumn() );
        }
        else if( !bShift && bMod1 )
        {
            if( nCode == KEY_A )
                SelectAll();
            else if( (KEY_1 <= nCode) && (nCode <= KEY_9) )
            {
                sal_uInt32 nType = nCode - KEY_1;
                if( nType < maTypeNames.size() )
                    Execute( CSVCMD_SETCOLUMNTYPE, nType );
            }
        }
    }

    return rKCode.GetGroup() == KEYGROUP_CURSOR;
}

bool ScCsvGrid::Command( const CommandEvent& rCEvt )
{
    bool bConsumed = true;
    switch( rCEvt.GetCommand() )
    {
        case CommandEventId::ContextMenu:
        {
            if( rCEvt.IsMouseEvent() )
            {
                Point aPos( rCEvt.GetMousePosPixel() );
                sal_uInt32 nColIx = GetColumnFromX( aPos.X() );
                if( IsValidColumn( nColIx ) && (GetFirstX() <= aPos.X()) && (aPos.X() <= GetLastX()) )
                {
                    if( !IsSelected( nColIx ) )
                        DoSelectAction( nColIx, 0 );    // focus & select
                    ExecutePopup( aPos );
                }
            }
            else
            {
                sal_uInt32 nColIx = GetFocusColumn();
                if( !IsSelected( nColIx ) )
                    Select( nColIx );
                sal_Int32 nX1 = std::max( GetColumnX( nColIx ), GetFirstX() );
                sal_Int32 nX2 = std::min( GetColumnX( nColIx + 1 ), GetWidth() );
                ExecutePopup( Point( (nX1 + nX2) / 2, GetHeight() / 2 ) );
            }
            break;
        }
        case CommandEventId::Wheel:
        {
            tools::Rectangle aRect( Point(), maWinSize );
            if( aRect.Contains( rCEvt.GetMousePosPixel() ) )
            {
                const CommandWheelData* pData = rCEvt.GetWheelData();
                if( pData && (pData->GetMode() == CommandWheelMode::SCROLL) && !pData->IsHorz() )
                    Execute( CSVCMD_SETLINEOFFSET, GetFirstVisLine() - pData->GetNotchDelta() );
            }
            break;
        }
        default:
            bConsumed = false;
            break;
    }
    return bConsumed;
}

void ScCsvGrid::StyleUpdated()
{
    InitColors();
    InitFonts();
    UpdateLayoutData();
    Execute( CSVCMD_UPDATECELLTEXTS );

    ScCsvControl::StyleUpdated();
}

void ScCsvGrid::ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints )
{
    InitColors();
    Repaint();
}

// painting -------------------------------------------------------------------

void ScCsvGrid::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
{
    ImplRedraw(rRenderContext);
}

void ScCsvGrid::ImplRedraw(vcl::RenderContext& rRenderContext)
{
    if( IsVisible() )
    {
        if( !IsValidGfx() )
        {
            ValidateGfx();
            ImplDrawBackgrDev();
            ImplDrawGridDev();
        }
        rRenderContext.DrawOutDev( Point(), maWinSize, Point(), maWinSize, *mpGridDev );
    }
}

EditEngine* ScCsvGrid::GetEditEngine()
{
    return mpEditEngine.get();
}

void ScCsvGrid::ImplSetColumnClipRegion( OutputDevice& rOutDev, sal_uInt32 nColIndex )
{
    rOutDev.SetClipRegion( vcl::Region( tools::Rectangle(
        std::max( GetColumnX( nColIndex ), GetFirstX() ) + 1, 0,
        std::min( GetColumnX( nColIndex + 1 ), GetLastX() ), GetHeight() - 1 ) ) );
}

void ScCsvGrid::ImplDrawColumnHeader( OutputDevice& rOutDev, sal_uInt32 nColIndex, Color aFillColor )
{
    sal_Int32 nX1 = GetColumnX( nColIndex ) + 1;
    sal_Int32 nX2 = GetColumnX( nColIndex + 1 );
    sal_Int32 nHdrHt = GetHdrHeight();

    rOutDev.SetLineColor();
    rOutDev.SetFillColor( aFillColor );
    rOutDev.DrawRect( tools::Rectangle( nX1, 0, nX2, nHdrHt ) );

    rOutDev.SetFont( maHeaderFont );
    rOutDev.SetTextColor( maHeaderTextColor );
    rOutDev.SetTextFillColor();
    rOutDev.DrawText( Point( nX1 + 1, 0 ), GetColumnTypeName( nColIndex ) );

    rOutDev.SetLineColor( maHeaderGridColor );
    rOutDev.DrawLine( Point( nX1, nHdrHt ), Point( nX2, nHdrHt ) );
    rOutDev.DrawLine( Point( nX2, 0 ), Point( nX2, nHdrHt ) );
}

void ScCsvGrid::ImplDrawCellText( const Point& rPos, const OUString& rText )
{
    OUString aPlainText = rText.replaceAll( "\t"" " );
    aPlainText = aPlainText.replaceAll( "\n"" " );
    mpEditEngine->SetPaperSize( maEdEngSize );
    mpEditEngine->SetTextCurrentDefaults(aPlainText);
    mpEditEngine->Draw(*mpBackgrDev, rPos);

    sal_Int32 nCharIx = 0;
    while( (nCharIx = rText.indexOf( '\t', nCharIx )) != -1 )
    {
        sal_Int32 nX1 = rPos.X() + GetCharWidth() * nCharIx;
        sal_Int32 nX2 = nX1 + GetCharWidth() - 2;
        sal_Int32 nY = rPos.Y() + GetLineHeight() / 2;
        Color aColor( maTextColor );
        mpBackgrDev->SetLineColor( aColor );
        mpBackgrDev->DrawLine( Point( nX1, nY ), Point( nX2, nY ) );
        mpBackgrDev->DrawLine( Point( nX2 - 2, nY - 2 ), Point( nX2, nY ) );
        mpBackgrDev->DrawLine( Point( nX2 - 2, nY + 2 ), Point( nX2, nY ) );
        ++nCharIx;
    }
    nCharIx = 0;
    while( (nCharIx = rText.indexOf( '\n', nCharIx )) != -1 )
    {
        sal_Int32 nX1 = rPos.X() + GetCharWidth() * nCharIx;
        sal_Int32 nX2 = nX1 + GetCharWidth() - 2;
        sal_Int32 nY = rPos.Y() + GetLineHeight() / 2;
        Color aColor( maTextColor );
        mpBackgrDev->SetLineColor( aColor );
        mpBackgrDev->DrawLine( Point( nX1, nY ), Point( nX2, nY ) );
        mpBackgrDev->DrawLine( Point( nX1 + 2, nY - 2 ), Point( nX1, nY ) );
        mpBackgrDev->DrawLine( Point( nX1 + 2, nY + 2 ), Point( nX1, nY ) );
        mpBackgrDev->DrawLine( Point( nX2, nY - 2 ), Point( nX2, nY ) );
        ++nCharIx;
    }
}

void ScCsvGrid::ImplDrawFirstLineSep( bool bSet )
{
    if( IsVisibleLine( mnFirstImpLine ) && (mnFirstImpLine != GetFirstVisLine() ) )
    {
        sal_Int32 nY = GetY( mnFirstImpLine );
        sal_Int32 nX = std::min( GetColumnX( GetLastVisColumn() + 1 ), GetLastX() );
        mpBackgrDev->SetLineColor( bSet ? maGridPBColor : maGridColor );
        mpBackgrDev->DrawLine( Point( GetFirstX() + 1, nY ), Point( nX, nY ) );
    }
}

void ScCsvGrid::ImplDrawColumnBackgr( sal_uInt32 nColIndex )
{
    if( !IsVisibleColumn( nColIndex ) )
        return;

    ImplSetColumnClipRegion( *mpBackgrDev, nColIndex );

    // grid
    mpBackgrDev->SetLineColor();
    mpBackgrDev->SetFillColor( maBackColor );
    sal_Int32 nX1 = GetColumnX( nColIndex ) + 1;
    sal_Int32 nX2 = GetColumnX( nColIndex + 1 );
    sal_Int32 nY2 = GetY( GetLastVisLine() + 1 );
    sal_Int32 nHdrHt = GetHdrHeight();
    tools::Rectangle aRect( nX1, nHdrHt, nX2, nY2 );
    mpBackgrDev->DrawRect( aRect );
    mpBackgrDev->SetLineColor( maGridColor );
    mpBackgrDev->DrawGrid( aRect, Size( 1, GetLineHeight() ), DrawGridFlags::HorzLines );
    mpBackgrDev->DrawLine( Point( nX2, nHdrHt ), Point( nX2, nY2 ) );
    ImplDrawFirstLineSep( true );

    // cell texts
    mpEditEngine->SetDefaultItem( SvxColorItem( maTextColor, EE_CHAR_COLOR ) );
    size_t nLineCount = ::std::min( static_cast< size_t >( GetLastVisLine() - GetFirstVisLine() + 1 ), maTexts.size() );
    // #i67432# cut string to avoid edit engine performance problems with very large strings
    sal_Int32 nFirstVisPos = ::std::max( GetColumnPos( nColIndex ), GetFirstVisPos() );
    sal_Int32 nLastVisPos = ::std::min( GetColumnPos( nColIndex + 1 ), GetLastVisPos() );
    sal_Int32 nStrPos = nFirstVisPos - GetColumnPos( nColIndex );
    sal_Int32 nStrLen = nLastVisPos - nFirstVisPos + 1;
    sal_Int32 nStrX = GetX( nFirstVisPos );
    for( size_t nLine = 0; nLine < nLineCount; ++nLine )
    {
        std::vector<OUString>& rStrVec = maTexts[ nLine ];
        if( (nColIndex < rStrVec.size()) && (rStrVec[ nColIndex ].getLength() > nStrPos) )
        {
            const OUString& rStr = rStrVec[ nColIndex ];
            OUString aText = rStr.copy( nStrPos, ::std::min( nStrLen, rStr.getLength() - nStrPos) );
            ImplDrawCellText( Point( nStrX, GetY( GetFirstVisLine() + nLine ) ), aText );
        }
    }

    // header
    ImplDrawColumnHeader( *mpBackgrDev, nColIndex, maHeaderBackColor );

    mpBackgrDev->SetClipRegion();
}

void ScCsvGrid::ImplDrawRowHeaders()
{
    mpBackgrDev->SetLineColor();
    mpBackgrDev->SetFillColor( maAppBackColor );
    Point aPoint( GetHdrX(), 0 );
    tools::Rectangle aRect( aPoint, Size( GetHdrWidth() + 1, GetHeight() ) );
    mpBackgrDev->DrawRect( aRect );

    mpBackgrDev->SetFillColor( maHeaderBackColor );
    aRect.SetBottom( GetY( GetLastVisLine() + 1 ) );
    mpBackgrDev->DrawRect( aRect );

    // line numbers
    mpBackgrDev->SetFont( maHeaderFont );
    mpBackgrDev->SetTextColor( maHeaderTextColor );
    mpBackgrDev->SetTextFillColor();
    sal_Int32 nLastLine = GetLastVisLine();
    for( sal_Int32 nLine = GetFirstVisLine(); nLine <= nLastLine; ++nLine )
    {
        OUString aText( OUString::number( nLine + 1 ) );
        sal_Int32 nX = GetHdrX() + (GetHdrWidth() - mpBackgrDev->GetTextWidth( aText )) / 2;
        mpBackgrDev->DrawText( Point( nX, GetY( nLine ) ), aText );
    }

    // grid
    mpBackgrDev->SetLineColor( maHeaderGridColor );
    if( IsRTL() )
    {
        mpBackgrDev->DrawLine( Point( 0, 0 ), Point( 0, GetHeight() - 1 ) );
        mpBackgrDev->DrawLine( aRect.TopLeft(), aRect.BottomLeft() );
    }
    else
        mpBackgrDev->DrawLine( aRect.TopRight(), aRect.BottomRight() );
    aRect.SetTop( GetHdrHeight() );
    mpBackgrDev->DrawGrid( aRect, Size( 1, GetLineHeight() ), DrawGridFlags::HorzLines );
}

void ScCsvGrid::ImplDrawBackgrDev()
{
    mpBackgrDev->SetLineColor();
    mpBackgrDev->SetFillColor( maAppBackColor );
    mpBackgrDev->DrawRect( tools::Rectangle(
        Point( GetFirstX() + 1, 0 ), Size( GetWidth() - GetHdrWidth(), GetHeight() ) ) );

    sal_uInt32 nLastCol = GetLastVisColumn();
    if (nLastCol == CSV_COLUMN_INVALID)
        return;
    for( sal_uInt32 nColIx = GetFirstVisColumn(); nColIx <= nLastCol; ++nColIx )
        ImplDrawColumnBackgr( nColIx );

    ImplDrawRowHeaders();
}

void ScCsvGrid::ImplDrawColumnSelection( sal_uInt32 nColIndex )
{
    ImplInvertCursor( GetRulerCursorPos() );
    ImplSetColumnClipRegion( *mpGridDev, nColIndex );
    mpGridDev->DrawOutDev( Point(), maWinSize, Point(), maWinSize, *mpBackgrDev );

    if( IsSelected( nColIndex ) )
    {
        sal_Int32 nX1 = GetColumnX( nColIndex ) + 1;
        sal_Int32 nX2 = GetColumnX( nColIndex + 1 );

        // header
        tools::Rectangle aRect( nX1, 0, nX2, GetHdrHeight() );
        mpGridDev->SetLineColor();
        if( maHeaderBackColor.IsDark() )
            // redraw with light gray background in dark mode
            ImplDrawColumnHeader( *mpGridDev, nColIndex, COL_LIGHTGRAY );
        else
        {
            // use transparent active color
            mpGridDev->SetFillColor( maSelectColor );
            mpGridDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aRect ) ), CSV_HDR_TRANSPARENCY );
        }

        // column selection
        aRect = tools::Rectangle( nX1, GetHdrHeight() + 1, nX2, GetY( GetLastVisLine() + 1 ) - 1 );
        ImplInvertRect( *mpGridDev, aRect );
    }

    mpGridDev->SetClipRegion();
    ImplInvertCursor( GetRulerCursorPos() );
}

void ScCsvGrid::ImplDrawGridDev()
{
    mpGridDev->DrawOutDev( Point(), maWinSize, Point(), maWinSize, *mpBackgrDev );
    sal_uInt32 nLastCol = GetLastVisColumn();
    if (nLastCol == CSV_COLUMN_INVALID)
        return;
    for( sal_uInt32 nColIx = GetFirstVisColumn(); nColIx <= nLastCol; ++nColIx )
        ImplDrawColumnSelection( nColIx );
}

void ScCsvGrid::ImplDrawColumn( sal_uInt32 nColIndex )
{
    ImplDrawColumnBackgr( nColIndex );
    ImplDrawColumnSelection( nColIndex );
}

void ScCsvGrid::ImplDrawHorzScrolled( sal_Int32 nOldPos )
{
    sal_Int32 nPos = GetFirstVisPos();
    if( !IsValidGfx() || (nPos == nOldPos) )
        return;
    if( std::abs( nPos - nOldPos ) > GetVisPosCount() / 2 )
    {
        ImplDrawBackgrDev();
        ImplDrawGridDev();
        return;
    }

    Point aSrc, aDest;
    sal_uInt32 nFirstColIx, nLastColIx;
    if( nPos < nOldPos )
    {
        aSrc = Point( GetFirstX() + 1, 0 );
        aDest = Point( GetFirstX() + GetCharWidth() * (nOldPos - nPos) + 1, 0 );
        nFirstColIx = GetColumnFromPos( nPos );
        nLastColIx = GetColumnFromPos( nOldPos );
    }
    else
    {
        aSrc = Point( GetFirstX() + GetCharWidth() * (nPos - nOldPos) + 1, 0 );
        aDest = Point( GetFirstX() + 1, 0 );
        nFirstColIx = GetColumnFromPos( std::min( nOldPos + GetVisPosCount(), GetPosCount() ) - 1 );
        nLastColIx = GetColumnFromPos( std::min( nPos + GetVisPosCount(), GetPosCount() ) - 1 );
    }

    ImplInvertCursor( GetRulerCursorPos() + (nPos - nOldPos) );
    tools::Rectangle aRectangle( GetFirstX(), 0, GetLastX(), GetHeight() - 1 );
    vcl::Region aClipReg( aRectangle );
    mpBackgrDev->SetClipRegion( aClipReg );
    mpBackgrDev->CopyArea( aDest, aSrc, maWinSize );
    mpBackgrDev->SetClipRegion();
    mpGridDev->SetClipRegion( aClipReg );
    mpGridDev->CopyArea( aDest, aSrc, maWinSize );
    mpGridDev->SetClipRegion();
    ImplInvertCursor( GetRulerCursorPos() );

    for( sal_uInt32 nColIx = nFirstColIx; nColIx <= nLastColIx; ++nColIx )
        ImplDrawColumn( nColIx );

    sal_Int32 nLastX = GetX( GetPosCount() ) + 1;
    if( nLastX <= GetLastX() )
    {
        tools::Rectangle aRect( nLastX, 0, GetLastX(), GetHeight() - 1 );
        mpBackgrDev->SetLineColor();
        mpBackgrDev->SetFillColor( maAppBackColor );
        mpBackgrDev->DrawRect( aRect );
        mpGridDev->SetLineColor();
        mpGridDev->SetFillColor( maAppBackColor );
        mpGridDev->DrawRect( aRect );
    }
}

void ScCsvGrid::ImplInvertCursor( sal_Int32 nPos )
{
    if( IsVisibleSplitPos( nPos ) )
    {
        sal_Int32 nX = GetX( nPos ) - 1;
        tools::Rectangle aRect( Point( nX, 0 ), Size( 3, GetHdrHeight() ) );
        ImplInvertRect( *mpGridDev, aRect );
        aRect.SetTop( GetHdrHeight() + 1 );
        aRect.SetBottom( GetY( GetLastVisLine() + 1 ) );
        ImplInvertRect( *mpGridDev, aRect );
    }
}

tools::Rectangle ScCsvGrid::GetFocusRect()
{
    auto nColIndex = GetFocusColumn();
    if( HasFocus() && IsVisibleColumn( nColIndex ) )
    {
        sal_Int32 nX1 = std::max( GetColumnX( nColIndex ), GetFirstX() ) + 1;
        sal_Int32 nX2 = std::min( GetColumnX( nColIndex + 1 ) - sal_Int32( 1 ), GetLastX() );
        sal_Int32 nY2 = std::min( GetY( GetLastVisLine() + 1 ), GetHeight() ) - 1;
        return tools::Rectangle( nX1, 0, nX2, nY2 );
    }
    return weld::CustomWidgetController::GetFocusRect();
}

// accessibility ==============================================================

css::uno::Reference<css::accessibility::XAccessible> ScCsvGrid::CreateAccessible()
{
    rtl::Reference<ScAccessibleCsvGrid> xRef(new ScAccessibleCsvGrid(*this));
    mxAccessible = xRef;
    return xRef;
}

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

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

¤ Dauer der Verarbeitung: 0.12 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.