1416 lines
46 KiB
C++
1416 lines
46 KiB
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
|
|
auto pDefSet = std::make_unique<SfxItemSet>(mpEditEngine->GetEmptyItemSet());
|
|
EditEngine::SetFontInfoInItemSet(*pDefSet, maMonoFont);
|
|
pDefSet->Put(aAsianItem);
|
|
pDefSet->Put(aComplexItem);
|
|
|
|
// set Asian/Complex font size to height of character in Latin font
|
|
sal_uLong nFontHt = static_cast< sal_uLong >( maMonoFont.GetFontSize().Height() );
|
|
pDefSet->Put(SvxFontHeightItem(nFontHt, 100, EE_CHAR_FONTHEIGHT_CJK));
|
|
pDefSet->Put(SvxFontHeightItem(nFontHt, 100, EE_CHAR_FONTHEIGHT_CTL));
|
|
|
|
// copy other items from default font
|
|
const SfxPoolItem& rWeightItem = pDefSet->Get(EE_CHAR_WEIGHT);
|
|
std::unique_ptr<SfxPoolItem> pNewItem(rWeightItem.Clone());
|
|
pNewItem->SetWhich(EE_CHAR_WEIGHT_CJK);
|
|
pDefSet->Put(*pNewItem);
|
|
pNewItem->SetWhich(EE_CHAR_WEIGHT_CTL);
|
|
pDefSet->Put(*pNewItem);
|
|
const SfxPoolItem& rItalicItem = pDefSet->Get(EE_CHAR_ITALIC);
|
|
pNewItem.reset(rItalicItem.Clone());
|
|
pNewItem->SetWhich(EE_CHAR_ITALIC_CJK);
|
|
pDefSet->Put(*pNewItem);
|
|
pNewItem->SetWhich(EE_CHAR_ITALIC_CTL);
|
|
pDefSet->Put(*pNewItem);
|
|
const SfxPoolItem& rLangItem = pDefSet->Get(EE_CHAR_LANGUAGE);
|
|
pNewItem.reset(rLangItem.Clone());
|
|
pNewItem->SetWhich(EE_CHAR_LANGUAGE_CJK);
|
|
pDefSet->Put(*pNewItem);
|
|
pNewItem->SetWhich(EE_CHAR_LANGUAGE_CTL);
|
|
pDefSet->Put(*pNewItem);
|
|
|
|
mpEditEngine->SetDefaults(std::move(pDefSet));
|
|
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: */
|