/* -*- 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 .
*/
if (IsRotated() && SvxRotateMode::SVX_ROTATE_MODE_STANDARD != meRotMode )
{ // tdf#143377 We need to limit applying Skew to geometry since the closer // we get to 0.0 or PI the more sin(mfOrientation) will get to zero and the // huger the Skew effect will be. For that, use an epsilon-radius of 1/2 // degree around the dangerous points 0.0 and PI.
// Snap to modulo to [0.0 .. 2PI[ to make compare easier constdouble fSnapped(::basegfx::snapToZeroRange(mfOrientation, M_PI * 2.0));
// As a compromise, allow up to 1/2 degree staticconstdouble fMinAng(M_PI/360.0);
// Check if Skew makes sense or would be too huge constbool bForbidSkew(
fSnapped < fMinAng || // range [0.0 .. fMinAng]
fSnapped > (M_PI * 2.0) - fMinAng || // range [PI-fMinAng .. 2PI[
fabs(fSnapped - M_PI) < fMinAng); // range [PI-fMinAng .. PI+fMinAng]
if(!bForbidSkew)
{ // when rotated, adapt values. Get Skew (cos/sin == 1/tan) constdouble fSkew(aY.getY() * (cos(mfOrientation) / sin(mfOrientation)));
switch (meRotMode)
{ case SvxRotateMode::SVX_ROTATE_MODE_TOP: // shear Y-Axis
aY.setX(-fSkew); break; case SvxRotateMode::SVX_ROTATE_MODE_CENTER: // shear origin half, Y full
aOrigin.setX(aOrigin.getX() + (fSkew * 0.5));
aY.setX(-fSkew); break; case SvxRotateMode::SVX_ROTATE_MODE_BOTTOM: // shear origin full, Y full
aOrigin.setX(aOrigin.getX() + fSkew);
aY.setX(-fSkew); break; default: // SvxRotateMode::SVX_ROTATE_MODE_STANDARD, already excluded above break;
}
}
}
// use column vectors as coordinate axes, homogen column for translation return basegfx::utils::createCoordinateSystemTransform( aOrigin, aX, aY );
}
// adjust rectangle for partly visible merged cells if( IsMerged() )
{ // not *sure* what exactly this is good for, // it is just a hard set extension at merged cells, // probably *should* be included in the above extended // GetColPosition/GetColWidth already. This might be // added due to GetColPosition/GetColWidth not working // correctly over PageChanges (if used), but not sure.
aRange.expand(
basegfx::B2DRange(
aRange.getMinX() - mnAddLeft,
aRange.getMinY() - mnAddTop,
aRange.getMaxX() + mnAddRight,
aRange.getMaxY() + mnAddBottom ) );
}
if (!mxImpl->mbMayHaveCellRotation)
{ // activate once when a cell gets actually rotated to allow fast // answering HasCellRotation() calls
mxImpl->mbMayHaveCellRotation = aTempCell.IsRotated();
}
}
bool Array::HasCellRotation() const
{ if (!mxImpl->mbMayHaveCellRotation)
{ // never set, no need to check returnfalse;
}
return mxImpl->HasCellRotation();
}
const Style& Array::GetCellStyleLeft( sal_Int32 nCol, sal_Int32 nRow ) const
{ // outside clipping rows or overlapped in merged cells: invisible if( !mxImpl->IsRowInClipRange( nRow ) || mxImpl->IsMergedOverlappedLeft( nCol, nRow ) ) return OBJ_STYLE_NONE; // left clipping border: always own left style if( nCol == mxImpl->mnFirstClipCol ) return mxImpl->GetMergedStyleSourceCell(nCol, nRow)->GetStyleLeft(); // right clipping border: always right style of left neighbor cell if( nCol == mxImpl->mnLastClipCol + 1 ) return mxImpl->GetMergedStyleSourceCell(nCol - 1, nRow)->GetStyleRight(); // outside clipping columns: invisible if( !mxImpl->IsColInClipRange( nCol ) ) return OBJ_STYLE_NONE; // inside clipping range: maximum of own left style and right style of left neighbor cell return std::max(mxImpl->GetMergedStyleSourceCell(nCol, nRow)->GetStyleLeft(),
mxImpl->GetMergedStyleSourceCell(nCol - 1, nRow)->GetStyleRight());
}
const Style& Array::GetCellStyleRight( sal_Int32 nCol, sal_Int32 nRow ) const
{ // outside clipping rows or overlapped in merged cells: invisible if( !mxImpl->IsRowInClipRange( nRow ) || mxImpl->IsMergedOverlappedRight( nCol, nRow ) ) return OBJ_STYLE_NONE; // left clipping border: always left style of right neighbor cell if( nCol + 1 == mxImpl->mnFirstClipCol ) return mxImpl->GetMergedStyleSourceCell(nCol + 1, nRow)->GetStyleLeft(); // right clipping border: always own right style if( nCol == mxImpl->mnLastClipCol ) return mxImpl->GetMergedStyleSourceCell(nCol, nRow)->GetStyleRight(); // outside clipping columns: invisible if( !mxImpl->IsColInClipRange( nCol ) ) return OBJ_STYLE_NONE; // inside clipping range: maximum of own right style and left style of right neighbor cell return std::max(mxImpl->GetMergedStyleSourceCell(nCol, nRow)->GetStyleRight(),
mxImpl->GetMergedStyleSourceCell(nCol + 1, nRow)->GetStyleLeft());
}
const Style& Array::GetCellStyleTop( sal_Int32 nCol, sal_Int32 nRow ) const
{ // outside clipping columns or overlapped in merged cells: invisible if( !mxImpl->IsColInClipRange( nCol ) || mxImpl->IsMergedOverlappedTop( nCol, nRow ) ) return OBJ_STYLE_NONE; // top clipping border: always own top style if( nRow == mxImpl->mnFirstClipRow ) return mxImpl->GetMergedStyleSourceCell(nCol, nRow)->GetStyleTop(); // bottom clipping border: always bottom style of top neighbor cell if( nRow == mxImpl->mnLastClipRow + 1 ) return mxImpl->GetMergedStyleSourceCell(nCol, nRow - 1)->GetStyleBottom(); // outside clipping rows: invisible if( !mxImpl->IsRowInClipRange( nRow ) ) return OBJ_STYLE_NONE; // inside clipping range: maximum of own top style and bottom style of top neighbor cell return std::max(mxImpl->GetMergedStyleSourceCell(nCol, nRow)->GetStyleTop(),
mxImpl->GetMergedStyleSourceCell(nCol, nRow - 1)->GetStyleBottom());
}
const Style& Array::GetCellStyleBottom( sal_Int32 nCol, sal_Int32 nRow ) const
{ // outside clipping columns or overlapped in merged cells: invisible if( !mxImpl->IsColInClipRange( nCol ) || mxImpl->IsMergedOverlappedBottom( nCol, nRow ) ) return OBJ_STYLE_NONE; // top clipping border: always top style of bottom neighbor cell if( nRow + 1 == mxImpl->mnFirstClipRow ) return mxImpl->GetMergedStyleSourceCell(nCol, nRow + 1)->GetStyleTop(); // bottom clipping border: always own bottom style if( nRow == mxImpl->mnLastClipRow ) return mxImpl->GetMergedStyleSourceCell(nCol, nRow)->GetStyleBottom(); // outside clipping rows: invisible if( !mxImpl->IsRowInClipRange( nRow ) ) return OBJ_STYLE_NONE; // inside clipping range: maximum of own bottom style and top style of bottom neighbor cell return std::max(mxImpl->GetMergedStyleSourceCell(nCol, nRow)->GetStyleBottom(),
mxImpl->GetMergedStyleSourceCell(nCol, nRow + 1)->GetStyleTop());
}
if( pCell->IsMerged() )
{ // not *sure* what exactly this is good for, // it is just a hard set extension at merged cells, // probably *should* be included in the above extended // GetColPosition/GetColWidth already. This might be // added due to GetColPosition/GetColWidth not working // correctly over PageChanges (if used), but not sure.
aRect.AdjustLeft( -(pCell->mnAddLeft) );
aRect.AdjustRight(pCell->mnAddRight );
aRect.AdjustTop( -(pCell->mnAddTop) );
aRect.AdjustBottom(pCell->mnAddBottom );
}
// It may be necessary to extend the loop ranges by one cell to the outside, // when possible. This is needed e.g. when there is in Calc a Cell with an // upper CellBorder using DoubleLine and that is right/left connected upwards // to also DoubleLine. These upper DoubleLines will be extended to meet the // lower of the upper CellBorder and thus have graphical parts that are // displayed one cell below and right/left of the target cell - analog to // other examples in all other directions. // It would be possible to explicitly test this (if possible by indices at all) // looping and testing the styles in the outer cells to detect this, but since // for other usages (e.g. UI) usually nFirstRow==0 and nLastRow==GetRowCount()-1 // (and analog for Col) it is okay to just expand the range when available. // Do *not* change nFirstRow/nLastRow due to these needed to the boolean tests // below (!) // Checked usages, this method is used in Calc EditView/Print/Export stuff and // in UI (Dialog), not for Writer Tables and Draw/Impress tables. All usages // seem okay with this change, so I will add it. const sal_Int32 nStartRow(nFirstRow > 0 ? nFirstRow - 1 : nFirstRow); const sal_Int32 nEndRow(nLastRow < GetRowCount() - 1 ? nLastRow + 1 : nLastRow); const sal_Int32 nStartCol(nFirstCol > 0 ? nFirstCol - 1 : nFirstCol); const sal_Int32 nEndCol(nLastCol < GetColCount() - 1 ? nLastCol + 1 : nLastCol);
// remember for which merged cells crossed lines were already created. To // do so, hold the sal_Int32 cell index in a set for fast check
std::unordered_set< sal_Int32 > aMergedCells;
for (sal_Int32 nRow(nStartRow); nRow <= nEndRow; ++nRow)
{ for (sal_Int32 nCol(nStartCol); nCol <= nEndCol; ++nCol)
{ // get Cell and CoordinateSystem (*only* for this Cell, do *not* expand for // merged cells (!)), check if used (non-empty vectors) const Cell* pCell(mxImpl->GetCell(nCol, nRow));
basegfx::B2DHomMatrix aCoordinateSystem(pCell->CreateCoordinateSystemSingleCell(*this, nCol, nRow));
basegfx::B2DVector aX(basegfx::utils::getColumn(aCoordinateSystem, 0));
basegfx::B2DVector aY(basegfx::utils::getColumn(aCoordinateSystem, 1));
// get needed local values
basegfx::B2DPoint aOrigin(basegfx::utils::getColumn(aCoordinateSystem, 2)); constbool bOverlapX(pCell->mbOverlapX); constbool bFirstCol(nCol == nFirstCol);
// handle rotation: If cell is rotated, handle lower/right edge inside // this local geometry due to the created CoordinateSystem already representing // the needed transformations. constbool bRotated(pCell->IsRotated());
// Additionally avoid double-handling by suppressing handling when self not rotated, // but above/left is rotated and thus already handled. Two directly connected // rotated will paint/create both edges, they might be rotated differently. constbool bSuppressLeft(!bRotated && nCol > nFirstCol && mxImpl->GetCell(nCol - 1, nRow)->IsRotated()); constbool bSuppressAbove(!bRotated && nRow > nFirstRow && mxImpl->GetCell(nCol, nRow - 1)->IsRotated());
// create upper line for this Cell if ((!bOverlapY // true for first line in merged cells or cells
|| bFirstRow) // true for non_Calc usages of this tooling
&& !bSuppressAbove) // true when above is not rotated, so edge is already handled (see bRotated)
{ // get CellStyle - method will take care to get the correct one, e.g. // for merged cells (it uses mxImpl->GetMergedOriginCell that works with topLeft's of these) const Style& rTop(GetCellStyleTop(nCol, nRow));
// create lower line for this Cell if (bLastRow // true for non_Calc usages of this tooling
|| bRotated) // true if cell is rotated, handle lower edge in local geometry
{ const Style& rBottom(GetCellStyleBottom(nCol, nRow));
// create left line for this Cell if ((!bOverlapX // true for first column in merged cells or cells
|| bFirstCol) // true for non_Calc usages of this tooling
&& !bSuppressLeft) // true when left is not rotated, so edge is already handled (see bRotated)
{ const Style& rLeft(GetCellStyleLeft(nCol, nRow));
// create right line for this Cell if (bLastCol // true for non_Calc usages of this tooling
|| bRotated) // true if cell is rotated, handle right edge in local geometry
{ const Style& rRight(GetCellStyleRight(nCol, nRow));
// tdf#126269 check for crossed lines, these need special treatment, especially // for merged cells (see comments in task). Separate treatment of merged and // non-merged cells to allow better handling of both types if(pCell->IsMerged())
{ // first check if this merged cell was already handled. To do so, // calculate and use the index of the TopLeft cell
sal_Int32 nColLeft(nCol), nRowTop(nRow), nColRight(nCol), nRowBottom(nRow);
GetMergedRange(nColLeft, nRowTop, nColRight, nRowBottom, nCol, nRow); const sal_Int32 nIndexOfMergedCell(mxImpl->GetIndex(nColLeft, nRowTop));
auto aItInsertedPair = aMergedCells.insert(nIndexOfMergedCell); if(aItInsertedPair.second)
{ // not found, so not yet handled.
// Get and check if diagonal styles are used // Note: For GetCellStyleBLTR below I tried to use nRowBottom // as Y-value what seemed more logical, but that // is wrong. Despite defining a line starting at // bottom-left, the Style is defined in the cell at top-left const Style& rTLBR(GetCellStyleTLBR(nColLeft, nRowTop)); const Style& rBLTR(GetCellStyleBLTR(nColLeft, nRowTop));
if(rTLBR.IsUsed() || rBLTR.IsUsed())
{ // test if merged cell overlaps ClipRange at all (needs visualization) if(mxImpl->OverlapsClipRange(nColLeft, nRowTop, nColRight, nRowBottom))
{ // when merged, get extended coordinate system and derived values // for the full range of this merged cell. Only work with rMergedCell // (which is the top-left single cell of the merged cell) from here on
aCoordinateSystem = mxImpl->GetCell(nColLeft, nRowTop)->CreateCoordinateSystemMergedCell(
*this, nColLeft, nRowTop, nColRight, nRowBottom);
aX = basegfx::utils::getColumn(aCoordinateSystem, 0);
aY = basegfx::utils::getColumn(aCoordinateSystem, 1);
aOrigin = basegfx::utils::getColumn(aCoordinateSystem, 2);
// check if clip is needed
basegfx::B2DRange aClipRange;
// first use row/col ClipTest for raw check bool bNeedToClip(
!mxImpl->IsColInClipRange(nColLeft) ||
!mxImpl->IsRowInClipRange(nRowTop) ||
!mxImpl->IsColInClipRange(nColRight) ||
!mxImpl->IsRowInClipRange(nRowBottom));
if(bNeedToClip)
{ // now get ClipRange and CellRange in logical coordinates
aClipRange = GetB2DRange(
mxImpl->mnFirstClipCol, mxImpl->mnFirstClipRow,
mxImpl->mnLastClipCol, mxImpl->mnLastClipRow);
HelperCreateBLTREntry(*this, rBLTR, aData, aOrigin, aX, aY,
nCol, nRow, nCol, nRow, pForceColor, nullptr);
}
}
}
} elseif(!aY.equalZero())
{ // cell has height, but no width. Create left vertical line for this Cell if ((!bOverlapX // true for first column in merged cells or cells
|| bFirstCol) // true for non_Calc usages of this tooling
&& !bSuppressLeft) // true when left is not rotated, so edge is already handled (see bRotated)
{ const Style& rLeft(GetCellStyleLeft(nCol, nRow));
if (rLeft.IsUsed())
{
HelperCreateVerticalEntry(*this, rLeft, nCol, nRow, aOrigin, aX, aY, aData, true, pForceColor);
}
}
} else
{ // Cell has *no* size, thus no visualization
}
}
}
// create instance of SdrFrameBorderPrimitive2D if // SdrFrameBorderDataVector is used
drawinglayer::primitive2d::Primitive2DContainer aSequence;
if(!aData.empty())
{
aSequence.append( new drawinglayer::primitive2d::SdrFrameBorderPrimitive2D(
std::move(aData), true)); // force visualization to minimal one discrete unit (pixel)
}
#ifdef OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL for(autoconst& rClipRange : aClipRanges)
{ // draw ClipRange in yellow to allow simple interactive optical control in office
aSequence.append( new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
basegfx::utils::createPolygonFromRect(rClipRange),
basegfx::BColor(1.0, 1.0, 0.0)));
} #endif
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.