2867 lines
108 KiB
C++
2867 lines
108 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 <scitems.hxx>
|
|
#include <editeng/brushitem.hxx>
|
|
#include <svtools/colorcfg.hxx>
|
|
#include <svx/rotmodit.hxx>
|
|
#include <svx/svdocapt.hxx>
|
|
#include <editeng/shaditem.hxx>
|
|
#include <editeng/svxfont.hxx>
|
|
#include <tools/poly.hxx>
|
|
#include <vcl/svapp.hxx>
|
|
#include <vcl/pdfextoutdevdata.hxx>
|
|
#include <svx/framelinkarray.hxx>
|
|
#include <drawinglayer/geometry/viewinformation2d.hxx>
|
|
#include <drawinglayer/processor2d/baseprocessor2d.hxx>
|
|
#include <drawinglayer/processor2d/processor2dtools.hxx>
|
|
#include <officecfg/Office/Common.hxx>
|
|
#include <officecfg/Office/Calc.hxx>
|
|
#include <vcl/lineinfo.hxx>
|
|
#include <vcl/gradient.hxx>
|
|
#include <vcl/settings.hxx>
|
|
#include <vcl/pdf/PDFNote.hxx>
|
|
#include <svx/unoapi.hxx>
|
|
#include <sal/log.hxx>
|
|
#include <comphelper/lok.hxx>
|
|
#include <o3tl/unit_conversion.hxx>
|
|
#include <basegfx/matrix/b2dhommatrix.hxx>
|
|
|
|
#include <output.hxx>
|
|
#include <document.hxx>
|
|
#include <drwlayer.hxx>
|
|
#include <formulacell.hxx>
|
|
#include <attrib.hxx>
|
|
#include <patattr.hxx>
|
|
#include <progress.hxx>
|
|
#include <pagedata.hxx>
|
|
#include <chgtrack.hxx>
|
|
#include <chgviset.hxx>
|
|
#include <viewutil.hxx>
|
|
#include <gridmerg.hxx>
|
|
#include <fillinfo.hxx>
|
|
#include <scmod.hxx>
|
|
#include <appoptio.hxx>
|
|
#include <postit.hxx>
|
|
#include <validat.hxx>
|
|
#include <detfunc.hxx>
|
|
#include <editutil.hxx>
|
|
|
|
#include <SparklineRenderer.hxx>
|
|
#include <colorscale.hxx>
|
|
|
|
#include <math.h>
|
|
#include <memory>
|
|
|
|
using namespace com::sun::star;
|
|
|
|
// Static Data
|
|
|
|
// color for ChangeTracking "by author" as in the writer (swmodul1.cxx)
|
|
|
|
#define SC_AUTHORCOLORCOUNT 9
|
|
|
|
const Color nAuthorColor[ SC_AUTHORCOLORCOUNT ] = {
|
|
COL_LIGHTRED, COL_LIGHTBLUE, COL_LIGHTMAGENTA,
|
|
COL_GREEN, COL_RED, COL_BLUE,
|
|
COL_BROWN, COL_MAGENTA, COL_CYAN };
|
|
|
|
// Helper class for color assignment to avoid repeated lookups for the same user
|
|
|
|
ScActionColorChanger::ScActionColorChanger( const ScChangeTrack& rTrack ) :
|
|
rOpt( ScModule::get()->GetAppOptions() ),
|
|
rUsers( rTrack.GetUserCollection() ),
|
|
nLastUserIndex( 0 ),
|
|
nColor( COL_BLACK )
|
|
{
|
|
}
|
|
|
|
void ScActionColorChanger::Update( const ScChangeAction& rAction )
|
|
{
|
|
Color nSetColor;
|
|
switch (rAction.GetType())
|
|
{
|
|
case SC_CAT_INSERT_COLS:
|
|
case SC_CAT_INSERT_ROWS:
|
|
case SC_CAT_INSERT_TABS:
|
|
nSetColor = rOpt.GetTrackInsertColor();
|
|
break;
|
|
case SC_CAT_DELETE_COLS:
|
|
case SC_CAT_DELETE_ROWS:
|
|
case SC_CAT_DELETE_TABS:
|
|
nSetColor = rOpt.GetTrackDeleteColor();
|
|
break;
|
|
case SC_CAT_MOVE:
|
|
nSetColor = rOpt.GetTrackMoveColor();
|
|
break;
|
|
default:
|
|
nSetColor = rOpt.GetTrackContentColor();
|
|
break;
|
|
}
|
|
if ( nSetColor != COL_TRANSPARENT ) // color assigned
|
|
nColor = nSetColor;
|
|
else // by author
|
|
{
|
|
if (aLastUserName != rAction.GetUser())
|
|
{
|
|
aLastUserName = rAction.GetUser();
|
|
std::set<OUString>::const_iterator it = rUsers.find(aLastUserName);
|
|
if (it == rUsers.end())
|
|
{
|
|
// empty string is possible if a name wasn't found while saving a 5.0 file
|
|
SAL_INFO_IF( aLastUserName.isEmpty(), "sc.ui", "Author not found" );
|
|
nLastUserIndex = 0;
|
|
}
|
|
else
|
|
{
|
|
size_t nPos = std::distance(rUsers.begin(), it);
|
|
nLastUserIndex = nPos % SC_AUTHORCOLORCOUNT;
|
|
}
|
|
}
|
|
nColor = nAuthorColor[nLastUserIndex];
|
|
}
|
|
}
|
|
|
|
ScOutputData::ScOutputData( OutputDevice* pNewDev, ScOutputType eNewType,
|
|
ScTableInfo& rTabInfo, ScDocument* pNewDoc,
|
|
SCTAB nNewTab, tools::Long nNewScrX, tools::Long nNewScrY,
|
|
SCCOL nNewX1, SCROW nNewY1, SCCOL nNewX2, SCROW nNewY2,
|
|
double nPixelPerTwipsX, double nPixelPerTwipsY,
|
|
const Fraction* pZoomX, const Fraction* pZoomY ) :
|
|
mpOriginalTargetDevice( pNewDev ),
|
|
mpDev( pNewDev ),
|
|
mpRefDevice( pNewDev ), // default is output device
|
|
pFmtDevice( pNewDev ), // default is output device
|
|
mrTabInfo( rTabInfo ),
|
|
pRowInfo( rTabInfo.mpRowInfo.get() ),
|
|
nArrCount( rTabInfo.mnArrCount ),
|
|
mpDoc( pNewDoc ),
|
|
nTab( nNewTab ),
|
|
nScrX( nNewScrX ),
|
|
nScrY( nNewScrY ),
|
|
nX1( nNewX1 ),
|
|
nY1( nNewY1 ),
|
|
nX2( nNewX2 ),
|
|
nY2( nNewY2 ),
|
|
eType( eNewType ),
|
|
mnPPTX( nPixelPerTwipsX ),
|
|
mnPPTY( nPixelPerTwipsY ),
|
|
pViewShell( nullptr ),
|
|
pDrawView( nullptr ),
|
|
bEditMode( false ),
|
|
nEditCol( 0 ),
|
|
nEditRow( 0 ),
|
|
bMetaFile( false ),
|
|
bPagebreakMode( false ),
|
|
bSolidBackground( false ),
|
|
mbUseStyleColor( false ),
|
|
mbForceAutoColor( officecfg::Office::Common::Accessibility::IsAutomaticFontColor::get() ),
|
|
mbSyntaxMode( false ),
|
|
aGridColor( COL_BLACK ),
|
|
mbShowNullValues( true ),
|
|
mbShowFormulas( false ),
|
|
bShowSpellErrors( false ),
|
|
bMarkClipped( false ), // sal_False for printer/metafile etc.
|
|
bSnapPixel( false ),
|
|
bAnyClipped( false ),
|
|
bVertical(false),
|
|
mpTargetPaintWindow(nullptr), // #i74769# use SdrPaintWindow direct
|
|
mpSpellCheckCxt(nullptr)
|
|
{
|
|
if (pZoomX)
|
|
aZoomX = *pZoomX;
|
|
else
|
|
aZoomX = Fraction(1,1);
|
|
if (pZoomY)
|
|
aZoomY = *pZoomY;
|
|
else
|
|
aZoomY = Fraction(1,1);
|
|
|
|
nVisX1 = nX1;
|
|
nVisY1 = nY1;
|
|
nVisX2 = nX2;
|
|
nVisY2 = nY2;
|
|
mpDoc->StripHidden( nVisX1, nVisY1, nVisX2, nVisY2, nTab );
|
|
|
|
nScrW = 0;
|
|
for (SCCOL nX=nVisX1; nX<=nVisX2; nX++)
|
|
nScrW += pRowInfo[0].basicCellInfo(nX).nWidth;
|
|
|
|
nMirrorW = nScrW;
|
|
|
|
nScrH = 0;
|
|
for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
|
|
nScrH += pRowInfo[nArrY].nHeight;
|
|
|
|
bTabProtected = mpDoc->IsTabProtected( nTab );
|
|
bLayoutRTL = mpDoc->IsLayoutRTL( nTab );
|
|
|
|
// always needed, so call at the end of the constructor
|
|
SetCellRotations();
|
|
InitOutputEditEngine();
|
|
}
|
|
|
|
ScOutputData::~ScOutputData()
|
|
{
|
|
}
|
|
|
|
void ScOutputData::SetSpellCheckContext( const sc::SpellCheckContext* pCxt )
|
|
{
|
|
mpSpellCheckCxt = pCxt;
|
|
}
|
|
|
|
void ScOutputData::SetContentDevice( OutputDevice* pContentDev )
|
|
{
|
|
// use pContentDev instead of pDev where used
|
|
|
|
if ( mpRefDevice == mpDev )
|
|
mpRefDevice = pContentDev;
|
|
if ( pFmtDevice == mpDev )
|
|
pFmtDevice = pContentDev;
|
|
mpDev = pContentDev;
|
|
}
|
|
|
|
void ScOutputData::SetMirrorWidth( tools::Long nNew )
|
|
{
|
|
nMirrorW = nNew;
|
|
}
|
|
|
|
void ScOutputData::SetGridColor( const Color& rColor )
|
|
{
|
|
aGridColor = rColor;
|
|
}
|
|
|
|
void ScOutputData::SetMarkClipped( bool bSet )
|
|
{
|
|
bMarkClipped = bSet;
|
|
}
|
|
|
|
void ScOutputData::SetShowNullValues( bool bSet )
|
|
{
|
|
mbShowNullValues = bSet;
|
|
}
|
|
|
|
void ScOutputData::SetShowFormulas( bool bSet )
|
|
{
|
|
mbShowFormulas = bSet;
|
|
}
|
|
|
|
void ScOutputData::SetShowSpellErrors( bool bSet )
|
|
{
|
|
bShowSpellErrors = bSet;
|
|
// reset EditEngine because it depends on bShowSpellErrors
|
|
mxOutputEditEngine.reset();
|
|
}
|
|
|
|
void ScOutputData::SetSnapPixel()
|
|
{
|
|
bSnapPixel = true;
|
|
}
|
|
|
|
void ScOutputData::SetEditCell( SCCOL nCol, SCROW nRow )
|
|
{
|
|
nEditCol = nCol;
|
|
nEditRow = nRow;
|
|
bEditMode = true;
|
|
}
|
|
|
|
void ScOutputData::SetMetaFileMode( bool bNewMode )
|
|
{
|
|
bMetaFile = bNewMode;
|
|
}
|
|
|
|
void ScOutputData::SetSyntaxMode( bool bNewMode )
|
|
{
|
|
mbSyntaxMode = bNewMode;
|
|
if ( bNewMode && !mxValueColor )
|
|
{
|
|
const svtools::ColorConfig& rColorCfg = ScModule::get()->GetColorConfig();
|
|
mxValueColor = rColorCfg.GetColorValue( svtools::CALCVALUE ).nColor;
|
|
mxTextColor = rColorCfg.GetColorValue( svtools::CALCTEXT ).nColor;
|
|
mxFormulaColor = rColorCfg.GetColorValue( svtools::CALCFORMULA ).nColor;
|
|
}
|
|
}
|
|
|
|
bool ScOutputData::ReopenPDFStructureElement(vcl::PDFWriter::StructElement aType, SCROW nRow, SCCOL nCol)
|
|
{
|
|
bool bReopenTag = false;
|
|
vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(mpDev->GetExtOutDevData());
|
|
if (pPDF)
|
|
{
|
|
if (aType == vcl::PDFWriter::Part) // Worksheet
|
|
{
|
|
if (pPDF->GetScPDFState()->m_WorksheetId != -1)
|
|
{
|
|
sal_Int32 nId = pPDF->GetScPDFState()->m_WorksheetId;
|
|
pPDF->BeginStructureElement(nId);
|
|
bReopenTag = true;
|
|
}
|
|
}
|
|
else if (aType == vcl::PDFWriter::Table)
|
|
{
|
|
if (pPDF->GetScPDFState()->m_TableId != -1)
|
|
{
|
|
sal_Int32 nId = pPDF->GetScPDFState()->m_TableId;
|
|
pPDF->BeginStructureElement(nId);
|
|
bReopenTag = true;
|
|
}
|
|
}
|
|
else if (aType == vcl::PDFWriter::TableRow)
|
|
{
|
|
const auto aIter = pPDF->GetScPDFState()->m_TableRowMap.find(nRow);
|
|
if (aIter != pPDF->GetScPDFState()->m_TableRowMap.end() && nRow == aIter->first)
|
|
{
|
|
sal_Int32 nId = (*aIter).second;
|
|
pPDF->BeginStructureElement(nId);
|
|
bReopenTag = true;
|
|
}
|
|
}
|
|
else if (aType == vcl::PDFWriter::TableData)
|
|
{
|
|
const std::pair<SCROW, SCCOL> keyToFind = std::make_pair(nRow, nCol);
|
|
const auto aIter = pPDF->GetScPDFState()->m_TableDataMap.find(keyToFind);
|
|
if (aIter != pPDF->GetScPDFState()->m_TableDataMap.end() && keyToFind == aIter->first)
|
|
{
|
|
sal_Int32 nId = (*aIter).second;
|
|
pPDF->BeginStructureElement(nId);
|
|
bReopenTag = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bReopenTag;
|
|
}
|
|
|
|
void ScOutputData::DrawGrid(vcl::RenderContext& rRenderContext, bool bGrid, bool bPage, bool bMergeCover)
|
|
{
|
|
// bMergeCover : Draw lines in sheet bgcolor to cover lok client grid lines in merged cell areas.
|
|
// When scNoGridBackground is set in lok mode, bMergeCover is set to true and bGrid to false.
|
|
|
|
SCCOL nX;
|
|
SCROW nY;
|
|
tools::Long nPosX;
|
|
tools::Long nPosY;
|
|
SCSIZE nArrY;
|
|
ScBreakType nBreak = ScBreakType::NONE;
|
|
ScBreakType nBreakOld = ScBreakType::NONE;
|
|
|
|
bool bDashed = false;
|
|
Color aPageColor;
|
|
Color aManualColor;
|
|
|
|
if (bPagebreakMode)
|
|
bPage = false; // no "normal" breaks over the whole width/height
|
|
|
|
// It is a big mess to distinguish when we are using pixels and when logic
|
|
// units for drawing. Ultimately we want to work only in the logic units,
|
|
// but until that happens, we need to special-case:
|
|
//
|
|
// * metafile
|
|
// * drawing to the screen - everything is internally counted in pixels there
|
|
//
|
|
// 'Internally' in the above means the pCellInfo[...].nWidth and
|
|
// pRowInfo[...]->nHeight:
|
|
//
|
|
// * when bWorksInPixels is true: these are in pixels
|
|
// * when bWorksInPixels is false: these are in the logic units
|
|
//
|
|
// This is where all the confusion comes from, ultimately we want them
|
|
// always in the logic units (100th of millimeters), but we need to get
|
|
// there gradually (get rid of setting MapUnit::MapPixel first), otherwise we'd
|
|
// break all the drawing by one change.
|
|
// So until that happens, we need to special case.
|
|
bool bWorksInPixels = bMetaFile;
|
|
const svtools::ColorConfig& rColorCfg = ScModule::get()->GetColorConfig();
|
|
Color aSheetBGColor = rColorCfg.GetColorValue(::svtools::DOCCOLOR).nColor;
|
|
|
|
if ( eType == OUTTYPE_WINDOW )
|
|
{
|
|
bWorksInPixels = true;
|
|
aPageColor = rColorCfg.GetColorValue(svtools::CALCPAGEBREAKAUTOMATIC).nColor;
|
|
aManualColor = rColorCfg.GetColorValue(svtools::CALCPAGEBREAKMANUAL).nColor;
|
|
}
|
|
else
|
|
{
|
|
aPageColor = aGridColor;
|
|
aManualColor = aGridColor;
|
|
}
|
|
|
|
tools::Long nOneX = 1;
|
|
tools::Long nOneY = 1;
|
|
if (!bWorksInPixels)
|
|
{
|
|
Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
|
|
nOneX = aOnePixel.Width();
|
|
nOneY = aOnePixel.Height();
|
|
}
|
|
|
|
tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
|
|
tools::Long nSignedOneX = nOneX * nLayoutSign;
|
|
|
|
rRenderContext.SetLineColor(bMergeCover ? aSheetBGColor : aGridColor);
|
|
|
|
ScGridMerger aGrid(&rRenderContext, nOneX, nOneY);
|
|
|
|
// vertical lines
|
|
|
|
nPosX = nScrX;
|
|
if ( bLayoutRTL )
|
|
nPosX += nMirrorW - nOneX;
|
|
|
|
for (nX=nX1; nX<=nX2; nX++)
|
|
{
|
|
sal_uInt16 nWidth = pRowInfo[0].basicCellInfo(nX).nWidth;
|
|
if (nWidth)
|
|
{
|
|
nPosX += nWidth * nLayoutSign;
|
|
|
|
if ( bPage )
|
|
{
|
|
// Search also in hidden part for page breaks
|
|
SCCOL nCol = nX + 1;
|
|
while (nCol <= mpDoc->MaxCol())
|
|
{
|
|
nBreak = mpDoc->HasColBreak(nCol, nTab);
|
|
bool bHidden = mpDoc->ColHidden(nCol, nTab);
|
|
|
|
if ( nBreak != ScBreakType::NONE || !bHidden )
|
|
break;
|
|
++nCol;
|
|
}
|
|
|
|
if (nBreak != nBreakOld)
|
|
{
|
|
aGrid.Flush();
|
|
|
|
if (static_cast<int>(nBreak))
|
|
{
|
|
rRenderContext.SetLineColor( (nBreak & ScBreakType::Manual) ? aManualColor :
|
|
aPageColor );
|
|
bDashed = true;
|
|
}
|
|
else
|
|
{
|
|
rRenderContext.SetLineColor(bMergeCover ? aSheetBGColor : aGridColor);
|
|
bDashed = false;
|
|
}
|
|
|
|
nBreakOld = nBreak;
|
|
}
|
|
}
|
|
|
|
bool bDraw = bGrid || nBreakOld != ScBreakType::NONE || bMergeCover; // simple grid only if set that way
|
|
|
|
sal_uInt16 nWidthXplus1 = pRowInfo[0].basicCellInfo(nX+1).nWidth;
|
|
bool bSingle = false; //! get into Fillinfo !!!!!
|
|
if ( nX<mpDoc->MaxCol() )
|
|
{
|
|
bSingle = ( nWidthXplus1 == 0 );
|
|
for (nArrY=1; nArrY+1<nArrCount && !bSingle; nArrY++)
|
|
{
|
|
if (pRowInfo[nArrY].cellInfo(nX+1).bHOverlapped)
|
|
bSingle = true;
|
|
if (pRowInfo[nArrY].cellInfo(nX).bHideGrid)
|
|
bSingle = true;
|
|
}
|
|
}
|
|
|
|
if (bDraw)
|
|
{
|
|
if ( nX<mpDoc->MaxCol() && bSingle )
|
|
{
|
|
SCCOL nVisX = nX + 1;
|
|
while ( nVisX < mpDoc->MaxCol() && !mpDoc->GetColWidth(nVisX,nTab) )
|
|
++nVisX;
|
|
|
|
nPosY = nScrY;
|
|
for (nArrY=1; nArrY+1<nArrCount; nArrY++)
|
|
{
|
|
RowInfo* pThisRowInfo = &pRowInfo[nArrY];
|
|
const tools::Long nNextY = nPosY + pThisRowInfo->nHeight;
|
|
|
|
bool bHOver = pThisRowInfo->cellInfo(nX).bHideGrid;
|
|
if (!bHOver)
|
|
{
|
|
if (nWidthXplus1)
|
|
bHOver = pThisRowInfo->cellInfo(nX+1).bHOverlapped;
|
|
else
|
|
{
|
|
if (nVisX <= nX2)
|
|
bHOver = pThisRowInfo->cellInfo(nVisX).bHOverlapped;
|
|
else
|
|
bHOver = mpDoc->GetAttr(
|
|
nVisX,pThisRowInfo->nRowNo,nTab,ATTR_MERGE_FLAG)
|
|
->IsHorOverlapped();
|
|
if (bHOver)
|
|
bHOver = mpDoc->GetAttr(
|
|
nX + 1,pThisRowInfo->nRowNo,nTab,ATTR_MERGE_FLAG)
|
|
->IsHorOverlapped();
|
|
}
|
|
}
|
|
|
|
if ((pThisRowInfo->bChanged && !bHOver && !bMergeCover) || (bHOver && bMergeCover))
|
|
{
|
|
aGrid.AddVerLine(bWorksInPixels, nPosX-nSignedOneX, nPosY, nNextY-nOneY, bDashed);
|
|
}
|
|
nPosY = nNextY;
|
|
}
|
|
}
|
|
else if (!bMergeCover)
|
|
{
|
|
aGrid.AddVerLine(bWorksInPixels, nPosX-nSignedOneX, nScrY, nScrY+nScrH-nOneY, bDashed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// horizontal lines
|
|
|
|
bool bHiddenRow = true;
|
|
SCROW nHiddenEndRow = -1;
|
|
nPosY = nScrY;
|
|
for (nArrY=1; nArrY+1<nArrCount; nArrY++)
|
|
{
|
|
SCSIZE nArrYplus1 = nArrY+1;
|
|
nY = pRowInfo[nArrY].nRowNo;
|
|
SCROW nYplus1 = nY+1;
|
|
nPosY += pRowInfo[nArrY].nHeight;
|
|
|
|
if (pRowInfo[nArrY].bChanged)
|
|
{
|
|
if ( bPage )
|
|
{
|
|
for (SCROW i = nYplus1; i <= mpDoc->MaxRow(); ++i)
|
|
{
|
|
if (i > nHiddenEndRow)
|
|
bHiddenRow = mpDoc->RowHidden(i, nTab, nullptr, &nHiddenEndRow);
|
|
/* TODO: optimize the row break thing for large hidden
|
|
* segments where HasRowBreak() has to be called
|
|
* nevertheless for each row, as a row break is drawn also
|
|
* for hidden rows, above them. This needed to be done only
|
|
* once per hidden segment, maybe giving manual breaks
|
|
* priority. Something like GetNextRowBreak() and
|
|
* GetNextManualRowBreak(). */
|
|
nBreak = mpDoc->HasRowBreak(i, nTab);
|
|
if (!bHiddenRow || nBreak != ScBreakType::NONE)
|
|
break;
|
|
}
|
|
|
|
if (nBreakOld != nBreak)
|
|
{
|
|
aGrid.Flush();
|
|
|
|
if (static_cast<int>(nBreak))
|
|
{
|
|
rRenderContext.SetLineColor( (nBreak & ScBreakType::Manual) ? aManualColor :
|
|
aPageColor );
|
|
bDashed = true;
|
|
}
|
|
else
|
|
{
|
|
rRenderContext.SetLineColor(bMergeCover ? aSheetBGColor : aGridColor);
|
|
bDashed = false;
|
|
}
|
|
|
|
nBreakOld = nBreak;
|
|
}
|
|
}
|
|
|
|
bool bDraw = bGrid || nBreakOld != ScBreakType::NONE || bMergeCover; // simple grid only if set so
|
|
|
|
bool bNextYisNextRow = (pRowInfo[nArrYplus1].nRowNo == nYplus1);
|
|
bool bSingle = !bNextYisNextRow; // Hidden
|
|
for (SCCOL i=nX1; i<=nX2 && !bSingle; i++)
|
|
{
|
|
if (pRowInfo[nArrYplus1].cellInfo(i).bVOverlapped)
|
|
bSingle = true;
|
|
}
|
|
|
|
if (bDraw)
|
|
{
|
|
if ( bSingle && nY<mpDoc->MaxRow() )
|
|
{
|
|
SCROW nVisY = pRowInfo[nArrYplus1].nRowNo;
|
|
|
|
nPosX = nScrX;
|
|
if ( bLayoutRTL )
|
|
nPosX += nMirrorW - nOneX;
|
|
|
|
for (SCCOL i=nX1; i<=nX2; i++)
|
|
{
|
|
const tools::Long nNextX = nPosX + pRowInfo[0].basicCellInfo(i).nWidth * nLayoutSign;
|
|
if (nNextX != nPosX) // visible
|
|
{
|
|
bool bVOver;
|
|
if ( bNextYisNextRow )
|
|
bVOver = pRowInfo[nArrYplus1].cellInfo(i).bVOverlapped;
|
|
else
|
|
{
|
|
bVOver = mpDoc->GetAttr(
|
|
i,nYplus1,nTab,ATTR_MERGE_FLAG)
|
|
->IsVerOverlapped()
|
|
&& mpDoc->GetAttr(
|
|
i,nVisY,nTab,ATTR_MERGE_FLAG)
|
|
->IsVerOverlapped();
|
|
//! nVisY from Array ??
|
|
}
|
|
|
|
if ((!bVOver && !bMergeCover) || (bVOver && bMergeCover))
|
|
{
|
|
aGrid.AddHorLine(bWorksInPixels, nPosX, nNextX-nSignedOneX, nPosY-nOneY, bDashed);
|
|
}
|
|
}
|
|
nPosX = nNextX;
|
|
}
|
|
}
|
|
else if (!bMergeCover)
|
|
{
|
|
aGrid.AddHorLine(bWorksInPixels, nScrX, nScrX+nScrW-nOneX, nPosY-nOneY, bDashed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScOutputData::SetPagebreakMode( ScPageBreakData* pPageData )
|
|
{
|
|
bPagebreakMode = true;
|
|
if (!pPageData)
|
|
return; // not yet initialized -> everything "not printed"
|
|
|
|
// mark printed range
|
|
// (everything in FillInfo is already initialized to sal_False)
|
|
|
|
sal_uInt16 nRangeCount = sal::static_int_cast<sal_uInt16>(pPageData->GetCount());
|
|
for (sal_uInt16 nPos=0; nPos<nRangeCount; nPos++)
|
|
{
|
|
ScRange aRange = pPageData->GetData( nPos ).GetPrintRange();
|
|
|
|
SCCOL nStartX = std::max( aRange.aStart.Col(), nX1 );
|
|
SCCOL nEndX = std::min( aRange.aEnd.Col(), nX2 );
|
|
SCROW nStartY = std::max( aRange.aStart.Row(), nY1 );
|
|
SCROW nEndY = std::min( aRange.aEnd.Row(), nY2 );
|
|
|
|
for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
|
|
{
|
|
RowInfo* pThisRowInfo = &pRowInfo[nArrY];
|
|
if ( pThisRowInfo->bChanged && pThisRowInfo->nRowNo >= nStartY &&
|
|
pThisRowInfo->nRowNo <= nEndY )
|
|
{
|
|
for (SCCOL nX=nStartX; nX<=nEndX; nX++)
|
|
pThisRowInfo->cellInfo(nX).bPrinted = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScOutputData::SetCellRotations()
|
|
{
|
|
//! save nRotMax
|
|
SCCOL nRotMax = nX2;
|
|
for (SCSIZE nRotY=0; nRotY<nArrCount; nRotY++)
|
|
if (pRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && pRowInfo[nRotY].nRotMaxCol > nRotMax)
|
|
nRotMax = pRowInfo[nRotY].nRotMaxCol;
|
|
|
|
for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++)
|
|
{
|
|
RowInfo* pThisRowInfo = &pRowInfo[nArrY];
|
|
if ( pThisRowInfo->nRotMaxCol != SC_ROTMAX_NONE &&
|
|
( pThisRowInfo->bChanged || pRowInfo[nArrY-1].bChanged ||
|
|
( nArrY+1<nArrCount && pRowInfo[nArrY+1].bChanged ) ) )
|
|
{
|
|
SCROW nY = pThisRowInfo->nRowNo;
|
|
|
|
for (SCCOL nX=0; nX<=nRotMax; nX++)
|
|
{
|
|
ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
|
|
const ScPatternAttr* pPattern = pInfo->pPatternAttr;
|
|
const SfxItemSet* pCondSet = pInfo->pConditionSet;
|
|
|
|
if ( !pPattern && !mpDoc->ColHidden(nX, nTab) )
|
|
{
|
|
pPattern = mpDoc->GetPattern( nX, nY, nTab );
|
|
pCondSet = mpDoc->GetCondResult( nX, nY, nTab );
|
|
}
|
|
|
|
if ( pPattern ) // column isn't hidden
|
|
{
|
|
ScRotateDir nDir = pPattern->GetRotateDir( pCondSet );
|
|
if (nDir != ScRotateDir::NONE)
|
|
{
|
|
// Needed for ScCellInfo internal decisions (bg fill, ...)
|
|
pInfo->nRotateDir = nDir;
|
|
|
|
// create target coordinates
|
|
const SCCOL nTargetX(nX - nVisX1 + 1);
|
|
const SCROW nTargetY(nY - nVisY1 + 1);
|
|
|
|
// Check for values - below in SetCellRotation these will
|
|
// be converted to size_t and thus may not be negative
|
|
if(nTargetX >= 0 && nTargetY >= 0)
|
|
{
|
|
// add rotation info to Array information
|
|
const Degree100 nAttrRotate(pPattern->GetRotateVal(pCondSet));
|
|
const SvxRotateMode eRotMode(pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue());
|
|
const double fOrient((bLayoutRTL ? -1.0 : 1.0) * toRadians(nAttrRotate)); // 1/100th degrees -> [0..2PI]
|
|
svx::frame::Array& rArray = mrTabInfo.maArray;
|
|
|
|
rArray.SetCellRotation(nTargetX, nTargetY, eRotMode, fOrient);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static ScRotateDir lcl_GetRotateDir( const ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
|
|
{
|
|
const ScPatternAttr* pPattern = pDoc->GetPattern( nCol, nRow, nTab );
|
|
const SfxItemSet* pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
|
|
|
|
ScRotateDir nRet = ScRotateDir::NONE;
|
|
|
|
Degree100 nAttrRotate = pPattern->GetRotateVal( pCondSet );
|
|
if ( nAttrRotate )
|
|
{
|
|
SvxRotateMode eRotMode =
|
|
pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue();
|
|
|
|
if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
|
|
nRet = ScRotateDir::Standard;
|
|
else if ( eRotMode == SVX_ROTATE_MODE_CENTER )
|
|
nRet = ScRotateDir::Center;
|
|
else if ( eRotMode == SVX_ROTATE_MODE_TOP || eRotMode == SVX_ROTATE_MODE_BOTTOM )
|
|
{
|
|
tools::Long nRot180 = nAttrRotate.get() % 18000; // 1/100 degree
|
|
if ( nRot180 == 9000 )
|
|
nRet = ScRotateDir::Center;
|
|
else if ( ( eRotMode == SVX_ROTATE_MODE_TOP && nRot180 < 9000 ) ||
|
|
( eRotMode == SVX_ROTATE_MODE_BOTTOM && nRot180 > 9000 ) )
|
|
nRet = ScRotateDir::Left;
|
|
else
|
|
nRet = ScRotateDir::Right;
|
|
}
|
|
}
|
|
|
|
return nRet;
|
|
}
|
|
|
|
static const SvxBrushItem* lcl_FindBackground( const ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
|
|
{
|
|
const ScPatternAttr* pPattern = pDoc->GetPattern( nCol, nRow, nTab );
|
|
const SfxItemSet* pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
|
|
const SvxBrushItem* pBackground =
|
|
&pPattern->GetItem( ATTR_BACKGROUND, pCondSet );
|
|
|
|
ScRotateDir nDir = lcl_GetRotateDir( pDoc, nCol, nRow, nTab );
|
|
|
|
// treat CENTER like RIGHT
|
|
if ( nDir == ScRotateDir::Right || nDir == ScRotateDir::Center )
|
|
{
|
|
// text goes to the right -> take background from the left
|
|
while ( nCol > 0 && lcl_GetRotateDir( pDoc, nCol, nRow, nTab ) == nDir &&
|
|
pBackground->GetColor().GetAlpha() != 0 )
|
|
{
|
|
--nCol;
|
|
pPattern = pDoc->GetPattern( nCol, nRow, nTab );
|
|
pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
|
|
pBackground = &pPattern->GetItem( ATTR_BACKGROUND, pCondSet );
|
|
}
|
|
}
|
|
else if ( nDir == ScRotateDir::Left )
|
|
{
|
|
// text goes to the left -> take background from the right
|
|
while ( nCol < pDoc->MaxCol() && lcl_GetRotateDir( pDoc, nCol, nRow, nTab ) == nDir &&
|
|
pBackground->GetColor().GetAlpha() != 0 )
|
|
{
|
|
++nCol;
|
|
pPattern = pDoc->GetPattern( nCol, nRow, nTab );
|
|
pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
|
|
pBackground = &pPattern->GetItem( ATTR_BACKGROUND, pCondSet );
|
|
}
|
|
}
|
|
|
|
return pBackground;
|
|
}
|
|
|
|
static bool lcl_EqualBack( const RowInfo& rFirst, const RowInfo& rOther,
|
|
SCCOL nX1, SCCOL nX2, bool bShowProt, bool bPagebreakMode )
|
|
{
|
|
if ( rFirst.bChanged != rOther.bChanged ||
|
|
rFirst.bEmptyBack != rOther.bEmptyBack )
|
|
return false;
|
|
|
|
SCCOL nX;
|
|
if ( bShowProt )
|
|
{
|
|
for ( nX=nX1; nX<=nX2; nX++ )
|
|
{
|
|
const ScPatternAttr* pPat1 = rFirst.cellInfo(nX).pPatternAttr;
|
|
const ScPatternAttr* pPat2 = rOther.cellInfo(nX).pPatternAttr;
|
|
if ( !pPat1 || !pPat2 ||
|
|
!SfxPoolItem::areSame(pPat1->GetItem(ATTR_PROTECTION), pPat2->GetItem(ATTR_PROTECTION) ) )
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( nX=nX1; nX<=nX2; nX++ )
|
|
if ( !SfxPoolItem::areSame(rFirst.cellInfo(nX).maBackground.getItem(), rOther.cellInfo(nX).maBackground.getItem() ) )
|
|
return false;
|
|
}
|
|
|
|
if ( rFirst.nRotMaxCol != SC_ROTMAX_NONE || rOther.nRotMaxCol != SC_ROTMAX_NONE )
|
|
for ( nX=nX1; nX<=nX2; nX++ )
|
|
if ( rFirst.cellInfo(nX).nRotateDir != rOther.cellInfo(nX).nRotateDir )
|
|
return false;
|
|
|
|
if ( bPagebreakMode )
|
|
for ( nX=nX1; nX<=nX2; nX++ )
|
|
if ( rFirst.cellInfo(nX).bPrinted != rOther.cellInfo(nX).bPrinted )
|
|
return false;
|
|
|
|
for ( nX=nX1; nX<=nX2; nX++ )
|
|
{
|
|
std::optional<Color> const & pCol1 = rFirst.cellInfo(nX).mxColorScale;
|
|
std::optional<Color> const & pCol2 = rOther.cellInfo(nX).mxColorScale;
|
|
if( (pCol1 && !pCol2) || (!pCol1 && pCol2) )
|
|
return false;
|
|
|
|
if (pCol1 && (*pCol1 != *pCol2))
|
|
return false;
|
|
|
|
const ScDataBarInfo* pInfo1 = rFirst.cellInfo(nX).pDataBar;
|
|
const ScDataBarInfo* pInfo2 = rOther.cellInfo(nX).pDataBar;
|
|
|
|
if( (pInfo1 && !pInfo2) || (!pInfo1 && pInfo2) )
|
|
return false;
|
|
|
|
if (pInfo1 && (*pInfo1 != *pInfo2))
|
|
return false;
|
|
|
|
// each cell with an icon set should be painted the same way
|
|
const ScIconSetInfo* pIconSet1 = rFirst.cellInfo(nX).pIconSet;
|
|
const ScIconSetInfo* pIconSet2 = rOther.cellInfo(nX).pIconSet;
|
|
|
|
if(pIconSet1 || pIconSet2)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ScOutputData::DrawDocumentBackground()
|
|
{
|
|
if ( !bSolidBackground )
|
|
return;
|
|
|
|
Color aBgColor(ScModule::get()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor);
|
|
mpDev->SetLineColor(aBgColor);
|
|
mpDev->SetFillColor(aBgColor);
|
|
|
|
Point aScreenPos = mpDev->PixelToLogic(Point(nScrX, nScrY));
|
|
Size aScreenSize = mpDev->PixelToLogic(Size(nScrW - 1,nScrH - 1));
|
|
|
|
mpDev->DrawRect(tools::Rectangle(aScreenPos, aScreenSize));
|
|
}
|
|
|
|
namespace {
|
|
|
|
const double lclCornerRectTransparency = 40.0;
|
|
|
|
void drawDataBars(vcl::RenderContext& rRenderContext, const ScDataBarInfo* pOldDataBarInfo, const tools::Rectangle& rRect, tools::Long nOneX, tools::Long nOneY)
|
|
{
|
|
tools::Long nPosZero = 0;
|
|
tools::Rectangle aPaintRect = rRect;
|
|
aPaintRect.AdjustTop(2 * nOneY );
|
|
aPaintRect.AdjustBottom( -(2 * nOneY) );
|
|
aPaintRect.AdjustLeft( 2 * nOneX );
|
|
aPaintRect.AdjustRight( -(2 * nOneX) );
|
|
if(pOldDataBarInfo->mnZero)
|
|
{
|
|
// need to calculate null point in cell
|
|
tools::Long nLength = aPaintRect.Right() - aPaintRect.Left();
|
|
nPosZero = static_cast<tools::Long>(aPaintRect.Left() + nLength*pOldDataBarInfo->mnZero/100.0);
|
|
}
|
|
else
|
|
{
|
|
nPosZero = aPaintRect.Left();
|
|
}
|
|
|
|
if(pOldDataBarInfo->mnLength < 0)
|
|
{
|
|
aPaintRect.SetRight( nPosZero );
|
|
tools::Long nLength = nPosZero - aPaintRect.Left();
|
|
aPaintRect.SetLeft( nPosZero + static_cast<tools::Long>(nLength * pOldDataBarInfo->mnLength/100.0) );
|
|
}
|
|
else if(pOldDataBarInfo->mnLength > 0)
|
|
{
|
|
aPaintRect.SetLeft( nPosZero );
|
|
tools::Long nLength = aPaintRect.Right() - nPosZero;
|
|
aPaintRect.SetRight( nPosZero + static_cast<tools::Long>(nLength * pOldDataBarInfo->mnLength/100.0) );
|
|
}
|
|
else
|
|
return;
|
|
|
|
if(pOldDataBarInfo->mbGradient)
|
|
{
|
|
rRenderContext.SetLineColor(pOldDataBarInfo->maColor);
|
|
Gradient aGradient(css::awt::GradientStyle_LINEAR, pOldDataBarInfo->maColor, COL_TRANSPARENT);
|
|
aGradient.SetSteps(255);
|
|
|
|
if(pOldDataBarInfo->mnLength < 0)
|
|
aGradient.SetAngle(2700_deg10);
|
|
else
|
|
aGradient.SetAngle(900_deg10);
|
|
|
|
rRenderContext.DrawGradient(aPaintRect, aGradient);
|
|
|
|
rRenderContext.SetLineColor();
|
|
}
|
|
else
|
|
{
|
|
rRenderContext.SetFillColor(pOldDataBarInfo->maColor);
|
|
rRenderContext.DrawRect(aPaintRect);
|
|
}
|
|
|
|
//draw axis
|
|
if(!(pOldDataBarInfo->mnZero && pOldDataBarInfo->mnZero != 100))
|
|
return;
|
|
|
|
Point aPoint1(nPosZero, rRect.Top());
|
|
Point aPoint2(nPosZero, rRect.Bottom());
|
|
LineInfo aLineInfo(LineStyle::Dash, 1);
|
|
aLineInfo.SetDashCount( 4 );
|
|
aLineInfo.SetDistance( 3 );
|
|
aLineInfo.SetDashLen( 3 );
|
|
rRenderContext.SetFillColor(pOldDataBarInfo->maAxisColor);
|
|
rRenderContext.SetLineColor(pOldDataBarInfo->maAxisColor);
|
|
rRenderContext.DrawLine(aPoint1, aPoint2, aLineInfo);
|
|
rRenderContext.SetLineColor();
|
|
rRenderContext.SetFillColor();
|
|
}
|
|
|
|
const BitmapEx& getIcon(sc::IconSetBitmapMap & rIconSetBitmapMap, ScIconSetType eType, sal_Int32 nIndex)
|
|
{
|
|
return ScIconSetFormat::getBitmap(rIconSetBitmapMap, eType, nIndex);
|
|
}
|
|
|
|
void drawIconSets(vcl::RenderContext& rRenderContext, const ScIconSetInfo* pOldIconSetInfo, const tools::Rectangle& rRect, tools::Long nOneX, tools::Long nOneY,
|
|
sc::IconSetBitmapMap & rIconSetBitmapMap)
|
|
{
|
|
ScIconSetType eType = pOldIconSetInfo->eIconSetType;
|
|
sal_Int32 nIndex = pOldIconSetInfo->nIconIndex;
|
|
const BitmapEx& rIcon = getIcon(rIconSetBitmapMap, eType, nIndex);
|
|
|
|
tools::Long aHeight = o3tl::convert(10, o3tl::Length::pt, o3tl::Length::mm100);
|
|
|
|
if (pOldIconSetInfo->mnHeight)
|
|
{
|
|
if (comphelper::LibreOfficeKit::isActive())
|
|
{
|
|
aHeight = rRenderContext.LogicToPixel(Size(0, pOldIconSetInfo->mnHeight), MapMode(MapUnit::MapTwip)).Height();
|
|
aHeight *= comphelper::LibreOfficeKit::getDPIScale();
|
|
}
|
|
else
|
|
{
|
|
aHeight = o3tl::convert(pOldIconSetInfo->mnHeight, o3tl::Length::twip, o3tl::Length::mm100);
|
|
}
|
|
}
|
|
|
|
Size aSize = rIcon.GetSizePixel();
|
|
double fRatio = static_cast<double>(aSize.Width()) / aSize.Height();
|
|
tools::Long aWidth = fRatio * aHeight;
|
|
|
|
rRenderContext.Push();
|
|
rRenderContext.SetClipRegion(vcl::Region(rRect));
|
|
rRenderContext.DrawBitmapEx(Point(rRect.Left() + 2 * nOneX, rRect.Bottom() - 2 * nOneY - aHeight), Size(aWidth, aHeight), rIcon);
|
|
rRenderContext.Pop();
|
|
}
|
|
|
|
void drawCells(vcl::RenderContext& rRenderContext, std::optional<Color> const & pColor, const SvxBrushItem* pBackground, std::optional<Color>& pOldColor, const SvxBrushItem*& pOldBackground,
|
|
tools::Rectangle& rRect, tools::Long nPosX, tools::Long nLayoutSign, tools::Long nOneX, tools::Long nOneY, const ScDataBarInfo* pDataBarInfo, const ScDataBarInfo*& pOldDataBarInfo,
|
|
const ScIconSetInfo* pIconSetInfo, const ScIconSetInfo*& pOldIconSetInfo,
|
|
sc::IconSetBitmapMap & rIconSetBitmapMap)
|
|
{
|
|
tools::Long nSignedOneX = nOneX * nLayoutSign;
|
|
// need to paint if old color scale has been used and now
|
|
// we have a different color or a style based background
|
|
// we can here fall back to pointer comparison
|
|
if (pOldColor && (pBackground || pOldColor != pColor || pOldDataBarInfo || pDataBarInfo || pIconSetInfo || pOldIconSetInfo))
|
|
{
|
|
rRect.SetRight( nPosX-nSignedOneX );
|
|
if( !pOldColor->IsTransparent() )
|
|
{
|
|
rRenderContext.SetFillColor( *pOldColor );
|
|
rRenderContext.DrawRect( rRect );
|
|
}
|
|
if( pOldDataBarInfo )
|
|
drawDataBars(rRenderContext, pOldDataBarInfo, rRect, nOneX, nOneY);
|
|
if( pOldIconSetInfo )
|
|
drawIconSets(rRenderContext, pOldIconSetInfo, rRect, nOneX, nOneY, rIconSetBitmapMap);
|
|
|
|
rRect.SetLeft( nPosX - nSignedOneX );
|
|
}
|
|
|
|
if ( pOldBackground && (pColor || !SfxPoolItem::areSame(pBackground, pOldBackground) || pOldDataBarInfo || pDataBarInfo || pIconSetInfo || pOldIconSetInfo) )
|
|
{
|
|
rRect.SetRight( nPosX-nSignedOneX );
|
|
if (pOldBackground) // ==0 if hidden
|
|
{
|
|
Color aBackCol = pOldBackground->GetColor();
|
|
if ( !aBackCol.IsTransparent() ) //! partial transparency?
|
|
{
|
|
rRenderContext.SetFillColor( aBackCol );
|
|
rRenderContext.DrawRect( rRect );
|
|
}
|
|
}
|
|
if( pOldDataBarInfo )
|
|
drawDataBars(rRenderContext, pOldDataBarInfo, rRect, nOneX, nOneY);
|
|
if( pOldIconSetInfo )
|
|
drawIconSets(rRenderContext, pOldIconSetInfo, rRect, nOneX, nOneY, rIconSetBitmapMap);
|
|
|
|
rRect.SetLeft( nPosX - nSignedOneX );
|
|
}
|
|
|
|
if (!pOldBackground && !pOldColor && (pDataBarInfo || pIconSetInfo))
|
|
{
|
|
rRect.SetRight( nPosX -nSignedOneX );
|
|
rRect.SetLeft( nPosX - nSignedOneX );
|
|
}
|
|
|
|
if(pColor)
|
|
{
|
|
// only update pOldColor if the colors changed
|
|
if (!pOldColor || *pOldColor != *pColor)
|
|
pOldColor = pColor;
|
|
|
|
pOldBackground = nullptr;
|
|
}
|
|
else if(pBackground)
|
|
{
|
|
pOldBackground = pBackground;
|
|
pOldColor.reset();
|
|
}
|
|
|
|
if(pDataBarInfo)
|
|
pOldDataBarInfo = pDataBarInfo;
|
|
else
|
|
pOldDataBarInfo = nullptr;
|
|
|
|
if(pIconSetInfo)
|
|
pOldIconSetInfo = pIconSetInfo;
|
|
else
|
|
pOldIconSetInfo = nullptr;
|
|
}
|
|
|
|
}
|
|
|
|
void ScOutputData::DrawBackground(vcl::RenderContext& rRenderContext)
|
|
{
|
|
vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(mpDev->GetExtOutDevData());
|
|
bool bTaggedPDF = pPDF && pPDF->GetIsExportTaggedPDF();
|
|
|
|
Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
|
|
tools::Long nOneXLogic = aOnePixel.Width();
|
|
tools::Long nOneYLogic = aOnePixel.Height();
|
|
|
|
// See more about bWorksInPixels in ScOutputData::DrawGrid
|
|
bool bWorksInPixels = false;
|
|
if (eType == OUTTYPE_WINDOW)
|
|
bWorksInPixels = true;
|
|
|
|
tools::Long nOneX = 1;
|
|
tools::Long nOneY = 1;
|
|
if (!bWorksInPixels)
|
|
{
|
|
nOneX = nOneXLogic;
|
|
nOneY = nOneYLogic;
|
|
}
|
|
|
|
tools::Rectangle aRect;
|
|
|
|
tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
|
|
|
|
rRenderContext.SetLineColor();
|
|
|
|
bool bShowProt = mbSyntaxMode && mpDoc->IsTabProtected(nTab);
|
|
bool bDoAll = bShowProt || bPagebreakMode || bSolidBackground;
|
|
|
|
bool bCellContrast = mbUseStyleColor &&
|
|
Application::GetSettings().GetStyleSettings().GetHighContrastMode();
|
|
|
|
tools::Long nPosY = nScrY;
|
|
|
|
const svtools::ColorConfig& rColorCfg = ScModule::get()->GetColorConfig();
|
|
Color aProtectedColor( rColorCfg.GetColorValue( svtools::CALCPROTECTEDBACKGROUND ).nColor );
|
|
auto pProtectedBackground = std::make_shared<SvxBrushItem>( aProtectedColor, ATTR_BACKGROUND );
|
|
|
|
// iterate through the rows to show
|
|
for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
|
|
{
|
|
RowInfo* pThisRowInfo = &pRowInfo[nArrY];
|
|
tools::Long nRowHeight = pThisRowInfo->nHeight;
|
|
|
|
if ( pThisRowInfo->bChanged )
|
|
{
|
|
if ( ( ( pThisRowInfo->bEmptyBack ) || mbSyntaxMode ) && !bDoAll )
|
|
{
|
|
// nothing
|
|
}
|
|
else
|
|
{
|
|
if (bTaggedPDF)
|
|
pPDF->WrapBeginStructureElement(vcl::PDFWriter::NonStructElement);
|
|
|
|
// scan for rows with the same background:
|
|
SCSIZE nSkip = 0;
|
|
while ( nArrY+nSkip+2<nArrCount &&
|
|
lcl_EqualBack( *pThisRowInfo, pRowInfo[nArrY+nSkip+1],
|
|
nX1, nX2, bShowProt, bPagebreakMode ) )
|
|
{
|
|
++nSkip;
|
|
nRowHeight += pRowInfo[nArrY+nSkip].nHeight; // after incrementing
|
|
}
|
|
|
|
tools::Long nPosX = nScrX;
|
|
|
|
if ( bLayoutRTL )
|
|
nPosX += nMirrorW - nOneX;
|
|
|
|
aRect = tools::Rectangle(nPosX, nPosY - nOneY, nPosX, nPosY - nOneY + nRowHeight);
|
|
if (bWorksInPixels)
|
|
aRect = rRenderContext.PixelToLogic(aRect); // internal data in pixels, but we'll be drawing in logic units
|
|
|
|
const SvxBrushItem* pOldBackground = nullptr;
|
|
const SvxBrushItem* pBackground = nullptr;
|
|
std::optional<Color> pOldColor;
|
|
const ScDataBarInfo* pOldDataBarInfo = nullptr;
|
|
const ScIconSetInfo* pOldIconSetInfo = nullptr;
|
|
SCCOL nMergedCols = 1;
|
|
SCCOL nOldMerged = 0;
|
|
|
|
for (SCCOL nX=nX1; nX + nMergedCols <= nX2 + 1; nX += nOldMerged)
|
|
{
|
|
ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX-1+nMergedCols);
|
|
|
|
nOldMerged = nMergedCols;
|
|
|
|
tools::Long nNewPosX = nPosX;
|
|
// extend for all merged cells
|
|
nMergedCols = 1;
|
|
if (pInfo->bMerged && pInfo->pPatternAttr)
|
|
{
|
|
const ScMergeAttr* pMerge =
|
|
&pInfo->pPatternAttr->GetItem(ATTR_MERGE);
|
|
nMergedCols = std::max<SCCOL>(1, pMerge->GetColMerge());
|
|
}
|
|
|
|
for (SCCOL nMerged = 0; nMerged < nMergedCols; ++nMerged)
|
|
{
|
|
SCCOL nCol = nX+nOldMerged+nMerged;
|
|
if (nCol > nX2+2)
|
|
break;
|
|
nNewPosX += pRowInfo[0].basicCellInfo(nCol-1).nWidth * nLayoutSign;
|
|
}
|
|
|
|
if (nNewPosX == nPosX)
|
|
continue; // Zero width, no need to draw.
|
|
|
|
if (bCellContrast)
|
|
{
|
|
// high contrast for cell borders and backgrounds -> empty background
|
|
pBackground = ScGlobal::GetEmptyBrushItem();
|
|
}
|
|
else if (bShowProt) // show cell protection in syntax mode
|
|
{
|
|
const ScPatternAttr* pP = pInfo->pPatternAttr;
|
|
if (pP)
|
|
{
|
|
const ScProtectionAttr& rProt = pP->GetItem(ATTR_PROTECTION);
|
|
if (rProt.GetProtection() || rProt.GetHideCell())
|
|
pBackground = pProtectedBackground.get();
|
|
else
|
|
pBackground = ScGlobal::GetEmptyBrushItem();
|
|
}
|
|
else
|
|
pBackground = nullptr;
|
|
}
|
|
else
|
|
pBackground = static_cast<const SvxBrushItem*>(pInfo->maBackground.getItem());
|
|
|
|
if ( bPagebreakMode && !pInfo->bPrinted )
|
|
pBackground = pProtectedBackground.get();
|
|
|
|
if ( pInfo->nRotateDir > ScRotateDir::Standard &&
|
|
!pBackground->GetColor().IsFullyTransparent() &&
|
|
!bCellContrast )
|
|
{
|
|
SCROW nY = pRowInfo[nArrY].nRowNo;
|
|
pBackground = lcl_FindBackground( mpDoc, nX, nY, nTab );
|
|
}
|
|
|
|
std::optional<Color> const & pColor = pInfo->mxColorScale;
|
|
const ScDataBarInfo* pDataBarInfo = pInfo->pDataBar;
|
|
const ScIconSetInfo* pIconSetInfo = pInfo->pIconSet;
|
|
|
|
tools::Long nPosXLogic = nPosX;
|
|
if (bWorksInPixels)
|
|
nPosXLogic = rRenderContext.PixelToLogic(Point(nPosX, 0)).X();
|
|
|
|
drawCells(rRenderContext, pColor, pBackground, pOldColor, pOldBackground, aRect, nPosXLogic, nLayoutSign, nOneXLogic, nOneYLogic, pDataBarInfo, pOldDataBarInfo, pIconSetInfo, pOldIconSetInfo, mpDoc->GetIconSetBitmapMap());
|
|
|
|
nPosX = nNewPosX;
|
|
}
|
|
|
|
tools::Long nPosXLogic = nPosX;
|
|
if (bWorksInPixels)
|
|
nPosXLogic = rRenderContext.PixelToLogic(Point(nPosX, 0)).X();
|
|
|
|
drawCells(rRenderContext, std::optional<Color>(), nullptr, pOldColor, pOldBackground, aRect, nPosXLogic, nLayoutSign, nOneXLogic, nOneYLogic, nullptr, pOldDataBarInfo, nullptr, pOldIconSetInfo, mpDoc->GetIconSetBitmapMap());
|
|
|
|
nArrY += nSkip;
|
|
|
|
if (bTaggedPDF)
|
|
pPDF->EndStructureElement();
|
|
}
|
|
}
|
|
nPosY += nRowHeight;
|
|
}
|
|
}
|
|
|
|
void ScOutputData::DrawShadow()
|
|
{
|
|
DrawExtraShadow( false, false, false, false );
|
|
}
|
|
|
|
void ScOutputData::DrawExtraShadow(bool bLeft, bool bTop, bool bRight, bool bBottom)
|
|
{
|
|
vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(mpDev->GetExtOutDevData());
|
|
bool bTaggedPDF = pPDF && pPDF->GetIsExportTaggedPDF();
|
|
|
|
mpDev->SetLineColor();
|
|
|
|
const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
|
|
bool bCellContrast = mbUseStyleColor && rStyleSettings.GetHighContrastMode();
|
|
Color aAutoTextColor;
|
|
if ( bCellContrast )
|
|
aAutoTextColor = ScModule::get()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
|
|
|
|
tools::Long nInitPosX = nScrX;
|
|
if ( bLayoutRTL )
|
|
{
|
|
Size aOnePixel = mpDev->PixelToLogic(Size(1,1));
|
|
tools::Long nOneX = aOnePixel.Width();
|
|
nInitPosX += nMirrorW - nOneX;
|
|
}
|
|
tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
|
|
|
|
tools::Long nPosY = nScrY - pRowInfo[0].nHeight;
|
|
for (SCSIZE nArrY=0; nArrY<nArrCount; nArrY++)
|
|
{
|
|
bool bCornerY = ( nArrY == 0 ) || ( nArrY+1 == nArrCount );
|
|
bool bSkipY = ( nArrY==0 && !bTop ) || ( nArrY+1 == nArrCount && !bBottom );
|
|
|
|
RowInfo* pThisRowInfo = &pRowInfo[nArrY];
|
|
tools::Long nRowHeight = pThisRowInfo->nHeight;
|
|
|
|
if ( pThisRowInfo->bChanged && !bSkipY )
|
|
{
|
|
tools::Long nPosX = nInitPosX - pRowInfo[0].basicCellInfo(nX1-1).nWidth * nLayoutSign;
|
|
for (SCCOL nCol=nX1-1; nCol<=nX2+1; nCol++)
|
|
{
|
|
bool bCornerX = ( nCol==nX1-1 || nCol==nX2+1 );
|
|
bool bSkipX = ( nCol==nX1-1 && !bLeft ) || ( nCol==nX2+1 && !bRight );
|
|
|
|
for (sal_uInt16 nPass=0; nPass<2; nPass++) // horizontal / vertical
|
|
{
|
|
const SvxShadowItem* pAttr = nPass ?
|
|
pThisRowInfo->cellInfo(nCol).pVShadowOrigin :
|
|
pThisRowInfo->cellInfo(nCol).pHShadowOrigin;
|
|
if ( pAttr && !bSkipX )
|
|
{
|
|
if (bTaggedPDF)
|
|
pPDF->WrapBeginStructureElement(vcl::PDFWriter::NonStructElement);
|
|
|
|
ScShadowPart ePart = nPass ?
|
|
pThisRowInfo->cellInfo(nCol).eVShadowPart :
|
|
pThisRowInfo->cellInfo(nCol).eHShadowPart;
|
|
|
|
bool bDo = true;
|
|
if ( (nPass==0 && bCornerX) || (nPass==1 && bCornerY) )
|
|
if ( ePart != SC_SHADOW_CORNER )
|
|
bDo = false;
|
|
|
|
if (bDo)
|
|
{
|
|
tools::Long nThisWidth = pRowInfo[0].basicCellInfo(nCol).nWidth;
|
|
tools::Long nMaxWidth = nThisWidth;
|
|
if (!nMaxWidth)
|
|
{
|
|
//! direction must depend on shadow location
|
|
SCCOL nWx = nCol+1;
|
|
while (nWx<nX2 && !pRowInfo[0].basicCellInfo(nWx).nWidth)
|
|
++nWx;
|
|
nMaxWidth = pRowInfo[0].basicCellInfo(nWx).nWidth;
|
|
}
|
|
|
|
// rectangle is in logical orientation
|
|
tools::Rectangle aRect( nPosX, nPosY,
|
|
nPosX + ( nThisWidth - 1 ) * nLayoutSign,
|
|
nPosY + pRowInfo[nArrY].nHeight - 1 );
|
|
|
|
tools::Long nSize = pAttr->GetWidth();
|
|
tools::Long nSizeX = static_cast<tools::Long>(nSize*mnPPTX);
|
|
if (nSizeX >= nMaxWidth) nSizeX = nMaxWidth-1;
|
|
tools::Long nSizeY = static_cast<tools::Long>(nSize*mnPPTY);
|
|
if (nSizeY >= nRowHeight) nSizeY = nRowHeight-1;
|
|
|
|
nSizeX *= nLayoutSign; // used only to add to rectangle values
|
|
|
|
SvxShadowLocation eLoc = pAttr->GetLocation();
|
|
if ( bLayoutRTL )
|
|
{
|
|
// Shadow location is specified as "visual" (right is always right),
|
|
// so the attribute's location value is mirrored here and in FillInfo.
|
|
switch (eLoc)
|
|
{
|
|
case SvxShadowLocation::BottomRight: eLoc = SvxShadowLocation::BottomLeft; break;
|
|
case SvxShadowLocation::BottomLeft: eLoc = SvxShadowLocation::BottomRight; break;
|
|
case SvxShadowLocation::TopRight: eLoc = SvxShadowLocation::TopLeft; break;
|
|
case SvxShadowLocation::TopLeft: eLoc = SvxShadowLocation::TopRight; break;
|
|
default:
|
|
{
|
|
// added to avoid warnings
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ePart == SC_SHADOW_HORIZ || ePart == SC_SHADOW_HSTART ||
|
|
ePart == SC_SHADOW_CORNER)
|
|
{
|
|
if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::TopRight)
|
|
aRect.SetTop( aRect.Bottom() - nSizeY );
|
|
else
|
|
aRect.SetBottom( aRect.Top() + nSizeY );
|
|
}
|
|
if (ePart == SC_SHADOW_VERT || ePart == SC_SHADOW_VSTART ||
|
|
ePart == SC_SHADOW_CORNER)
|
|
{
|
|
if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::BottomLeft)
|
|
aRect.SetLeft( aRect.Right() - nSizeX );
|
|
else
|
|
aRect.SetRight( aRect.Left() + nSizeX );
|
|
}
|
|
if (ePart == SC_SHADOW_HSTART)
|
|
{
|
|
if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::BottomLeft)
|
|
aRect.AdjustRight( -nSizeX );
|
|
else
|
|
aRect.AdjustLeft(nSizeX );
|
|
}
|
|
if (ePart == SC_SHADOW_VSTART)
|
|
{
|
|
if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::TopRight)
|
|
aRect.AdjustBottom( -nSizeY );
|
|
else
|
|
aRect.AdjustTop(nSizeY );
|
|
}
|
|
|
|
//! merge rectangles?
|
|
mpDev->SetFillColor( bCellContrast ? aAutoTextColor : pAttr->GetColor() );
|
|
mpDev->DrawRect( aRect );
|
|
|
|
if (bTaggedPDF)
|
|
pPDF->EndStructureElement();
|
|
}
|
|
}
|
|
}
|
|
|
|
nPosX += pRowInfo[0].basicCellInfo(nCol).nWidth * nLayoutSign;
|
|
}
|
|
}
|
|
nPosY += nRowHeight;
|
|
}
|
|
}
|
|
|
|
void ScOutputData::DrawClear()
|
|
{
|
|
tools::Rectangle aRect;
|
|
Size aOnePixel = mpDev->PixelToLogic(Size(1,1));
|
|
tools::Long nOneX = aOnePixel.Width();
|
|
tools::Long nOneY = aOnePixel.Height();
|
|
|
|
// (called only for ScGridWindow)
|
|
Color aBgColor(ScModule::get()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor);
|
|
|
|
if (bMetaFile)
|
|
nOneX = nOneY = 0;
|
|
|
|
mpDev->SetLineColor();
|
|
|
|
mpDev->SetFillColor( aBgColor );
|
|
|
|
tools::Long nPosY = nScrY;
|
|
for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
|
|
{
|
|
RowInfo* pThisRowInfo = &pRowInfo[nArrY];
|
|
tools::Long nRowHeight = pThisRowInfo->nHeight;
|
|
|
|
if ( pThisRowInfo->bChanged )
|
|
{
|
|
// scan for more rows which must be painted:
|
|
SCSIZE nSkip = 0;
|
|
while ( nArrY+nSkip+2<nArrCount && pRowInfo[nArrY+nSkip+1].bChanged )
|
|
{
|
|
++nSkip;
|
|
nRowHeight += pRowInfo[nArrY+nSkip].nHeight; // after incrementing
|
|
}
|
|
|
|
aRect = tools::Rectangle( Point( nScrX, nPosY ),
|
|
Size( nScrW+1-nOneX, nRowHeight+1-nOneY) );
|
|
mpDev->DrawRect( aRect );
|
|
|
|
nArrY += nSkip;
|
|
}
|
|
nPosY += nRowHeight;
|
|
}
|
|
}
|
|
|
|
// Lines
|
|
|
|
static tools::Long lclGetSnappedX( const OutputDevice& rDev, tools::Long nPosX, bool bSnapPixel )
|
|
{
|
|
return (bSnapPixel && nPosX) ? rDev.PixelToLogic( rDev.LogicToPixel( Size( nPosX, 0 ) ) ).Width() : nPosX;
|
|
}
|
|
|
|
static tools::Long lclGetSnappedY( const OutputDevice& rDev, tools::Long nPosY, bool bSnapPixel )
|
|
{
|
|
return (bSnapPixel && nPosY) ? rDev.PixelToLogic( rDev.LogicToPixel( Size( 0, nPosY ) ) ).Height() : nPosY;
|
|
}
|
|
|
|
void ScOutputData::DrawFrame(vcl::RenderContext& rRenderContext)
|
|
{
|
|
vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(mpDev->GetExtOutDevData());
|
|
bool bTaggedPDF = pPDF && pPDF->GetIsExportTaggedPDF();
|
|
if (bTaggedPDF)
|
|
pPDF->WrapBeginStructureElement(vcl::PDFWriter::NonStructElement);
|
|
|
|
DrawModeFlags nOldDrawMode = rRenderContext.GetDrawMode();
|
|
|
|
Color aSingleColor;
|
|
bool bUseSingleColor = false;
|
|
const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
|
|
bool bCellContrast = mbUseStyleColor && rStyleSettings.GetHighContrastMode();
|
|
|
|
// if a Calc OLE object is embedded in Draw/Impress, the VCL DrawMode is used
|
|
// for display mode / B&W printing. The VCL DrawMode handling doesn't work for lines
|
|
// that are drawn with DrawRect, so if the line/background bits are set, the DrawMode
|
|
// must be reset and the border colors handled here.
|
|
|
|
if ( ( nOldDrawMode & DrawModeFlags::WhiteFill ) && ( nOldDrawMode & DrawModeFlags::BlackLine ) )
|
|
{
|
|
rRenderContext.SetDrawMode( nOldDrawMode & (~DrawModeFlags::WhiteFill) );
|
|
aSingleColor = COL_BLACK;
|
|
bUseSingleColor = true;
|
|
}
|
|
else if ( ( nOldDrawMode & DrawModeFlags::SettingsFill ) && ( nOldDrawMode & DrawModeFlags::SettingsLine ) )
|
|
{
|
|
rRenderContext.SetDrawMode( nOldDrawMode & (~DrawModeFlags::SettingsFill) );
|
|
aSingleColor = rStyleSettings.GetWindowTextColor(); // same as used in VCL for DrawModeFlags::SettingsLine
|
|
bUseSingleColor = true;
|
|
}
|
|
else if ( bCellContrast )
|
|
{
|
|
aSingleColor = ScModule::get()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
|
|
bUseSingleColor = true;
|
|
}
|
|
|
|
const Color* pForceColor = bUseSingleColor ? &aSingleColor : nullptr;
|
|
|
|
if (mrTabInfo.maArray.HasCellRotation())
|
|
{
|
|
DrawRotatedFrame(rRenderContext); // removes the lines that must not be painted here
|
|
}
|
|
|
|
tools::Long nInitPosX = nScrX;
|
|
if ( bLayoutRTL )
|
|
{
|
|
Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
|
|
tools::Long nOneX = aOnePixel.Width();
|
|
nInitPosX += nMirrorW - nOneX;
|
|
}
|
|
tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
|
|
|
|
// *** set column and row sizes of the frame border array ***
|
|
|
|
svx::frame::Array& rArray = mrTabInfo.maArray;
|
|
size_t nColCount = rArray.GetColCount();
|
|
size_t nRowCount = rArray.GetRowCount();
|
|
|
|
// row heights
|
|
|
|
// row 0 is not visible (dummy for borders from top) - subtract its height from initial position
|
|
// subtract 1 unit more, because position 0 is first *in* cell, grid line is one unit before
|
|
tools::Long nOldPosY = nScrY - 1 - pRowInfo[ 0 ].nHeight;
|
|
tools::Long nOldSnapY = lclGetSnappedY( rRenderContext, nOldPosY, bSnapPixel );
|
|
rArray.SetYOffset( nOldSnapY );
|
|
for( size_t nRow = 0; nRow < nRowCount; ++nRow )
|
|
{
|
|
tools::Long nNewPosY = nOldPosY + pRowInfo[ nRow ].nHeight;
|
|
tools::Long nNewSnapY = lclGetSnappedY( rRenderContext, nNewPosY, bSnapPixel );
|
|
rArray.SetRowHeight( nRow, nNewSnapY - nOldSnapY );
|
|
nOldPosY = nNewPosY;
|
|
nOldSnapY = nNewSnapY;
|
|
}
|
|
|
|
// column widths
|
|
|
|
// column nX1-1 is not visible (dummy for borders from left) - subtract its width from initial position
|
|
// subtract 1 unit more, because position 0 is first *in* cell, grid line is one unit above
|
|
tools::Long nOldPosX = nInitPosX - nLayoutSign * (1 + pRowInfo[ 0 ].basicCellInfo( nX1 - 1 ).nWidth);
|
|
tools::Long nOldSnapX = lclGetSnappedX( rRenderContext, nOldPosX, bSnapPixel );
|
|
// set X offset for left-to-right sheets; for right-to-left sheets this is done after for() loop
|
|
if( !bLayoutRTL )
|
|
rArray.SetXOffset( nOldSnapX );
|
|
for( SCCOL nCol = nX1 - 1; nCol <= nX2 + 1; ++nCol )
|
|
{
|
|
size_t nArrCol = bLayoutRTL ? nX2 + 1 - nCol : nCol - (nX1 - 1);
|
|
tools::Long nNewPosX = nOldPosX + pRowInfo[ 0 ].basicCellInfo( nCol ).nWidth * nLayoutSign;
|
|
tools::Long nNewSnapX = lclGetSnappedX( rRenderContext, nNewPosX, bSnapPixel );
|
|
rArray.SetColWidth( nArrCol, std::abs( nNewSnapX - nOldSnapX ) );
|
|
nOldPosX = nNewPosX;
|
|
nOldSnapX = nNewSnapX;
|
|
}
|
|
if( bLayoutRTL )
|
|
rArray.SetXOffset( nOldSnapX );
|
|
|
|
// *** draw the array ***
|
|
|
|
size_t nFirstCol = 1;
|
|
size_t nFirstRow = 1;
|
|
size_t nLastCol = nColCount - 2;
|
|
size_t nLastRow = nRowCount - 2;
|
|
|
|
if( mrTabInfo.mbPageMode )
|
|
rArray.SetClipRange( nFirstCol, nFirstRow, nLastCol, nLastRow );
|
|
|
|
// draw only rows with set RowInfo::bChanged flag
|
|
size_t nRow1 = nFirstRow;
|
|
std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(CreateProcessor2D());
|
|
if (!pProcessor)
|
|
return;
|
|
while( nRow1 <= nLastRow )
|
|
{
|
|
while( (nRow1 <= nLastRow) && !pRowInfo[ nRow1 ].bChanged ) ++nRow1;
|
|
if( nRow1 <= nLastRow )
|
|
{
|
|
size_t nRow2 = nRow1;
|
|
while( (nRow2 + 1 <= nLastRow) && pRowInfo[ nRow2 + 1 ].bChanged ) ++nRow2;
|
|
auto xPrimitive = rArray.CreateB2DPrimitiveRange(
|
|
nFirstCol, nRow1, nLastCol, nRow2, pForceColor );
|
|
pProcessor->process(xPrimitive);
|
|
nRow1 = nRow2 + 1;
|
|
}
|
|
}
|
|
pProcessor.reset();
|
|
|
|
rRenderContext.SetDrawMode(nOldDrawMode);
|
|
|
|
if (bTaggedPDF)
|
|
pPDF->EndStructureElement();
|
|
}
|
|
|
|
void ScOutputData::DrawRotatedFrame(vcl::RenderContext& rRenderContext)
|
|
{
|
|
//! save nRotMax
|
|
SCCOL nRotMax = nX2;
|
|
for (SCSIZE nRotY=0; nRotY<nArrCount; nRotY++)
|
|
if (pRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && pRowInfo[nRotY].nRotMaxCol > nRotMax)
|
|
nRotMax = pRowInfo[nRotY].nRotMaxCol;
|
|
|
|
const ScPatternAttr* pPattern;
|
|
const SfxItemSet* pCondSet;
|
|
|
|
const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
|
|
bool bCellContrast = mbUseStyleColor && rStyleSettings.GetHighContrastMode();
|
|
|
|
tools::Long nInitPosX = nScrX;
|
|
if ( bLayoutRTL )
|
|
{
|
|
Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
|
|
tools::Long nOneX = aOnePixel.Width();
|
|
nInitPosX += nMirrorW - nOneX;
|
|
}
|
|
tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
|
|
|
|
tools::Rectangle aClipRect( Point(nScrX, nScrY), Size(nScrW, nScrH) );
|
|
if (bMetaFile)
|
|
{
|
|
rRenderContext.Push();
|
|
rRenderContext.IntersectClipRegion( aClipRect );
|
|
}
|
|
else
|
|
rRenderContext.SetClipRegion( vcl::Region( aClipRect ) );
|
|
|
|
std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(CreateProcessor2D( ));
|
|
tools::Long nPosY = nScrY;
|
|
for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++)
|
|
{
|
|
// Rotated is also drawn one line above/below Changed if parts extend into the cell
|
|
|
|
RowInfo& rPrevRowInfo = pRowInfo[nArrY-1];
|
|
RowInfo& rThisRowInfo = pRowInfo[nArrY];
|
|
RowInfo& rNextRowInfo = pRowInfo[nArrY+1];
|
|
|
|
tools::Long nRowHeight = rThisRowInfo.nHeight;
|
|
if ( rThisRowInfo.nRotMaxCol != SC_ROTMAX_NONE &&
|
|
( rThisRowInfo.bChanged || rPrevRowInfo.bChanged ||
|
|
( nArrY+1<nArrCount && rNextRowInfo.bChanged ) ) )
|
|
{
|
|
SCROW nY = rThisRowInfo.nRowNo;
|
|
tools::Long nPosX = 0;
|
|
SCCOL nX;
|
|
for (nX=0; nX<=nRotMax; nX++)
|
|
{
|
|
if (nX==nX1) nPosX = nInitPosX; // calculated individually for preceding positions
|
|
|
|
ScCellInfo* pInfo = &rThisRowInfo.cellInfo(nX);
|
|
tools::Long nColWidth = pRowInfo[0].basicCellInfo(nX).nWidth;
|
|
if ( pInfo->nRotateDir > ScRotateDir::Standard &&
|
|
!pInfo->bHOverlapped && !pInfo->bVOverlapped )
|
|
{
|
|
pPattern = pInfo->pPatternAttr;
|
|
pCondSet = pInfo->pConditionSet;
|
|
if (!pPattern)
|
|
{
|
|
pPattern = mpDoc->GetPattern( nX, nY, nTab );
|
|
pInfo->pPatternAttr = pPattern;
|
|
pCondSet = mpDoc->GetCondResult( nX, nY, nTab );
|
|
pInfo->pConditionSet = pCondSet;
|
|
}
|
|
|
|
//! LastPattern etc.
|
|
|
|
Degree100 nAttrRotate = pPattern->GetRotateVal( pCondSet );
|
|
SvxRotateMode eRotMode =
|
|
pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue();
|
|
|
|
if (nAttrRotate)
|
|
{
|
|
if (nX < nX1) // compute negative position
|
|
{
|
|
nPosX = nInitPosX;
|
|
SCCOL nCol = nX1;
|
|
while (nCol > nX)
|
|
{
|
|
--nCol;
|
|
nPosX -= nLayoutSign * static_cast<tools::Long>(pRowInfo[0].basicCellInfo(nCol).nWidth);
|
|
}
|
|
}
|
|
|
|
// start position minus 1 so rotated backgrounds suit the border
|
|
// (border is on the grid)
|
|
|
|
tools::Long nTop = nPosY - 1;
|
|
tools::Long nBottom = nPosY + nRowHeight - 1;
|
|
tools::Long nTopLeft = nPosX - nLayoutSign;
|
|
tools::Long nTopRight = nPosX + (nColWidth - 1) * nLayoutSign;
|
|
tools::Long nBotLeft = nTopLeft;
|
|
tools::Long nBotRight = nTopRight;
|
|
|
|
// inclusion of the sign here hasn't been decided yet
|
|
// (if not, the extension of the non-rotated background must also be changed)
|
|
double nRealOrient = nLayoutSign * toRadians(nAttrRotate); // 1/100th degrees
|
|
double nCos = cos(nRealOrient);
|
|
double nSin = sin(nRealOrient);
|
|
//! restrict !!!
|
|
tools::Long nSkew = static_cast<tools::Long>(nRowHeight * nCos / nSin);
|
|
|
|
switch (eRotMode)
|
|
{
|
|
case SVX_ROTATE_MODE_BOTTOM:
|
|
nTopLeft += nSkew;
|
|
nTopRight += nSkew;
|
|
break;
|
|
case SVX_ROTATE_MODE_CENTER:
|
|
nSkew /= 2;
|
|
nTopLeft += nSkew;
|
|
nTopRight += nSkew;
|
|
nBotLeft -= nSkew;
|
|
nBotRight -= nSkew;
|
|
break;
|
|
case SVX_ROTATE_MODE_TOP:
|
|
nBotLeft -= nSkew;
|
|
nBotRight -= nSkew;
|
|
break;
|
|
default:
|
|
{
|
|
// added to avoid warnings
|
|
}
|
|
}
|
|
|
|
Point aPoints[4];
|
|
aPoints[0] = Point(nTopLeft, nTop);
|
|
aPoints[1] = Point(nTopRight, nTop);
|
|
aPoints[2] = Point(nBotRight, nBottom);
|
|
aPoints[3] = Point(nBotLeft, nBottom);
|
|
|
|
const SvxBrushItem* pBackground(static_cast<const SvxBrushItem*>(pInfo->maBackground.getItem()));
|
|
if (!pBackground)
|
|
pBackground = &pPattern->GetItem(ATTR_BACKGROUND, pCondSet);
|
|
if (bCellContrast)
|
|
{
|
|
// high contrast for cell borders and backgrounds -> empty background
|
|
pBackground = ScGlobal::GetEmptyBrushItem();
|
|
}
|
|
if (!pInfo->mxColorScale)
|
|
{
|
|
const Color& rColor = pBackground->GetColor();
|
|
if (rColor.GetAlpha() != 0)
|
|
{
|
|
// draw background only for the changed row itself
|
|
// (background doesn't extend into other cells).
|
|
// For the borders (rotated and normal), clipping should be
|
|
// set if the row isn't changed, but at least the borders
|
|
// don't cover the cell contents.
|
|
if (rThisRowInfo.bChanged)
|
|
{
|
|
tools::Polygon aPoly(4, aPoints);
|
|
|
|
// for DrawPolygon, without Pen one pixel is left out
|
|
// to the right and below...
|
|
if (!rColor.IsTransparent())
|
|
rRenderContext.SetLineColor(rColor);
|
|
else
|
|
rRenderContext.SetLineColor();
|
|
rRenderContext.SetFillColor(rColor);
|
|
rRenderContext.DrawPolygon(aPoly);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tools::Polygon aPoly(4, aPoints);
|
|
std::optional<Color> const & pColor = pInfo->mxColorScale;
|
|
|
|
// for DrawPolygon, without Pen one pixel is left out
|
|
// to the right and below...
|
|
if (!pColor->IsTransparent())
|
|
rRenderContext.SetLineColor(*pColor);
|
|
else
|
|
rRenderContext.SetLineColor();
|
|
rRenderContext.SetFillColor(*pColor);
|
|
rRenderContext.DrawPolygon(aPoly);
|
|
|
|
}
|
|
}
|
|
}
|
|
nPosX += nColWidth * nLayoutSign;
|
|
}
|
|
}
|
|
nPosY += nRowHeight;
|
|
}
|
|
|
|
pProcessor.reset();
|
|
|
|
if (bMetaFile)
|
|
rRenderContext.Pop();
|
|
else
|
|
rRenderContext.SetClipRegion();
|
|
}
|
|
|
|
std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> ScOutputData::CreateProcessor2D( )
|
|
{
|
|
mpDoc->InitDrawLayer(mpDoc->GetDocumentShell());
|
|
ScDrawLayer* pDrawLayer = mpDoc->GetDrawLayer();
|
|
if (!pDrawLayer)
|
|
return nullptr;
|
|
|
|
basegfx::B2DRange aViewRange;
|
|
SdrPage *pDrawPage = pDrawLayer->GetPage( static_cast< sal_uInt16 >( nTab ) );
|
|
drawinglayer::geometry::ViewInformation2D aNewViewInfos;
|
|
aNewViewInfos.setViewTransformation(mpDev->GetViewTransformation());
|
|
aNewViewInfos.setViewport(aViewRange);
|
|
aNewViewInfos.setVisualizedPage(GetXDrawPageForSdrPage( pDrawPage ));
|
|
|
|
return drawinglayer::processor2d::createProcessor2DFromOutputDevice(
|
|
*mpDev, aNewViewInfos );
|
|
}
|
|
|
|
// Printer
|
|
|
|
vcl::Region ScOutputData::GetChangedAreaRegion()
|
|
{
|
|
vcl::Region aRegion;
|
|
tools::Rectangle aDrawingRect;
|
|
bool bHad(false);
|
|
tools::Long nPosY = nScrY;
|
|
SCSIZE nArrY;
|
|
|
|
aDrawingRect.SetLeft( nScrX );
|
|
aDrawingRect.SetRight( nScrX+nScrW-1 );
|
|
|
|
for(nArrY=1; nArrY+1<nArrCount; nArrY++)
|
|
{
|
|
RowInfo* pThisRowInfo = &pRowInfo[nArrY];
|
|
|
|
if(pThisRowInfo->bChanged)
|
|
{
|
|
if(!bHad)
|
|
{
|
|
aDrawingRect.SetTop( nPosY );
|
|
bHad = true;
|
|
}
|
|
|
|
aDrawingRect.SetBottom( nPosY + pRowInfo[nArrY].nHeight - 1 );
|
|
}
|
|
else if(bHad)
|
|
{
|
|
aRegion.Union(mpDev->PixelToLogic(aDrawingRect));
|
|
bHad = false;
|
|
}
|
|
|
|
nPosY += pRowInfo[nArrY].nHeight;
|
|
}
|
|
|
|
if(bHad)
|
|
{
|
|
aRegion.Union(mpDev->PixelToLogic(aDrawingRect));
|
|
}
|
|
|
|
return aRegion;
|
|
}
|
|
|
|
bool ScOutputData::SetChangedClip()
|
|
{
|
|
tools::PolyPolygon aPoly;
|
|
|
|
tools::Rectangle aDrawingRect;
|
|
aDrawingRect.SetLeft( nScrX );
|
|
aDrawingRect.SetRight( nScrX+nScrW-1 );
|
|
|
|
bool bHad = false;
|
|
tools::Long nPosY = nScrY;
|
|
SCSIZE nArrY;
|
|
for (nArrY=1; nArrY+1<nArrCount; nArrY++)
|
|
{
|
|
RowInfo* pThisRowInfo = &pRowInfo[nArrY];
|
|
|
|
if ( pThisRowInfo->bChanged )
|
|
{
|
|
if (!bHad)
|
|
{
|
|
aDrawingRect.SetTop( nPosY );
|
|
bHad = true;
|
|
}
|
|
aDrawingRect.SetBottom( nPosY + pRowInfo[nArrY].nHeight - 1 );
|
|
}
|
|
else if (bHad)
|
|
{
|
|
aPoly.Insert( tools::Polygon( mpDev->PixelToLogic(aDrawingRect) ) );
|
|
bHad = false;
|
|
}
|
|
nPosY += pRowInfo[nArrY].nHeight;
|
|
}
|
|
|
|
if (bHad)
|
|
aPoly.Insert( tools::Polygon( mpDev->PixelToLogic(aDrawingRect) ) );
|
|
|
|
bool bRet = (aPoly.Count() != 0);
|
|
if (bRet)
|
|
mpDev->SetClipRegion(vcl::Region(aPoly));
|
|
return bRet;
|
|
}
|
|
|
|
void ScOutputData::FindChanged()
|
|
{
|
|
SCCOL nX;
|
|
SCSIZE nArrY;
|
|
|
|
bool bWasIdleEnabled = mpDoc->IsIdleEnabled();
|
|
mpDoc->EnableIdle(false);
|
|
for (nArrY=0; nArrY<nArrCount; nArrY++)
|
|
pRowInfo[nArrY].bChanged = false;
|
|
|
|
SCCOL nCol1 = mpDoc->MaxCol(), nCol2 = 0;
|
|
SCROW nRow1 = mpDoc->MaxRow(), nRow2 = 0;
|
|
bool bAnyDirty = false;
|
|
bool bAnyChanged = false;
|
|
|
|
for (nArrY=0; nArrY<nArrCount; nArrY++)
|
|
{
|
|
RowInfo* pThisRowInfo = &pRowInfo[nArrY];
|
|
for (nX=nX1; nX<=nX2; nX++)
|
|
{
|
|
const ScRefCellValue& rCell = pThisRowInfo->cellInfo(nX).maCell;
|
|
|
|
if (rCell.getType() != CELLTYPE_FORMULA)
|
|
continue;
|
|
|
|
ScFormulaCell* pFCell = rCell.getFormula();
|
|
if (pFCell->IsRunning())
|
|
// still being interpreted. Skip it.
|
|
continue;
|
|
|
|
bool bDirty = pFCell->GetDirty();
|
|
bAnyChanged = bAnyChanged || pFCell->IsChanged();
|
|
|
|
if (bDirty)
|
|
{
|
|
if (!bAnyDirty)
|
|
{
|
|
ScProgress::CreateInterpretProgress(mpDoc);
|
|
bAnyDirty = true;
|
|
}
|
|
|
|
ScAddress& rPos(pFCell->aPos);
|
|
nCol1 = std::min(rPos.Col(), nCol1);
|
|
nCol2 = std::max(rPos.Col(), nCol2);
|
|
nRow1 = std::min(rPos.Row(), nRow1);
|
|
nRow2 = std::max(rPos.Row(), nRow2);
|
|
|
|
const SfxUInt32Item* pItem = mpDoc->GetAttr(rPos, ATTR_VALIDDATA);
|
|
const ScValidationData* pData = mpDoc->GetValidationEntry(pItem->GetValue());
|
|
if (pData)
|
|
{
|
|
ScRefCellValue aCell(*mpDoc, rPos);
|
|
if (pData->IsDataValid(aCell, rPos))
|
|
ScDetectiveFunc(*mpDoc, rPos.Tab()).DeleteCirclesAt(rPos.Col(), rPos.Row());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bAnyDirty || bAnyChanged)
|
|
{
|
|
if (bAnyDirty)
|
|
mpDoc->EnsureFormulaCellResults(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab), true);
|
|
|
|
for (nArrY=0; nArrY<nArrCount; nArrY++)
|
|
{
|
|
RowInfo* pThisRowInfo = &pRowInfo[nArrY];
|
|
for (nX=nX1; nX<=nX2; nX++)
|
|
{
|
|
const ScRefCellValue& rCell = pThisRowInfo->cellInfo(nX).maCell;
|
|
|
|
if (rCell.getType() != CELLTYPE_FORMULA)
|
|
continue;
|
|
|
|
ScFormulaCell* pFCell = rCell.getFormula();
|
|
if (pFCell->IsRunning())
|
|
// still being interpreted. Skip it.
|
|
continue;
|
|
|
|
if (!pFCell->IsChanged())
|
|
// the result hasn't changed. Skip it.
|
|
continue;
|
|
|
|
pThisRowInfo->bChanged = true;
|
|
if ( pThisRowInfo->cellInfo(nX).bMerged )
|
|
{
|
|
SCSIZE nOverY = nArrY + 1;
|
|
while ( nOverY<nArrCount &&
|
|
pRowInfo[nOverY].cellInfo(nX).bVOverlapped )
|
|
{
|
|
pRowInfo[nOverY].bChanged = true;
|
|
++nOverY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bAnyDirty)
|
|
ScProgress::DeleteInterpretProgress();
|
|
}
|
|
|
|
mpDoc->EnableIdle(bWasIdleEnabled);
|
|
}
|
|
|
|
ReferenceMark ScOutputData::FillReferenceMark( SCCOL nRefStartX, SCROW nRefStartY,
|
|
SCCOL nRefEndX, SCROW nRefEndY, const Color& rColor)
|
|
{
|
|
ReferenceMark aResult;
|
|
|
|
PutInOrder( nRefStartX, nRefEndX );
|
|
PutInOrder( nRefStartY, nRefEndY );
|
|
|
|
if ( nRefStartX == nRefEndX && nRefStartY == nRefEndY )
|
|
mpDoc->ExtendMerge( nRefStartX, nRefStartY, nRefEndX, nRefEndY, nTab );
|
|
|
|
if ( nRefStartX <= nVisX2 && nRefEndX >= nVisX1 &&
|
|
nRefStartY <= nVisY2 && nRefEndY >= nVisY1 )
|
|
{
|
|
tools::Long nMinX = nScrX;
|
|
tools::Long nMinY = nScrY;
|
|
tools::Long nMaxX = nScrX + nScrW - 1;
|
|
tools::Long nMaxY = nScrY + nScrH - 1;
|
|
if ( bLayoutRTL )
|
|
std::swap( nMinX, nMaxX );
|
|
tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
|
|
|
|
bool bTop = false;
|
|
bool bBottom = false;
|
|
bool bLeft = false;
|
|
bool bRight = false;
|
|
|
|
tools::Long nPosY = nScrY;
|
|
bool bNoStartY = ( nY1 < nRefStartY );
|
|
bool bNoEndY = false;
|
|
for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++) // loop to end for bNoEndY check
|
|
{
|
|
SCROW nY = pRowInfo[nArrY].nRowNo;
|
|
|
|
if ( nY==nRefStartY || (nY>nRefStartY && bNoStartY) )
|
|
{
|
|
nMinY = nPosY;
|
|
bTop = true;
|
|
}
|
|
if ( nY==nRefEndY )
|
|
{
|
|
nMaxY = nPosY + pRowInfo[nArrY].nHeight - 2;
|
|
bBottom = true;
|
|
}
|
|
if ( nY>nRefEndY && bNoEndY )
|
|
{
|
|
nMaxY = nPosY-2;
|
|
bBottom = true;
|
|
}
|
|
bNoStartY = ( nY < nRefStartY );
|
|
bNoEndY = ( nY < nRefEndY );
|
|
nPosY += pRowInfo[nArrY].nHeight;
|
|
}
|
|
|
|
tools::Long nPosX = nScrX;
|
|
if ( bLayoutRTL )
|
|
nPosX += nMirrorW - 1; // always in pixels
|
|
|
|
for (SCCOL nX=nX1; nX<=nX2; nX++)
|
|
{
|
|
if ( nX==nRefStartX )
|
|
{
|
|
nMinX = nPosX;
|
|
bLeft = true;
|
|
}
|
|
if ( nX==nRefEndX )
|
|
{
|
|
nMaxX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - 2 ) * nLayoutSign;
|
|
bRight = true;
|
|
}
|
|
nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
|
|
}
|
|
|
|
if (bTop && bBottom && bLeft && bRight)
|
|
{
|
|
// mnPPT[XY] already has the factor aZoom[XY] in it.
|
|
aResult = ReferenceMark( nMinX / mnPPTX,
|
|
nMinY / mnPPTY,
|
|
( nMaxX - nMinX ) / mnPPTX,
|
|
( nMaxY - nMinY ) / mnPPTY,
|
|
nTab,
|
|
rColor );
|
|
}
|
|
}
|
|
|
|
return aResult;
|
|
}
|
|
|
|
void ScOutputData::DrawRefMark( SCCOL nRefStartX, SCROW nRefStartY,
|
|
SCCOL nRefEndX, SCROW nRefEndY,
|
|
const Color& rColor, bool bHandle )
|
|
{
|
|
PutInOrder( nRefStartX, nRefEndX );
|
|
PutInOrder( nRefStartY, nRefEndY );
|
|
|
|
if ( nRefStartX == nRefEndX && nRefStartY == nRefEndY )
|
|
mpDoc->ExtendMerge( nRefStartX, nRefStartY, nRefEndX, nRefEndY, nTab );
|
|
else if (mpDoc->ValidCol(nRefEndX) && mpDoc->ValidRow(nRefEndY) &&
|
|
mpDoc->HasAttrib(nRefEndX, nRefEndY, nTab, HasAttrFlags::Merged))
|
|
mpDoc->ExtendMerge(nRefEndX, nRefEndY, nRefEndX, nRefEndY, nTab);
|
|
|
|
if ( !(nRefStartX <= nVisX2 && nRefEndX >= nVisX1 &&
|
|
nRefStartY <= nVisY2 && nRefEndY >= nVisY1) )
|
|
return;
|
|
|
|
tools::Long nMinX = nScrX;
|
|
tools::Long nMinY = nScrY;
|
|
tools::Long nMaxX = nScrX + nScrW - 1;
|
|
tools::Long nMaxY = nScrY + nScrH - 1;
|
|
if ( bLayoutRTL )
|
|
std::swap( nMinX, nMaxX );
|
|
tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
|
|
|
|
bool bTop = false;
|
|
bool bBottom = false;
|
|
bool bLeft = false;
|
|
bool bRight = false;
|
|
|
|
tools::Long nPosY = nScrY;
|
|
bool bNoStartY = ( nY1 < nRefStartY );
|
|
bool bNoEndY = false;
|
|
for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++) // loop to end for bNoEndY check
|
|
{
|
|
SCROW nY = pRowInfo[nArrY].nRowNo;
|
|
|
|
if ( nY==nRefStartY || (nY>nRefStartY && bNoStartY) )
|
|
{
|
|
nMinY = nPosY;
|
|
bTop = true;
|
|
}
|
|
if ( nY==nRefEndY )
|
|
{
|
|
nMaxY = nPosY + pRowInfo[nArrY].nHeight - 2;
|
|
bBottom = true;
|
|
}
|
|
if ( nY>nRefEndY && bNoEndY )
|
|
{
|
|
nMaxY = nPosY-2;
|
|
bBottom = true;
|
|
}
|
|
bNoStartY = ( nY < nRefStartY );
|
|
bNoEndY = ( nY < nRefEndY );
|
|
nPosY += pRowInfo[nArrY].nHeight;
|
|
}
|
|
|
|
tools::Long nPosX = nScrX;
|
|
if ( bLayoutRTL )
|
|
nPosX += nMirrorW - 1; // always in pixels
|
|
|
|
for (SCCOL nX=nX1; nX<=nX2; nX++)
|
|
{
|
|
if ( nX==nRefStartX )
|
|
{
|
|
nMinX = nPosX;
|
|
bLeft = true;
|
|
}
|
|
if ( nX==nRefEndX )
|
|
{
|
|
nMaxX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - 2 ) * nLayoutSign;
|
|
bRight = true;
|
|
}
|
|
nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
|
|
}
|
|
|
|
if ( nMaxX * nLayoutSign < nMinX * nLayoutSign || nMaxY < nMinY )
|
|
return;
|
|
|
|
mpDev->SetLineColor( rColor );
|
|
if (bTop && bBottom && bLeft && bRight && !comphelper::LibreOfficeKit::isActive() )
|
|
{
|
|
mpDev->SetFillColor();
|
|
mpDev->DrawRect( tools::Rectangle( nMinX, nMinY, nMaxX, nMaxY ) );
|
|
}
|
|
else if ( !comphelper::LibreOfficeKit::isActive() )
|
|
{
|
|
if (bTop)
|
|
mpDev->DrawLine( Point( nMinX, nMinY ), Point( nMaxX, nMinY ) );
|
|
if (bBottom)
|
|
mpDev->DrawLine( Point( nMinX, nMaxY ), Point( nMaxX, nMaxY ) );
|
|
if (bLeft)
|
|
mpDev->DrawLine( Point( nMinX, nMinY ), Point( nMinX, nMaxY ) );
|
|
if (bRight)
|
|
mpDev->DrawLine( Point( nMaxX, nMinY ), Point( nMaxX, nMaxY ) );
|
|
}
|
|
if ( !bHandle || !bRight || !bBottom || comphelper::LibreOfficeKit::isActive() )
|
|
return;
|
|
|
|
mpDev->SetLineColor( rColor );
|
|
mpDev->SetFillColor( rColor );
|
|
|
|
const sal_Int32 aRadius = 4;
|
|
|
|
sal_Int32 aRectMaxX1 = nMaxX - nLayoutSign * aRadius;
|
|
sal_Int32 aRectMaxX2 = nMaxX + nLayoutSign;
|
|
sal_Int32 aRectMinX1 = nMinX - nLayoutSign;
|
|
sal_Int32 aRectMinX2 = nMinX + nLayoutSign * aRadius;
|
|
|
|
sal_Int32 aRectMaxY1 = nMaxY - aRadius;
|
|
sal_Int32 aRectMaxY2 = nMaxY + 1;
|
|
sal_Int32 aRectMinY1 = nMinY - 1;
|
|
sal_Int32 aRectMinY2 = nMinY + aRadius;
|
|
|
|
// Draw corner rectangles
|
|
tools::Rectangle aLowerRight( aRectMaxX1, aRectMaxY1, aRectMaxX2, aRectMaxY2 );
|
|
tools::Rectangle aUpperLeft ( aRectMinX1, aRectMinY1, aRectMinX2, aRectMinY2 );
|
|
tools::Rectangle aLowerLeft ( aRectMinX1, aRectMaxY1, aRectMinX2, aRectMaxY2 );
|
|
tools::Rectangle aUpperRight( aRectMaxX1, aRectMinY1, aRectMaxX2, aRectMinY2 );
|
|
|
|
mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aLowerRight ) ), lclCornerRectTransparency );
|
|
mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aUpperLeft ) ), lclCornerRectTransparency );
|
|
mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aLowerLeft ) ), lclCornerRectTransparency );
|
|
mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aUpperRight ) ), lclCornerRectTransparency );
|
|
}
|
|
|
|
void ScOutputData::DrawOneChange( SCCOL nRefStartX, SCROW nRefStartY,
|
|
SCCOL nRefEndX, SCROW nRefEndY,
|
|
const Color& rColor, sal_uInt16 nType )
|
|
{
|
|
PutInOrder( nRefStartX, nRefEndX );
|
|
PutInOrder( nRefStartY, nRefEndY );
|
|
|
|
if ( nRefStartX == nRefEndX && nRefStartY == nRefEndY )
|
|
mpDoc->ExtendMerge( nRefStartX, nRefStartY, nRefEndX, nRefEndY, nTab );
|
|
|
|
if ( !(nRefStartX <= nVisX2 + 1 && nRefEndX >= nVisX1 &&
|
|
nRefStartY <= nVisY2 + 1 && nRefEndY >= nVisY1) ) // +1 because it touches next cells left/top
|
|
return;
|
|
|
|
tools::Long nMinX = nScrX;
|
|
tools::Long nMinY = nScrY;
|
|
tools::Long nMaxX = nScrX+nScrW-1;
|
|
tools::Long nMaxY = nScrY+nScrH-1;
|
|
if ( bLayoutRTL )
|
|
std::swap( nMinX, nMaxX );
|
|
tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
|
|
|
|
bool bTop = false;
|
|
bool bBottom = false;
|
|
bool bLeft = false;
|
|
bool bRight = false;
|
|
|
|
tools::Long nPosY = nScrY;
|
|
bool bNoStartY = ( nY1 < nRefStartY );
|
|
bool bNoEndY = false;
|
|
for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++) // loop to end for bNoEndY check
|
|
{
|
|
SCROW nY = pRowInfo[nArrY].nRowNo;
|
|
|
|
if ( nY==nRefStartY || (nY>nRefStartY && bNoStartY) )
|
|
{
|
|
nMinY = nPosY - 1;
|
|
bTop = true;
|
|
}
|
|
if ( nY==nRefEndY )
|
|
{
|
|
nMaxY = nPosY + pRowInfo[nArrY].nHeight - 1;
|
|
bBottom = true;
|
|
}
|
|
if ( nY>nRefEndY && bNoEndY )
|
|
{
|
|
nMaxY = nPosY - 1;
|
|
bBottom = true;
|
|
}
|
|
bNoStartY = ( nY < nRefStartY );
|
|
bNoEndY = ( nY < nRefEndY );
|
|
nPosY += pRowInfo[nArrY].nHeight;
|
|
}
|
|
|
|
tools::Long nPosX = nScrX;
|
|
if ( bLayoutRTL )
|
|
nPosX += nMirrorW - 1; // always in pixels
|
|
|
|
for (SCCOL nX=nX1; nX<=nX2+1; nX++)
|
|
{
|
|
if ( nX==nRefStartX )
|
|
{
|
|
nMinX = nPosX - nLayoutSign;
|
|
bLeft = true;
|
|
}
|
|
if ( nX==nRefEndX )
|
|
{
|
|
nMaxX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - 1 ) * nLayoutSign;
|
|
bRight = true;
|
|
}
|
|
nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
|
|
}
|
|
|
|
if ( nMaxX * nLayoutSign < nMinX * nLayoutSign || nMaxY < nMinY )
|
|
return;
|
|
|
|
if ( nType == SC_CAT_DELETE_ROWS )
|
|
bLeft = bRight = bBottom = false; //! thick lines???
|
|
else if ( nType == SC_CAT_DELETE_COLS )
|
|
bTop = bBottom = bRight = false; //! thick lines???
|
|
|
|
mpDev->SetLineColor( rColor );
|
|
if (bTop && bBottom && bLeft && bRight)
|
|
{
|
|
mpDev->SetFillColor();
|
|
mpDev->DrawRect( tools::Rectangle( nMinX, nMinY, nMaxX, nMaxY ) );
|
|
}
|
|
else
|
|
{
|
|
if (bTop)
|
|
{
|
|
mpDev->DrawLine( Point( nMinX,nMinY ), Point( nMaxX,nMinY ) );
|
|
if ( nType == SC_CAT_DELETE_ROWS )
|
|
mpDev->DrawLine( Point( nMinX,nMinY+1 ), Point( nMaxX,nMinY+1 ) );
|
|
}
|
|
if (bBottom)
|
|
mpDev->DrawLine( Point( nMinX,nMaxY ), Point( nMaxX,nMaxY ) );
|
|
if (bLeft)
|
|
{
|
|
mpDev->DrawLine( Point( nMinX,nMinY ), Point( nMinX,nMaxY ) );
|
|
if ( nType == SC_CAT_DELETE_COLS )
|
|
mpDev->DrawLine( Point( nMinX+nLayoutSign,nMinY ), Point( nMinX+nLayoutSign,nMaxY ) );
|
|
}
|
|
if (bRight)
|
|
mpDev->DrawLine( Point( nMaxX,nMinY ), Point( nMaxX,nMaxY ) );
|
|
}
|
|
if ( bLeft && bTop )
|
|
{
|
|
mpDev->SetLineColor();
|
|
mpDev->SetFillColor( rColor );
|
|
mpDev->DrawRect( tools::Rectangle( nMinX+nLayoutSign, nMinY+1, nMinX+3*nLayoutSign, nMinY+3 ) );
|
|
}
|
|
}
|
|
|
|
void ScOutputData::DrawChangeTrack()
|
|
{
|
|
ScChangeTrack* pTrack = mpDoc->GetChangeTrack();
|
|
ScChangeViewSettings* pSettings = mpDoc->GetChangeViewSettings();
|
|
if ( !pTrack || !pTrack->GetFirst() || !pSettings || !pSettings->ShowChanges() )
|
|
return; // nothing there or hidden
|
|
|
|
ScActionColorChanger aColorChanger(*pTrack);
|
|
|
|
// clipping happens from the outside
|
|
//! without clipping, only paint affected cells ??!??!?
|
|
|
|
SCCOL nEndX = nX2;
|
|
SCROW nEndY = nY2;
|
|
if ( nEndX < mpDoc->MaxCol() ) ++nEndX; // also from the next cell since the mark
|
|
if ( nEndY < mpDoc->MaxRow() ) ++nEndY; // protrudes from the preceding cell
|
|
ScRange aViewRange( nX1, nY1, nTab, nEndX, nEndY, nTab );
|
|
const ScChangeAction* pAction = pTrack->GetFirst();
|
|
while (pAction)
|
|
{
|
|
if ( pAction->IsVisible() )
|
|
{
|
|
ScChangeActionType eActionType = pAction->GetType();
|
|
const ScBigRange& rBig = pAction->GetBigRange();
|
|
if ( rBig.aStart.Tab() == nTab )
|
|
{
|
|
ScRange aRange = rBig.MakeRange( *mpDoc );
|
|
|
|
if ( eActionType == SC_CAT_DELETE_ROWS )
|
|
aRange.aEnd.SetRow( aRange.aStart.Row() );
|
|
else if ( eActionType == SC_CAT_DELETE_COLS )
|
|
aRange.aEnd.SetCol( aRange.aStart.Col() );
|
|
|
|
if ( aRange.Intersects( aViewRange ) &&
|
|
ScViewUtil::IsActionShown( *pAction, *pSettings, *mpDoc ) )
|
|
{
|
|
aColorChanger.Update( *pAction );
|
|
Color aColor( aColorChanger.GetColor() );
|
|
DrawOneChange( aRange.aStart.Col(), aRange.aStart.Row(),
|
|
aRange.aEnd.Col(), aRange.aEnd.Row(), aColor, sal::static_int_cast<sal_uInt16>(eActionType) );
|
|
|
|
}
|
|
}
|
|
if ( eActionType == SC_CAT_MOVE &&
|
|
static_cast<const ScChangeActionMove*>(pAction)->
|
|
GetFromRange().aStart.Tab() == nTab )
|
|
{
|
|
ScRange aRange = static_cast<const ScChangeActionMove*>(pAction)->
|
|
GetFromRange().MakeRange( *mpDoc );
|
|
if ( aRange.Intersects( aViewRange ) &&
|
|
ScViewUtil::IsActionShown( *pAction, *pSettings, *mpDoc ) )
|
|
{
|
|
aColorChanger.Update( *pAction );
|
|
Color aColor( aColorChanger.GetColor() );
|
|
DrawOneChange( aRange.aStart.Col(), aRange.aStart.Row(),
|
|
aRange.aEnd.Col(), aRange.aEnd.Row(), aColor, sal::static_int_cast<sal_uInt16>(eActionType) );
|
|
}
|
|
}
|
|
}
|
|
|
|
pAction = pAction->GetNext();
|
|
}
|
|
}
|
|
|
|
void ScOutputData::DrawSparklines(vcl::RenderContext& rRenderContext)
|
|
{
|
|
Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
|
|
tools::Long nOneXLogic = aOnePixel.Width();
|
|
tools::Long nOneYLogic = aOnePixel.Height();
|
|
|
|
// See more about bWorksInPixels in ScOutputData::DrawGrid
|
|
bool bWorksInPixels = false;
|
|
if (eType == OUTTYPE_WINDOW)
|
|
bWorksInPixels = true;
|
|
|
|
tools::Long nOneX = 1;
|
|
tools::Long nOneY = 1;
|
|
if (!bWorksInPixels)
|
|
{
|
|
nOneX = nOneXLogic;
|
|
nOneY = nOneYLogic;
|
|
}
|
|
|
|
tools::Long nInitPosX = nScrX;
|
|
if ( bLayoutRTL )
|
|
nInitPosX += nMirrorW - 1; // always in pixels
|
|
tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
|
|
|
|
tools::Long nPosY = nScrY;
|
|
for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
|
|
{
|
|
RowInfo* pThisRowInfo = &pRowInfo[nArrY];
|
|
if ( pThisRowInfo->bChanged )
|
|
{
|
|
tools::Long nPosX = nInitPosX;
|
|
for (SCCOL nX=nX1; nX<=nX2; nX++)
|
|
{
|
|
ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
|
|
bool bIsMerged = false;
|
|
|
|
if ( nX==nX1 && pInfo->bHOverlapped && !pInfo->bVOverlapped )
|
|
{
|
|
// find start of merged cell
|
|
bIsMerged = true;
|
|
SCROW nY = pRowInfo[nArrY].nRowNo;
|
|
SCCOL nMergeX = nX;
|
|
SCROW nMergeY = nY;
|
|
mpDoc->ExtendOverlapped( nMergeX, nMergeY, nX, nY, nTab );
|
|
}
|
|
|
|
std::shared_ptr<sc::Sparkline> pSparkline;
|
|
ScAddress aCurrentAddress(nX, pRowInfo[nArrY].nRowNo, nTab);
|
|
|
|
if (!mpDoc->ColHidden(nX, nTab) && (pSparkline = mpDoc->GetSparkline(aCurrentAddress))
|
|
&& (bIsMerged || (!pInfo->bHOverlapped && !pInfo->bVOverlapped)))
|
|
{
|
|
const tools::Long nWidth = pRowInfo[0].basicCellInfo(nX).nWidth;
|
|
const tools::Long nHeight = pThisRowInfo->nHeight;
|
|
|
|
Point aPoint(nPosX, nPosY);
|
|
Size aSize(nWidth, nHeight);
|
|
|
|
sc::SparklineRenderer renderer(*mpDoc);
|
|
renderer.render(pSparkline, rRenderContext, tools::Rectangle(aPoint, aSize), nOneX, nOneY, double(aZoomX), double(aZoomY));
|
|
}
|
|
|
|
nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
|
|
}
|
|
}
|
|
nPosY += pThisRowInfo->nHeight;
|
|
}
|
|
|
|
}
|
|
|
|
//TODO: moggi Need to check if this can't be written simpler
|
|
void ScOutputData::DrawNoteMarks(vcl::RenderContext& rRenderContext)
|
|
{
|
|
// cool#6911 draw the note indicator browser-side instead
|
|
if (comphelper::LibreOfficeKit::isActive())
|
|
return;
|
|
|
|
tools::Long nInitPosX = nScrX;
|
|
if ( bLayoutRTL )
|
|
nInitPosX += nMirrorW - 1; // always in pixels
|
|
tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
|
|
|
|
tools::Long nPosY = nScrY - 1;
|
|
for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
|
|
{
|
|
RowInfo* pThisRowInfo = &pRowInfo[nArrY];
|
|
if ( pThisRowInfo->bChanged )
|
|
{
|
|
tools::Long nPosX = nInitPosX;
|
|
for (SCCOL nX=nX1; nX<=nX2; nX++)
|
|
{
|
|
ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
|
|
bool bIsMerged = false;
|
|
|
|
if ( nX==nX1 && pInfo->bHOverlapped && !pInfo->bVOverlapped )
|
|
{
|
|
// find start of merged cell
|
|
bIsMerged = true;
|
|
SCROW nY = pRowInfo[nArrY].nRowNo;
|
|
SCCOL nMergeX = nX;
|
|
SCROW nMergeY = nY;
|
|
mpDoc->ExtendOverlapped( nMergeX, nMergeY, nX, nY, nTab );
|
|
}
|
|
|
|
if (!mpDoc->ColHidden(nX, nTab) && mpDoc->GetNote(nX, pRowInfo[nArrY].nRowNo, nTab)
|
|
&& (bIsMerged || (!pInfo->bHOverlapped && !pInfo->bVOverlapped)))
|
|
{
|
|
ScModule* mod = ScModule::get();
|
|
rRenderContext.SetLineColor(mod->GetColorConfig().GetColorValue(svtools::CALCGRID).nColor);
|
|
|
|
const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
|
|
if ( mbUseStyleColor && rStyleSettings.GetHighContrastMode() )
|
|
rRenderContext.SetFillColor( mod->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor );
|
|
else
|
|
rRenderContext.SetFillColor( mod->GetColorConfig().GetColorValue(svtools::CALCCOMMENTS).nColor );
|
|
|
|
tools::Long nMarkX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - 1) * nLayoutSign;
|
|
if ( bIsMerged || pInfo->bMerged )
|
|
{
|
|
// if merged, add widths of all cells
|
|
SCCOL nNextX = nX + 1;
|
|
while ( nNextX <= nX2 + 1 && pThisRowInfo->cellInfo(nNextX).bHOverlapped )
|
|
{
|
|
nMarkX += pRowInfo[0].basicCellInfo(nNextX).nWidth * nLayoutSign;
|
|
++nNextX;
|
|
}
|
|
}
|
|
// DPI/ZOOM 100/100 => 6, 100/50 => 4.5, 100/150 => 7.5
|
|
// DPI/ZOOM 150/100 => 7.5, 150/50 => 6, 150/150 => 9
|
|
sal_Int16 nSize = officecfg::Office::Calc::Content::Display::NoteIndicator::get();
|
|
if (nSize < 1)
|
|
{
|
|
const double fSize(rRenderContext.GetDPIScaleFactor() * aZoomX * 3 + 3);
|
|
nSize = static_cast<sal_Int16>(fSize);
|
|
}
|
|
Point aPoints[3];
|
|
aPoints[0] = Point(nMarkX, nPosY);
|
|
aPoints[0].setX( bLayoutRTL ? aPoints[0].X() + nSize : aPoints[0].X() - nSize );
|
|
aPoints[1] = Point(nMarkX, nPosY);
|
|
aPoints[2] = Point(nMarkX, nPosY + nSize);
|
|
tools::Polygon aPoly(3, aPoints);
|
|
|
|
if ( bLayoutRTL ? ( nMarkX >= 0 ) : ( nMarkX < nScrX+nScrW ) )
|
|
rRenderContext.DrawPolygon(aPoly);
|
|
}
|
|
|
|
nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
|
|
}
|
|
}
|
|
nPosY += pThisRowInfo->nHeight;
|
|
}
|
|
}
|
|
|
|
void ScOutputData::DrawFormulaMarks(vcl::RenderContext& rRenderContext)
|
|
{
|
|
bool bFirst = true;
|
|
|
|
tools::Long nInitPosX = nScrX;
|
|
if ( bLayoutRTL )
|
|
nInitPosX += nMirrorW - 1; // always in pixels
|
|
tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
|
|
|
|
tools::Long nPosY = nScrY;
|
|
for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
|
|
{
|
|
RowInfo* pThisRowInfo = &pRowInfo[nArrY];
|
|
if ( pThisRowInfo->bChanged )
|
|
{
|
|
tools::Long nPosX = nInitPosX;
|
|
for (SCCOL nX=nX1; nX<=nX2; nX++)
|
|
{
|
|
ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
|
|
if (!mpDoc->ColHidden(nX, nTab) && !mpDoc->GetFormula(nX, pRowInfo[nArrY].nRowNo, nTab).isEmpty()
|
|
&& (!pInfo->bHOverlapped && !pInfo->bVOverlapped))
|
|
{
|
|
if (bFirst)
|
|
{
|
|
rRenderContext.SetLineColor(COL_WHITE);
|
|
|
|
const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
|
|
if ( mbUseStyleColor && rStyleSettings.GetHighContrastMode() )
|
|
rRenderContext.SetFillColor( ScModule::get()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor );
|
|
else
|
|
rRenderContext.SetFillColor(COL_LIGHTBLUE);
|
|
|
|
bFirst = false;
|
|
}
|
|
|
|
tools::Long nMarkX = nPosX;
|
|
tools::Long nMarkY = nPosY + pThisRowInfo->nHeight - 2;
|
|
if ( pInfo->bMerged )
|
|
{
|
|
for (SCSIZE nNextY=nArrY+1; nNextY+1<nArrCount; nNextY++)
|
|
{
|
|
bool bVOver;
|
|
if (pRowInfo[nNextY + 1].nRowNo == (pRowInfo[nNextY].nRowNo + 1)) {
|
|
bVOver = pRowInfo[nNextY].cellInfo(nX).bVOverlapped;
|
|
} else {
|
|
bVOver = mpDoc->GetAttr(nX,nNextY,nTab,ATTR_MERGE_FLAG)->IsVerOverlapped();
|
|
}
|
|
if (!bVOver) break;
|
|
nMarkY += pRowInfo[nNextY].nHeight;
|
|
}
|
|
}
|
|
// DPI/ZOOM 100/100 => 10, 100/50 => 7, 100/150 => 13
|
|
// DPI/ZOOM 150/100 => 13, 150/50 => 8.5, 150/150 => 17.5
|
|
const double nSize( rRenderContext.GetDPIScaleFactor() * aZoomX * 6 + 4);
|
|
Point aPoints[3];
|
|
aPoints[0] = Point(nMarkX, nMarkY);
|
|
aPoints[0].setX( bLayoutRTL ? aPoints[0].X() - nSize : aPoints[0].X() + nSize );
|
|
aPoints[1] = Point(nMarkX, nMarkY);
|
|
aPoints[2] = Point(nMarkX, nMarkY - nSize);
|
|
tools::Polygon aPoly(3, aPoints);
|
|
|
|
if ( bLayoutRTL ? ( nMarkX >= 0 ) : ( nMarkX < nScrX+nScrW ) )
|
|
rRenderContext.DrawPolygon(aPoly);
|
|
}
|
|
|
|
nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
|
|
}
|
|
}
|
|
nPosY += pThisRowInfo->nHeight;
|
|
}
|
|
}
|
|
|
|
void ScOutputData::AddPDFNotes()
|
|
{
|
|
vcl::PDFExtOutDevData* pPDFData = dynamic_cast< vcl::PDFExtOutDevData* >( mpDev->GetExtOutDevData() );
|
|
if ( !pPDFData || !pPDFData->GetIsExportNotes() )
|
|
return;
|
|
|
|
tools::Long nInitPosX = nScrX;
|
|
if ( bLayoutRTL )
|
|
{
|
|
Size aOnePixel = mpDev->PixelToLogic(Size(1,1));
|
|
tools::Long nOneX = aOnePixel.Width();
|
|
nInitPosX += nMirrorW - nOneX;
|
|
}
|
|
tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
|
|
|
|
tools::Long nPosY = nScrY;
|
|
for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
|
|
{
|
|
RowInfo* pThisRowInfo = &pRowInfo[nArrY];
|
|
if ( pThisRowInfo->bChanged )
|
|
{
|
|
tools::Long nPosX = nInitPosX;
|
|
for (SCCOL nX=nX1; nX<=nX2; nX++)
|
|
{
|
|
ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
|
|
bool bIsMerged = false;
|
|
SCROW nY = pRowInfo[nArrY].nRowNo;
|
|
SCCOL nMergeX = nX;
|
|
SCROW nMergeY = nY;
|
|
|
|
if ( nX==nX1 && pInfo->bHOverlapped && !pInfo->bVOverlapped )
|
|
{
|
|
// find start of merged cell
|
|
bIsMerged = true;
|
|
mpDoc->ExtendOverlapped( nMergeX, nMergeY, nX, nY, nTab );
|
|
// use origin's pCell for NotePtr test below
|
|
}
|
|
|
|
ScPostIt* pNote = mpDoc->GetNote(nMergeX, nMergeY, nTab);
|
|
|
|
if ( pNote && ( bIsMerged || ( !pInfo->bHOverlapped && !pInfo->bVOverlapped ) ) )
|
|
{
|
|
tools::Long nNoteWidth = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
|
|
tools::Long nNoteHeight = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTY );
|
|
|
|
tools::Long nMarkX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - nNoteWidth ) * nLayoutSign;
|
|
if ( bIsMerged || pInfo->bMerged )
|
|
{
|
|
// if merged, add widths of all cells
|
|
SCCOL nNextX = nX + 1;
|
|
while ( nNextX <= nX2 + 1 && pThisRowInfo->cellInfo(nNextX).bHOverlapped )
|
|
{
|
|
nMarkX += pRowInfo[0].basicCellInfo(nNextX).nWidth * nLayoutSign;
|
|
++nNextX;
|
|
}
|
|
}
|
|
if ( bLayoutRTL ? ( nMarkX >= 0 ) : ( nMarkX < nScrX+nScrW ) )
|
|
{
|
|
tools::Rectangle aNoteRect( nMarkX, nPosY, nMarkX+nNoteWidth*nLayoutSign, nPosY+nNoteHeight );
|
|
|
|
vcl::pdf::PDFNote aNote;
|
|
|
|
// Note title is the cell address (as on printed note pages)
|
|
ScAddress aAddress( nMergeX, nMergeY, nTab );
|
|
aNote.maTitle = aAddress.Format(ScRefFlags::VALID, mpDoc, mpDoc->GetAddressConvention());
|
|
|
|
// Content has to be a simple string without line breaks
|
|
OUString aContent = pNote->GetText();
|
|
aNote.maContents = aContent.replaceAll("\n", " ");
|
|
|
|
// If the caption is hidden, we need to show it to get its rectangle,
|
|
// then hide it again because it is also hidden in the file.
|
|
bool bShowCaption = pNote->IsCaptionShown();
|
|
if (!bShowCaption)
|
|
pNote->ShowCaption(aAddress, true);
|
|
|
|
SdrCaptionObj* pCaption = pNote->GetCaption();
|
|
tools::Rectangle aCaptionRect(pCaption->GetLogicRect());
|
|
Point aPoint(aCaptionRect.getX() + nScrX, aCaptionRect.getY() + nScrY);
|
|
Size aSize(aCaptionRect.GetWidth(), aCaptionRect.GetHeight());
|
|
tools::Rectangle aPopupRect(aPoint, aSize);
|
|
|
|
if (!bShowCaption)
|
|
pNote->ShowCaption(aAddress, false);
|
|
|
|
pPDFData->CreateNote(aNoteRect, aNote, aPopupRect);
|
|
}
|
|
}
|
|
|
|
nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
|
|
}
|
|
}
|
|
nPosY += pThisRowInfo->nHeight;
|
|
}
|
|
}
|
|
|
|
void ScOutputData::DrawClipMarks()
|
|
{
|
|
if (!bAnyClipped)
|
|
return;
|
|
|
|
ScModule* mod = ScModule::get();
|
|
Color aArrowFillCol(mod->GetColorConfig().GetColorValue(svtools::CALCTEXTOVERFLOW).nColor);
|
|
const bool bIsDarkBackground = mod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor.IsDark();
|
|
|
|
DrawModeFlags nOldDrawMode = mpDev->GetDrawMode();
|
|
|
|
tools::Long nInitPosX = nScrX;
|
|
if ( bLayoutRTL )
|
|
nInitPosX += nMirrorW - 1; // always in pixels
|
|
tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
|
|
|
|
tools::Rectangle aCellRect;
|
|
tools::Long nPosY = nScrY;
|
|
for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
|
|
{
|
|
RowInfo* pThisRowInfo = &pRowInfo[nArrY];
|
|
if ( pThisRowInfo->bChanged )
|
|
{
|
|
SCROW nY = pThisRowInfo->nRowNo;
|
|
tools::Long nPosX = nInitPosX;
|
|
for (SCCOL nX=nX1; nX<=nX2; nX++)
|
|
{
|
|
ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
|
|
if (pInfo->nClipMark != ScClipMark::NONE)
|
|
{
|
|
if (pInfo->bHOverlapped || pInfo->bVOverlapped)
|
|
{
|
|
// merge origin may be outside of visible area - use document functions
|
|
|
|
SCCOL nOverX = nX;
|
|
SCROW nOverY = nY;
|
|
tools::Long nStartPosX = nPosX;
|
|
tools::Long nStartPosY = nPosY;
|
|
|
|
while ( nOverX > 0 && ( mpDoc->GetAttr(
|
|
nOverX, nOverY, nTab, ATTR_MERGE_FLAG )->GetValue() & ScMF::Hor ) )
|
|
{
|
|
--nOverX;
|
|
nStartPosX -= nLayoutSign * static_cast<tools::Long>( mpDoc->GetColWidth(nOverX,nTab) * mnPPTX );
|
|
}
|
|
|
|
while ( nOverY > 0 && ( mpDoc->GetAttr(
|
|
nOverX, nOverY, nTab, ATTR_MERGE_FLAG )->GetValue() & ScMF::Ver ) )
|
|
{
|
|
--nOverY;
|
|
nStartPosY -= nLayoutSign * static_cast<tools::Long>( mpDoc->GetRowHeight(nOverY,nTab) * mnPPTY );
|
|
}
|
|
|
|
tools::Long nOutWidth = static_cast<tools::Long>( mpDoc->GetColWidth(nOverX,nTab) * mnPPTX );
|
|
tools::Long nOutHeight = static_cast<tools::Long>( mpDoc->GetRowHeight(nOverY,nTab) * mnPPTY );
|
|
|
|
const ScMergeAttr* pMerge = mpDoc->GetAttr( nOverX, nOverY, nTab, ATTR_MERGE );
|
|
SCCOL nCountX = pMerge->GetColMerge();
|
|
for (SCCOL i=1; i<nCountX; i++)
|
|
nOutWidth += mpDoc->GetColWidth(nOverX+i,nTab) * mnPPTX;
|
|
SCROW nCountY = pMerge->GetRowMerge();
|
|
nOutHeight += mpDoc->GetScaledRowHeight( nOverY+1, nOverY+nCountY-1, nTab, mnPPTY);
|
|
|
|
if ( bLayoutRTL )
|
|
nStartPosX -= nOutWidth - 1;
|
|
aCellRect = tools::Rectangle( Point( nStartPosX, nStartPosY ), Size( nOutWidth, nOutHeight ) );
|
|
}
|
|
else
|
|
{
|
|
tools::Long nOutWidth = pRowInfo[0].basicCellInfo(nX).nWidth;
|
|
tools::Long nOutHeight = pThisRowInfo->nHeight;
|
|
|
|
if ( pInfo->bMerged && pInfo->pPatternAttr )
|
|
{
|
|
SCCOL nOverX = nX;
|
|
SCROW nOverY = nY;
|
|
const ScMergeAttr* pMerge =
|
|
&pInfo->pPatternAttr->GetItem(ATTR_MERGE);
|
|
SCCOL nCountX = pMerge->GetColMerge();
|
|
for (SCCOL i=1; i<nCountX; i++)
|
|
nOutWidth += mpDoc->GetColWidth(nOverX+i,nTab) * mnPPTX;
|
|
SCROW nCountY = pMerge->GetRowMerge();
|
|
nOutHeight += mpDoc->GetScaledRowHeight( nOverY+1, nOverY+nCountY-1, nTab, mnPPTY);
|
|
}
|
|
|
|
tools::Long nStartPosX = nPosX;
|
|
if ( bLayoutRTL )
|
|
nStartPosX -= nOutWidth - 1;
|
|
// #i80447# create aCellRect from two points in case nOutWidth is 0
|
|
aCellRect = tools::Rectangle( Point( nStartPosX, nPosY ),
|
|
Point( nStartPosX+nOutWidth-1, nPosY+nOutHeight-1 ) );
|
|
}
|
|
|
|
aCellRect.AdjustBottom( -1 ); // don't paint over the cell grid
|
|
if ( bLayoutRTL )
|
|
aCellRect.AdjustLeft(1 );
|
|
else
|
|
aCellRect.AdjustRight( -1 );
|
|
|
|
tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
|
|
Size aMarkSize( nMarkPixel, (nMarkPixel-1)*2 );
|
|
|
|
const Color aColor = pInfo->maBackground ?
|
|
static_cast<const SvxBrushItem*>(pInfo->maBackground.getItem())->GetColor() :
|
|
COL_AUTO;
|
|
if ( aColor == COL_AUTO ? bIsDarkBackground : aColor.IsDark() )
|
|
mpDev->SetDrawMode( nOldDrawMode | DrawModeFlags::WhiteLine );
|
|
else
|
|
mpDev->SetDrawMode( nOldDrawMode | DrawModeFlags::BlackLine );
|
|
|
|
if (bVertical)
|
|
{
|
|
if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Bottom : ScClipMark::Top))
|
|
{
|
|
// visually top
|
|
tools::Rectangle aMarkRect = aCellRect;
|
|
aMarkRect.SetBottom(aCellRect.Top() + nMarkPixel - 1);
|
|
SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, true, true);
|
|
}
|
|
if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Top : ScClipMark::Bottom))
|
|
{
|
|
// visually bottom
|
|
tools::Rectangle aMarkRect = aCellRect;
|
|
aMarkRect.SetTop(aCellRect.Bottom() + nMarkPixel + 1);
|
|
SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, false,
|
|
true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Right : ScClipMark::Left))
|
|
{
|
|
// visually left
|
|
tools::Rectangle aMarkRect = aCellRect;
|
|
aMarkRect.SetRight(aCellRect.Left() + nMarkPixel - 1);
|
|
SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, true,
|
|
false);
|
|
}
|
|
if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Left : ScClipMark::Right))
|
|
{
|
|
// visually right
|
|
tools::Rectangle aMarkRect = aCellRect;
|
|
aMarkRect.SetLeft(aCellRect.Right() - nMarkPixel + 1);
|
|
SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, false,
|
|
false);
|
|
}
|
|
}
|
|
}
|
|
nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
|
|
}
|
|
}
|
|
nPosY += pThisRowInfo->nHeight;
|
|
}
|
|
|
|
mpDev->SetDrawMode(nOldDrawMode);
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|