1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <svx/framelinkarray.hxx>
25 #include <unordered_set>
27 #include <tools/debug.hxx>
28 #include <tools/gen.hxx>
29 #include <vcl/canvastools.hxx>
30 #include <svx/sdr/primitive2d/sdrframeborderprimitive2d.hxx>
31 #include <basegfx/matrix/b2dhommatrixtools.hxx>
32 #include <basegfx/polygon/b2dpolygonclipper.hxx>
33 // #include <basegfx/numeric/ftools.hxx>
35 //#define OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL
36 #ifdef OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL
37 #include <basegfx/polygon/b2dpolygontools.hxx>
38 #include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
41 namespace svx::frame
{
55 basegfx::B2DHomMatrix
HelperCreateB2DHomMatrixFromB2DRange(
56 const basegfx::B2DRange
& rRange
) const;
62 sal_Int32 mnAddBottom
;
64 SvxRotateMode meRotMode
;
74 void SetStyleLeft(const Style
& rStyle
) { maLeft
= rStyle
; }
75 void SetStyleRight(const Style
& rStyle
) { maRight
= rStyle
; }
76 void SetStyleTop(const Style
& rStyle
) { maTop
= rStyle
; }
77 void SetStyleBottom(const Style
& rStyle
) { maBottom
= rStyle
; }
78 void SetStyleTLBR(const Style
& rStyle
) { maTLBR
= rStyle
; }
79 void SetStyleBLTR(const Style
& rStyle
) { maBLTR
= rStyle
; }
81 const Style
& GetStyleLeft() const { return maLeft
; }
82 const Style
& GetStyleRight() const { return maRight
; }
83 const Style
& GetStyleTop() const { return maTop
; }
84 const Style
& GetStyleBottom() const { return maBottom
; }
85 const Style
& GetStyleTLBR() const { return maTLBR
; }
86 const Style
& GetStyleBLTR() const { return maBLTR
; }
88 bool IsMerged() const { return mbMergeOrig
|| mbOverlapX
|| mbOverlapY
; }
89 bool IsRotated() const { return mfOrientation
!= 0.0; }
93 basegfx::B2DHomMatrix
CreateCoordinateSystemSingleCell(
94 const Array
& rArray
, sal_Int32 nCol
, sal_Int32 nRow
) const;
95 basegfx::B2DHomMatrix
CreateCoordinateSystemMergedCell(
96 const Array
& rArray
, sal_Int32 nColLeft
, sal_Int32 nRowTop
, sal_Int32 nColRight
, sal_Int32 nRowBottom
) const;
101 typedef std::vector
< Cell
> CellVec
;
103 basegfx::B2DHomMatrix
Cell::HelperCreateB2DHomMatrixFromB2DRange(
104 const basegfx::B2DRange
& rRange
) const
106 if( rRange
.isEmpty() )
107 return basegfx::B2DHomMatrix();
109 basegfx::B2DPoint
aOrigin(rRange
.getMinimum());
110 basegfx::B2DVector
aX(rRange
.getWidth(), 0.0);
111 basegfx::B2DVector
aY(0.0, rRange
.getHeight());
113 if (IsRotated() && SvxRotateMode::SVX_ROTATE_MODE_STANDARD
!= meRotMode
)
115 // tdf#143377 We need to limit applying Skew to geometry since the closer
116 // we get to 0.0 or PI the more sin(mfOrientation) will get to zero and the
117 // huger the Skew effect will be. For that, use an epsilon-radius of 1/2
118 // degree around the dangerous points 0.0 and PI.
120 // Snap to modulo to [0.0 .. 2PI[ to make compare easier
121 const double fSnapped(::basegfx::snapToZeroRange(mfOrientation
, M_PI
* 2.0));
123 // As a compromise, allow up to 1/2 degree
124 static const double fMinAng(M_PI
/360.0);
126 // Check if Skew makes sense or would be too huge
127 const bool bForbidSkew(
128 fSnapped
< fMinAng
|| // range [0.0 .. fMinAng]
129 fSnapped
> (M_PI
* 2.0) - fMinAng
|| // range [PI-fMinAng .. 2PI[
130 fabs(fSnapped
- M_PI
) < fMinAng
); // range [PI-fMinAng .. PI+fMinAng]
134 // when rotated, adapt values. Get Skew (cos/sin == 1/tan)
135 const double fSkew(aY
.getY() * (cos(mfOrientation
) / sin(mfOrientation
)));
139 case SvxRotateMode::SVX_ROTATE_MODE_TOP
:
143 case SvxRotateMode::SVX_ROTATE_MODE_CENTER
:
144 // shear origin half, Y full
145 aOrigin
.setX(aOrigin
.getX() + (fSkew
* 0.5));
148 case SvxRotateMode::SVX_ROTATE_MODE_BOTTOM
:
149 // shear origin full, Y full
150 aOrigin
.setX(aOrigin
.getX() + fSkew
);
153 default: // SvxRotateMode::SVX_ROTATE_MODE_STANDARD, already excluded above
159 // use column vectors as coordinate axes, homogen column for translation
160 return basegfx::utils::createCoordinateSystemTransform( aOrigin
, aX
, aY
);
163 basegfx::B2DHomMatrix
Cell::CreateCoordinateSystemSingleCell(
164 const Array
& rArray
, sal_Int32 nCol
, sal_Int32 nRow
) const
166 const Point
aPoint( rArray
.GetColPosition( nCol
), rArray
.GetRowPosition( nRow
) );
167 const Size
aSize( rArray
.GetColWidth( nCol
, nCol
) + 1, rArray
.GetRowHeight( nRow
, nRow
) + 1 );
168 const basegfx::B2DRange
aRange( vcl::unotools::b2DRectangleFromRectangle( tools::Rectangle( aPoint
, aSize
) ) );
170 return HelperCreateB2DHomMatrixFromB2DRange( aRange
);
173 basegfx::B2DHomMatrix
Cell::CreateCoordinateSystemMergedCell(
174 const Array
& rArray
, sal_Int32 nColLeft
, sal_Int32 nRowTop
, sal_Int32 nColRight
, sal_Int32 nRowBottom
) const
176 basegfx::B2DRange
aRange( rArray
.GetB2DRange(
177 nColLeft
, nRowTop
, nColRight
, nRowBottom
) );
179 // adjust rectangle for partly visible merged cells
182 // not *sure* what exactly this is good for,
183 // it is just a hard set extension at merged cells,
184 // probably *should* be included in the above extended
185 // GetColPosition/GetColWidth already. This might be
186 // added due to GetColPosition/GetColWidth not working
187 // correctly over PageChanges (if used), but not sure.
190 aRange
.getMinX() - mnAddLeft
,
191 aRange
.getMinY() - mnAddTop
,
192 aRange
.getMaxX() + mnAddRight
,
193 aRange
.getMaxY() + mnAddBottom
) );
196 return HelperCreateB2DHomMatrixFromB2DRange( aRange
);
204 meRotMode(SvxRotateMode::SVX_ROTATE_MODE_STANDARD
),
205 mfOrientation( 0.0 ),
206 mbMergeOrig( false ),
212 void Cell::MirrorSelfX()
214 std::swap( maLeft
, maRight
);
215 std::swap( mnAddLeft
, mnAddRight
);
217 maRight
.MirrorSelf();
218 mfOrientation
= -mfOrientation
;
222 static void lclRecalcCoordVec( std::vector
<sal_Int32
>& rCoords
, const std::vector
<sal_Int32
>& rSizes
)
224 DBG_ASSERT( rCoords
.size() == rSizes
.size() + 1, "lclRecalcCoordVec - inconsistent vectors" );
225 auto aCIt
= rCoords
.begin();
226 for( const auto& rSize
: rSizes
)
228 *(aCIt
+ 1) = *aCIt
+ rSize
;
233 static void lclSetMergedRange( CellVec
& rCells
, sal_Int32 nWidth
, sal_Int32 nFirstCol
, sal_Int32 nFirstRow
, sal_Int32 nLastCol
, sal_Int32 nLastRow
)
235 for( sal_Int32 nCol
= nFirstCol
; nCol
<= nLastCol
; ++nCol
)
237 for( sal_Int32 nRow
= nFirstRow
; nRow
<= nLastRow
; ++nRow
)
239 Cell
& rCell
= rCells
[ nRow
* nWidth
+ nCol
];
240 rCell
.mbMergeOrig
= false;
241 rCell
.mbOverlapX
= nCol
> nFirstCol
;
242 rCell
.mbOverlapY
= nRow
> nFirstRow
;
245 rCells
[ nFirstRow
* nWidth
+ nFirstCol
].mbMergeOrig
= true;
249 const Style OBJ_STYLE_NONE
;
250 const Cell OBJ_CELL_NONE
;
255 std::vector
<sal_Int32
> maWidths
;
256 std::vector
<sal_Int32
> maHeights
;
257 mutable std::vector
<sal_Int32
> maXCoords
;
258 mutable std::vector
<sal_Int32
> maYCoords
;
261 sal_Int32 mnFirstClipCol
;
262 sal_Int32 mnFirstClipRow
;
263 sal_Int32 mnLastClipCol
;
264 sal_Int32 mnLastClipRow
;
265 mutable bool mbXCoordsDirty
;
266 mutable bool mbYCoordsDirty
;
267 bool mbMayHaveCellRotation
;
269 explicit ArrayImpl( sal_Int32 nWidth
, sal_Int32 nHeight
);
271 bool IsValidPos( sal_Int32 nCol
, sal_Int32 nRow
) const
272 { return (nCol
< mnWidth
) && (nRow
< mnHeight
); }
273 sal_Int32
GetIndex( sal_Int32 nCol
, sal_Int32 nRow
) const
274 { return nRow
* mnWidth
+ nCol
; }
276 const Cell
& GetCell( sal_Int32 nCol
, sal_Int32 nRow
) const;
277 Cell
& GetCellAcc( sal_Int32 nCol
, sal_Int32 nRow
);
279 sal_Int32
GetMergedFirstCol( sal_Int32 nCol
, sal_Int32 nRow
) const;
280 sal_Int32
GetMergedFirstRow( sal_Int32 nCol
, sal_Int32 nRow
) const;
281 sal_Int32
GetMergedLastCol( sal_Int32 nCol
, sal_Int32 nRow
) const;
282 sal_Int32
GetMergedLastRow( sal_Int32 nCol
, sal_Int32 nRow
) const;
284 const Cell
& GetMergedOriginCell( sal_Int32 nCol
, sal_Int32 nRow
) const;
285 const Cell
& GetMergedLastCell( sal_Int32 nCol
, sal_Int32 nRow
) const;
287 bool IsMergedOverlappedLeft( sal_Int32 nCol
, sal_Int32 nRow
) const;
288 bool IsMergedOverlappedRight( sal_Int32 nCol
, sal_Int32 nRow
) const;
289 bool IsMergedOverlappedTop( sal_Int32 nCol
, sal_Int32 nRow
) const;
290 bool IsMergedOverlappedBottom( sal_Int32 nCol
, sal_Int32 nRow
) const;
292 bool IsInClipRange( sal_Int32 nCol
, sal_Int32 nRow
) const;
293 bool IsColInClipRange( sal_Int32 nCol
) const;
294 bool IsRowInClipRange( sal_Int32 nRow
) const;
296 bool OverlapsClipRange( sal_Int32 nFirstCol
, sal_Int32 nFirstRow
, sal_Int32 nLastCol
, sal_Int32 nLastRow
) const;
298 sal_Int32
GetMirrorCol( sal_Int32 nCol
) const { return mnWidth
- nCol
- 1; }
300 sal_Int32
GetColPosition( sal_Int32 nCol
) const;
301 sal_Int32
GetRowPosition( sal_Int32 nRow
) const;
303 bool HasCellRotation() const;
306 ArrayImpl::ArrayImpl( sal_Int32 nWidth
, sal_Int32 nHeight
) :
311 mnLastClipCol( nWidth
- 1 ),
312 mnLastClipRow( nHeight
- 1 ),
313 mbXCoordsDirty( false ),
314 mbYCoordsDirty( false ),
315 mbMayHaveCellRotation( false )
317 // default-construct all vectors
318 maCells
.resize( mnWidth
* mnHeight
);
319 maWidths
.resize( mnWidth
, 0 );
320 maHeights
.resize( mnHeight
, 0 );
321 maXCoords
.resize( mnWidth
+ 1, 0 );
322 maYCoords
.resize( mnHeight
+ 1, 0 );
325 const Cell
& ArrayImpl::GetCell( sal_Int32 nCol
, sal_Int32 nRow
) const
327 return IsValidPos( nCol
, nRow
) ? maCells
[ GetIndex( nCol
, nRow
) ] : OBJ_CELL_NONE
;
330 Cell
& ArrayImpl::GetCellAcc( sal_Int32 nCol
, sal_Int32 nRow
)
333 return IsValidPos( nCol
, nRow
) ? maCells
[ GetIndex( nCol
, nRow
) ] : aDummy
;
336 sal_Int32
ArrayImpl::GetMergedFirstCol( sal_Int32 nCol
, sal_Int32 nRow
) const
338 sal_Int32 nFirstCol
= nCol
;
339 while( (nFirstCol
> 0) && GetCell( nFirstCol
, nRow
).mbOverlapX
) --nFirstCol
;
343 sal_Int32
ArrayImpl::GetMergedFirstRow( sal_Int32 nCol
, sal_Int32 nRow
) const
345 sal_Int32 nFirstRow
= nRow
;
346 while( (nFirstRow
> 0) && GetCell( nCol
, nFirstRow
).mbOverlapY
) --nFirstRow
;
350 sal_Int32
ArrayImpl::GetMergedLastCol( sal_Int32 nCol
, sal_Int32 nRow
) const
352 sal_Int32 nLastCol
= nCol
+ 1;
353 while( (nLastCol
< mnWidth
) && GetCell( nLastCol
, nRow
).mbOverlapX
) ++nLastCol
;
357 sal_Int32
ArrayImpl::GetMergedLastRow( sal_Int32 nCol
, sal_Int32 nRow
) const
359 sal_Int32 nLastRow
= nRow
+ 1;
360 while( (nLastRow
< mnHeight
) && GetCell( nCol
, nLastRow
).mbOverlapY
) ++nLastRow
;
364 const Cell
& ArrayImpl::GetMergedOriginCell( sal_Int32 nCol
, sal_Int32 nRow
) const
366 return GetCell( GetMergedFirstCol( nCol
, nRow
), GetMergedFirstRow( nCol
, nRow
) );
369 const Cell
& ArrayImpl::GetMergedLastCell( sal_Int32 nCol
, sal_Int32 nRow
) const
371 return GetCell( GetMergedLastCol( nCol
, nRow
), GetMergedLastRow( nCol
, nRow
) );
374 bool ArrayImpl::IsMergedOverlappedLeft( sal_Int32 nCol
, sal_Int32 nRow
) const
376 const Cell
& rCell
= GetCell( nCol
, nRow
);
377 return rCell
.mbOverlapX
|| (rCell
.mnAddLeft
> 0);
380 bool ArrayImpl::IsMergedOverlappedRight( sal_Int32 nCol
, sal_Int32 nRow
) const
382 return GetCell( nCol
+ 1, nRow
).mbOverlapX
|| (GetCell( nCol
, nRow
).mnAddRight
> 0);
385 bool ArrayImpl::IsMergedOverlappedTop( sal_Int32 nCol
, sal_Int32 nRow
) const
387 const Cell
& rCell
= GetCell( nCol
, nRow
);
388 return rCell
.mbOverlapY
|| (rCell
.mnAddTop
> 0);
391 bool ArrayImpl::IsMergedOverlappedBottom( sal_Int32 nCol
, sal_Int32 nRow
) const
393 return GetCell( nCol
, nRow
+ 1 ).mbOverlapY
|| (GetCell( nCol
, nRow
).mnAddBottom
> 0);
396 bool ArrayImpl::IsColInClipRange( sal_Int32 nCol
) const
398 return (mnFirstClipCol
<= nCol
) && (nCol
<= mnLastClipCol
);
401 bool ArrayImpl::IsRowInClipRange( sal_Int32 nRow
) const
403 return (mnFirstClipRow
<= nRow
) && (nRow
<= mnLastClipRow
);
406 bool ArrayImpl::OverlapsClipRange( sal_Int32 nFirstCol
, sal_Int32 nFirstRow
, sal_Int32 nLastCol
, sal_Int32 nLastRow
) const
408 if(nLastCol
< mnFirstClipCol
)
411 if(nFirstCol
> mnLastClipCol
)
414 if(nLastRow
< mnFirstClipRow
)
417 if(nFirstRow
> mnLastClipRow
)
423 bool ArrayImpl::IsInClipRange( sal_Int32 nCol
, sal_Int32 nRow
) const
425 return IsColInClipRange( nCol
) && IsRowInClipRange( nRow
);
428 sal_Int32
ArrayImpl::GetColPosition( sal_Int32 nCol
) const
432 lclRecalcCoordVec( maXCoords
, maWidths
);
433 mbXCoordsDirty
= false;
435 return maXCoords
[ nCol
];
438 sal_Int32
ArrayImpl::GetRowPosition( sal_Int32 nRow
) const
442 lclRecalcCoordVec( maYCoords
, maHeights
);
443 mbYCoordsDirty
= false;
445 return maYCoords
[ nRow
];
448 bool ArrayImpl::HasCellRotation() const
451 for (const auto& aCell
: maCells
)
453 if (aCell
.IsRotated())
464 class MergedCellIterator
467 explicit MergedCellIterator( const Array
& rArray
, sal_Int32 nCol
, sal_Int32 nRow
);
469 bool Is() const { return (mnCol
<= mnLastCol
) && (mnRow
<= mnLastRow
); }
470 sal_Int32
Col() const { return mnCol
; }
471 sal_Int32
Row() const { return mnRow
; }
473 MergedCellIterator
& operator++();
476 sal_Int32 mnFirstCol
;
477 sal_Int32 mnFirstRow
;
486 MergedCellIterator::MergedCellIterator( const Array
& rArray
, sal_Int32 nCol
, sal_Int32 nRow
)
488 DBG_ASSERT( rArray
.IsMerged( nCol
, nRow
), "svx::frame::MergedCellIterator::MergedCellIterator - not in merged range" );
489 rArray
.GetMergedRange( mnFirstCol
, mnFirstRow
, mnLastCol
, mnLastRow
, nCol
, nRow
);
494 MergedCellIterator
& MergedCellIterator::operator++()
496 DBG_ASSERT( Is(), "svx::frame::MergedCellIterator::operator++() - already invalid" );
497 if( ++mnCol
> mnLastCol
)
506 #define DBG_FRAME_CHECK( cond, funcname, error ) DBG_ASSERT( cond, "svx::frame::Array::" funcname " - " error )
507 #define DBG_FRAME_CHECK_COL( col, funcname ) DBG_FRAME_CHECK( (col) < GetColCount(), funcname, "invalid column index" )
508 #define DBG_FRAME_CHECK_ROW( row, funcname ) DBG_FRAME_CHECK( (row) < GetRowCount(), funcname, "invalid row index" )
509 #define DBG_FRAME_CHECK_COLROW( col, row, funcname ) DBG_FRAME_CHECK( ((col) < GetColCount()) && ((row) < GetRowCount()), funcname, "invalid cell index" )
510 #define DBG_FRAME_CHECK_COL_1( col, funcname ) DBG_FRAME_CHECK( (col) <= GetColCount(), funcname, "invalid column index" )
511 #define DBG_FRAME_CHECK_ROW_1( row, funcname ) DBG_FRAME_CHECK( (row) <= GetRowCount(), funcname, "invalid row index" )
514 #define CELL( col, row ) mxImpl->GetCell( col, row )
515 #define CELLACC( col, row ) mxImpl->GetCellAcc( col, row )
516 #define ORIGCELL( col, row ) mxImpl->GetMergedOriginCell( col, row )
517 #define LASTCELL( col, row ) mxImpl->GetMergedLastCell( col, row )
529 // array size and column/row indexes
530 void Array::Initialize( sal_Int32 nWidth
, sal_Int32 nHeight
)
532 mxImpl
.reset( new ArrayImpl( nWidth
, nHeight
) );
535 sal_Int32
Array::GetColCount() const
537 return mxImpl
->mnWidth
;
540 sal_Int32
Array::GetRowCount() const
542 return mxImpl
->mnHeight
;
545 sal_Int32
Array::GetCellCount() const
547 return mxImpl
->maCells
.size();
550 sal_Int32
Array::GetCellIndex( sal_Int32 nCol
, sal_Int32 nRow
, bool bRTL
) const
552 DBG_FRAME_CHECK_COLROW( nCol
, nRow
, "GetCellIndex" );
554 nCol
= mxImpl
->GetMirrorCol(nCol
);
555 return mxImpl
->GetIndex( nCol
, nRow
);
558 // cell border styles
559 void Array::SetCellStyleLeft( sal_Int32 nCol
, sal_Int32 nRow
, const Style
& rStyle
)
561 DBG_FRAME_CHECK_COLROW( nCol
, nRow
, "SetCellStyleLeft" );
562 CELLACC( nCol
, nRow
).SetStyleLeft(rStyle
);
565 void Array::SetCellStyleRight( sal_Int32 nCol
, sal_Int32 nRow
, const Style
& rStyle
)
567 DBG_FRAME_CHECK_COLROW( nCol
, nRow
, "SetCellStyleRight" );
568 CELLACC( nCol
, nRow
).SetStyleRight(rStyle
);
571 void Array::SetCellStyleTop( sal_Int32 nCol
, sal_Int32 nRow
, const Style
& rStyle
)
573 DBG_FRAME_CHECK_COLROW( nCol
, nRow
, "SetCellStyleTop" );
574 CELLACC( nCol
, nRow
).SetStyleTop(rStyle
);
577 void Array::SetCellStyleBottom( sal_Int32 nCol
, sal_Int32 nRow
, const Style
& rStyle
)
579 DBG_FRAME_CHECK_COLROW( nCol
, nRow
, "SetCellStyleBottom" );
580 CELLACC( nCol
, nRow
).SetStyleBottom(rStyle
);
583 void Array::SetCellStyleTLBR( sal_Int32 nCol
, sal_Int32 nRow
, const Style
& rStyle
)
585 DBG_FRAME_CHECK_COLROW( nCol
, nRow
, "SetCellStyleTLBR" );
586 CELLACC( nCol
, nRow
).SetStyleTLBR(rStyle
);
589 void Array::SetCellStyleBLTR( sal_Int32 nCol
, sal_Int32 nRow
, const Style
& rStyle
)
591 DBG_FRAME_CHECK_COLROW( nCol
, nRow
, "SetCellStyleBLTR" );
592 CELLACC( nCol
, nRow
).SetStyleBLTR(rStyle
);
595 void Array::SetCellStyleDiag( sal_Int32 nCol
, sal_Int32 nRow
, const Style
& rTLBR
, const Style
& rBLTR
)
597 DBG_FRAME_CHECK_COLROW( nCol
, nRow
, "SetCellStyleDiag" );
598 Cell
& rCell
= CELLACC( nCol
, nRow
);
599 rCell
.SetStyleTLBR(rTLBR
);
600 rCell
.SetStyleBLTR(rBLTR
);
603 void Array::SetColumnStyleLeft( sal_Int32 nCol
, const Style
& rStyle
)
605 DBG_FRAME_CHECK_COL( nCol
, "SetColumnStyleLeft" );
606 for( sal_Int32 nRow
= 0; nRow
< mxImpl
->mnHeight
; ++nRow
)
607 SetCellStyleLeft( nCol
, nRow
, rStyle
);
610 void Array::SetColumnStyleRight( sal_Int32 nCol
, const Style
& rStyle
)
612 DBG_FRAME_CHECK_COL( nCol
, "SetColumnStyleRight" );
613 for( sal_Int32 nRow
= 0; nRow
< mxImpl
->mnHeight
; ++nRow
)
614 SetCellStyleRight( nCol
, nRow
, rStyle
);
617 void Array::SetRowStyleTop( sal_Int32 nRow
, const Style
& rStyle
)
619 DBG_FRAME_CHECK_ROW( nRow
, "SetRowStyleTop" );
620 for( sal_Int32 nCol
= 0; nCol
< mxImpl
->mnWidth
; ++nCol
)
621 SetCellStyleTop( nCol
, nRow
, rStyle
);
624 void Array::SetRowStyleBottom( sal_Int32 nRow
, const Style
& rStyle
)
626 DBG_FRAME_CHECK_ROW( nRow
, "SetRowStyleBottom" );
627 for( sal_Int32 nCol
= 0; nCol
< mxImpl
->mnWidth
; ++nCol
)
628 SetCellStyleBottom( nCol
, nRow
, rStyle
);
631 void Array::SetCellRotation(sal_Int32 nCol
, sal_Int32 nRow
, SvxRotateMode eRotMode
, double fOrientation
)
633 DBG_FRAME_CHECK_COLROW(nCol
, nRow
, "SetCellRotation");
634 Cell
& rTarget
= CELLACC(nCol
, nRow
);
635 rTarget
.meRotMode
= eRotMode
;
636 rTarget
.mfOrientation
= fOrientation
;
638 if (!mxImpl
->mbMayHaveCellRotation
)
640 // activate once when a cell gets actually rotated to allow fast
641 // answering HasCellRotation() calls
642 mxImpl
->mbMayHaveCellRotation
= rTarget
.IsRotated();
646 bool Array::HasCellRotation() const
648 if (!mxImpl
->mbMayHaveCellRotation
)
650 // never set, no need to check
654 return mxImpl
->HasCellRotation();
657 const Style
& Array::GetCellStyleLeft( sal_Int32 nCol
, sal_Int32 nRow
) const
659 // outside clipping rows or overlapped in merged cells: invisible
660 if( !mxImpl
->IsRowInClipRange( nRow
) || mxImpl
->IsMergedOverlappedLeft( nCol
, nRow
) )
661 return OBJ_STYLE_NONE
;
662 // left clipping border: always own left style
663 if( nCol
== mxImpl
->mnFirstClipCol
)
664 return ORIGCELL( nCol
, nRow
).GetStyleLeft();
665 // right clipping border: always right style of left neighbor cell
666 if( nCol
== mxImpl
->mnLastClipCol
+ 1 )
667 return ORIGCELL( nCol
- 1, nRow
).GetStyleRight();
668 // outside clipping columns: invisible
669 if( !mxImpl
->IsColInClipRange( nCol
) )
670 return OBJ_STYLE_NONE
;
671 // inside clipping range: maximum of own left style and right style of left neighbor cell
672 return std::max( ORIGCELL( nCol
, nRow
).GetStyleLeft(), ORIGCELL( nCol
- 1, nRow
).GetStyleRight() );
675 const Style
& Array::GetCellStyleRight( sal_Int32 nCol
, sal_Int32 nRow
) const
677 // outside clipping rows or overlapped in merged cells: invisible
678 if( !mxImpl
->IsRowInClipRange( nRow
) || mxImpl
->IsMergedOverlappedRight( nCol
, nRow
) )
679 return OBJ_STYLE_NONE
;
680 // left clipping border: always left style of right neighbor cell
681 if( nCol
+ 1 == mxImpl
->mnFirstClipCol
)
682 return ORIGCELL( nCol
+ 1, nRow
).GetStyleLeft();
683 // right clipping border: always own right style
684 if( nCol
== mxImpl
->mnLastClipCol
)
685 return LASTCELL( nCol
, nRow
).GetStyleRight();
686 // outside clipping columns: invisible
687 if( !mxImpl
->IsColInClipRange( nCol
) )
688 return OBJ_STYLE_NONE
;
689 // inside clipping range: maximum of own right style and left style of right neighbor cell
690 return std::max( ORIGCELL( nCol
, nRow
).GetStyleRight(), ORIGCELL( nCol
+ 1, nRow
).GetStyleLeft() );
693 const Style
& Array::GetCellStyleTop( sal_Int32 nCol
, sal_Int32 nRow
) const
695 // outside clipping columns or overlapped in merged cells: invisible
696 if( !mxImpl
->IsColInClipRange( nCol
) || mxImpl
->IsMergedOverlappedTop( nCol
, nRow
) )
697 return OBJ_STYLE_NONE
;
698 // top clipping border: always own top style
699 if( nRow
== mxImpl
->mnFirstClipRow
)
700 return ORIGCELL( nCol
, nRow
).GetStyleTop();
701 // bottom clipping border: always bottom style of top neighbor cell
702 if( nRow
== mxImpl
->mnLastClipRow
+ 1 )
703 return ORIGCELL( nCol
, nRow
- 1 ).GetStyleBottom();
704 // outside clipping rows: invisible
705 if( !mxImpl
->IsRowInClipRange( nRow
) )
706 return OBJ_STYLE_NONE
;
707 // inside clipping range: maximum of own top style and bottom style of top neighbor cell
708 return std::max( ORIGCELL( nCol
, nRow
).GetStyleTop(), ORIGCELL( nCol
, nRow
- 1 ).GetStyleBottom() );
711 const Style
& Array::GetCellStyleBottom( sal_Int32 nCol
, sal_Int32 nRow
) const
713 // outside clipping columns or overlapped in merged cells: invisible
714 if( !mxImpl
->IsColInClipRange( nCol
) || mxImpl
->IsMergedOverlappedBottom( nCol
, nRow
) )
715 return OBJ_STYLE_NONE
;
716 // top clipping border: always top style of bottom neighbor cell
717 if( nRow
+ 1 == mxImpl
->mnFirstClipRow
)
718 return ORIGCELL( nCol
, nRow
+ 1 ).GetStyleTop();
719 // bottom clipping border: always own bottom style
720 if( nRow
== mxImpl
->mnLastClipRow
)
721 return LASTCELL( nCol
, nRow
).GetStyleBottom();
722 // outside clipping rows: invisible
723 if( !mxImpl
->IsRowInClipRange( nRow
) )
724 return OBJ_STYLE_NONE
;
725 // inside clipping range: maximum of own bottom style and top style of bottom neighbor cell
726 return std::max( ORIGCELL( nCol
, nRow
).GetStyleBottom(), ORIGCELL( nCol
, nRow
+ 1 ).GetStyleTop() );
729 const Style
& Array::GetCellStyleTLBR( sal_Int32 nCol
, sal_Int32 nRow
) const
731 return CELL( nCol
, nRow
).GetStyleTLBR();
734 const Style
& Array::GetCellStyleBLTR( sal_Int32 nCol
, sal_Int32 nRow
) const
736 return CELL( nCol
, nRow
).GetStyleBLTR();
739 const Style
& Array::GetCellStyleTL( sal_Int32 nCol
, sal_Int32 nRow
) const
741 // not in clipping range: always invisible
742 if( !mxImpl
->IsInClipRange( nCol
, nRow
) )
743 return OBJ_STYLE_NONE
;
744 // return style only for top-left cell
745 sal_Int32 nFirstCol
= mxImpl
->GetMergedFirstCol( nCol
, nRow
);
746 sal_Int32 nFirstRow
= mxImpl
->GetMergedFirstRow( nCol
, nRow
);
747 return ((nCol
== nFirstCol
) && (nRow
== nFirstRow
)) ?
748 CELL( nFirstCol
, nFirstRow
).GetStyleTLBR() : OBJ_STYLE_NONE
;
751 const Style
& Array::GetCellStyleBR( sal_Int32 nCol
, sal_Int32 nRow
) const
753 // not in clipping range: always invisible
754 if( !mxImpl
->IsInClipRange( nCol
, nRow
) )
755 return OBJ_STYLE_NONE
;
756 // return style only for bottom-right cell
757 sal_Int32 nLastCol
= mxImpl
->GetMergedLastCol( nCol
, nRow
);
758 sal_Int32 nLastRow
= mxImpl
->GetMergedLastRow( nCol
, nRow
);
759 return ((nCol
== nLastCol
) && (nRow
== nLastRow
)) ?
760 CELL( mxImpl
->GetMergedFirstCol( nCol
, nRow
), mxImpl
->GetMergedFirstRow( nCol
, nRow
) ).GetStyleTLBR() : OBJ_STYLE_NONE
;
763 const Style
& Array::GetCellStyleBL( sal_Int32 nCol
, sal_Int32 nRow
) const
765 // not in clipping range: always invisible
766 if( !mxImpl
->IsInClipRange( nCol
, nRow
) )
767 return OBJ_STYLE_NONE
;
768 // return style only for bottom-left cell
769 sal_Int32 nFirstCol
= mxImpl
->GetMergedFirstCol( nCol
, nRow
);
770 sal_Int32 nLastRow
= mxImpl
->GetMergedLastRow( nCol
, nRow
);
771 return ((nCol
== nFirstCol
) && (nRow
== nLastRow
)) ?
772 CELL( nFirstCol
, mxImpl
->GetMergedFirstRow( nCol
, nRow
) ).GetStyleBLTR() : OBJ_STYLE_NONE
;
775 const Style
& Array::GetCellStyleTR( sal_Int32 nCol
, sal_Int32 nRow
) const
777 // not in clipping range: always invisible
778 if( !mxImpl
->IsInClipRange( nCol
, nRow
) )
779 return OBJ_STYLE_NONE
;
780 // return style only for top-right cell
781 sal_Int32 nFirstRow
= mxImpl
->GetMergedFirstRow( nCol
, nRow
);
782 sal_Int32 nLastCol
= mxImpl
->GetMergedLastCol( nCol
, nRow
);
783 return ((nCol
== nLastCol
) && (nRow
== nFirstRow
)) ?
784 CELL( mxImpl
->GetMergedFirstCol( nCol
, nRow
), nFirstRow
).GetStyleBLTR() : OBJ_STYLE_NONE
;
788 void Array::SetMergedRange( sal_Int32 nFirstCol
, sal_Int32 nFirstRow
, sal_Int32 nLastCol
, sal_Int32 nLastRow
)
790 DBG_FRAME_CHECK_COLROW( nFirstCol
, nFirstRow
, "SetMergedRange" );
791 DBG_FRAME_CHECK_COLROW( nLastCol
, nLastRow
, "SetMergedRange" );
792 #if OSL_DEBUG_LEVEL >= 2
795 for( sal_Int32 nCurrCol
= nFirstCol
; !bFound
&& (nCurrCol
<= nLastCol
); ++nCurrCol
)
796 for( sal_Int32 nCurrRow
= nFirstRow
; !bFound
&& (nCurrRow
<= nLastRow
); ++nCurrRow
)
797 bFound
= CELL( nCurrCol
, nCurrRow
).IsMerged();
798 DBG_FRAME_CHECK( !bFound
, "SetMergedRange", "overlapping merged ranges" );
801 if( mxImpl
->IsValidPos( nFirstCol
, nFirstRow
) && mxImpl
->IsValidPos( nLastCol
, nLastRow
) )
802 lclSetMergedRange( mxImpl
->maCells
, mxImpl
->mnWidth
, nFirstCol
, nFirstRow
, nLastCol
, nLastRow
);
805 void Array::SetAddMergedLeftSize( sal_Int32 nCol
, sal_Int32 nRow
, sal_Int32 nAddSize
)
807 DBG_FRAME_CHECK_COLROW( nCol
, nRow
, "SetAddMergedLeftSize" );
808 DBG_FRAME_CHECK( mxImpl
->GetMergedFirstCol( nCol
, nRow
) == 0, "SetAddMergedLeftSize", "additional border inside array" );
809 for( MergedCellIterator
aIt( *this, nCol
, nRow
); aIt
.Is(); ++aIt
)
810 CELLACC( aIt
.Col(), aIt
.Row() ).mnAddLeft
= nAddSize
;
813 void Array::SetAddMergedRightSize( sal_Int32 nCol
, sal_Int32 nRow
, sal_Int32 nAddSize
)
815 DBG_FRAME_CHECK_COLROW( nCol
, nRow
, "SetAddMergedRightSize" );
816 DBG_FRAME_CHECK( mxImpl
->GetMergedLastCol( nCol
, nRow
) + 1 == mxImpl
->mnWidth
, "SetAddMergedRightSize", "additional border inside array" );
817 for( MergedCellIterator
aIt( *this, nCol
, nRow
); aIt
.Is(); ++aIt
)
818 CELLACC( aIt
.Col(), aIt
.Row() ).mnAddRight
= nAddSize
;
821 void Array::SetAddMergedTopSize( sal_Int32 nCol
, sal_Int32 nRow
, sal_Int32 nAddSize
)
823 DBG_FRAME_CHECK_COLROW( nCol
, nRow
, "SetAddMergedTopSize" );
824 DBG_FRAME_CHECK( mxImpl
->GetMergedFirstRow( nCol
, nRow
) == 0, "SetAddMergedTopSize", "additional border inside array" );
825 for( MergedCellIterator
aIt( *this, nCol
, nRow
); aIt
.Is(); ++aIt
)
826 CELLACC( aIt
.Col(), aIt
.Row() ).mnAddTop
= nAddSize
;
829 void Array::SetAddMergedBottomSize( sal_Int32 nCol
, sal_Int32 nRow
, sal_Int32 nAddSize
)
831 DBG_FRAME_CHECK_COLROW( nCol
, nRow
, "SetAddMergedBottomSize" );
832 DBG_FRAME_CHECK( mxImpl
->GetMergedLastRow( nCol
, nRow
) + 1 == mxImpl
->mnHeight
, "SetAddMergedBottomSize", "additional border inside array" );
833 for( MergedCellIterator
aIt( *this, nCol
, nRow
); aIt
.Is(); ++aIt
)
834 CELLACC( aIt
.Col(), aIt
.Row() ).mnAddBottom
= nAddSize
;
837 bool Array::IsMerged( sal_Int32 nCol
, sal_Int32 nRow
) const
839 DBG_FRAME_CHECK_COLROW( nCol
, nRow
, "IsMerged" );
840 return CELL( nCol
, nRow
).IsMerged();
843 void Array::GetMergedOrigin( sal_Int32
& rnFirstCol
, sal_Int32
& rnFirstRow
, sal_Int32 nCol
, sal_Int32 nRow
) const
845 DBG_FRAME_CHECK_COLROW( nCol
, nRow
, "GetMergedOrigin" );
846 rnFirstCol
= mxImpl
->GetMergedFirstCol( nCol
, nRow
);
847 rnFirstRow
= mxImpl
->GetMergedFirstRow( nCol
, nRow
);
850 void Array::GetMergedRange( sal_Int32
& rnFirstCol
, sal_Int32
& rnFirstRow
,
851 sal_Int32
& rnLastCol
, sal_Int32
& rnLastRow
, sal_Int32 nCol
, sal_Int32 nRow
) const
853 GetMergedOrigin( rnFirstCol
, rnFirstRow
, nCol
, nRow
);
854 rnLastCol
= mxImpl
->GetMergedLastCol( nCol
, nRow
);
855 rnLastRow
= mxImpl
->GetMergedLastRow( nCol
, nRow
);
859 void Array::SetClipRange( sal_Int32 nFirstCol
, sal_Int32 nFirstRow
, sal_Int32 nLastCol
, sal_Int32 nLastRow
)
861 DBG_FRAME_CHECK_COLROW( nFirstCol
, nFirstRow
, "SetClipRange" );
862 DBG_FRAME_CHECK_COLROW( nLastCol
, nLastRow
, "SetClipRange" );
863 mxImpl
->mnFirstClipCol
= nFirstCol
;
864 mxImpl
->mnFirstClipRow
= nFirstRow
;
865 mxImpl
->mnLastClipCol
= nLastCol
;
866 mxImpl
->mnLastClipRow
= nLastRow
;
870 void Array::SetXOffset( sal_Int32 nXOffset
)
872 mxImpl
->maXCoords
[ 0 ] = nXOffset
;
873 mxImpl
->mbXCoordsDirty
= true;
876 void Array::SetYOffset( sal_Int32 nYOffset
)
878 mxImpl
->maYCoords
[ 0 ] = nYOffset
;
879 mxImpl
->mbYCoordsDirty
= true;
882 void Array::SetColWidth( sal_Int32 nCol
, sal_Int32 nWidth
)
884 DBG_FRAME_CHECK_COL( nCol
, "SetColWidth" );
885 mxImpl
->maWidths
[ nCol
] = nWidth
;
886 mxImpl
->mbXCoordsDirty
= true;
889 void Array::SetRowHeight( sal_Int32 nRow
, sal_Int32 nHeight
)
891 DBG_FRAME_CHECK_ROW( nRow
, "SetRowHeight" );
892 mxImpl
->maHeights
[ nRow
] = nHeight
;
893 mxImpl
->mbYCoordsDirty
= true;
896 void Array::SetAllColWidths( sal_Int32 nWidth
)
898 std::fill( mxImpl
->maWidths
.begin(), mxImpl
->maWidths
.end(), nWidth
);
899 mxImpl
->mbXCoordsDirty
= true;
902 void Array::SetAllRowHeights( sal_Int32 nHeight
)
904 std::fill( mxImpl
->maHeights
.begin(), mxImpl
->maHeights
.end(), nHeight
);
905 mxImpl
->mbYCoordsDirty
= true;
908 sal_Int32
Array::GetColPosition( sal_Int32 nCol
) const
910 DBG_FRAME_CHECK_COL_1( nCol
, "GetColPosition" );
911 return mxImpl
->GetColPosition( nCol
);
914 sal_Int32
Array::GetRowPosition( sal_Int32 nRow
) const
916 DBG_FRAME_CHECK_ROW_1( nRow
, "GetRowPosition" );
917 return mxImpl
->GetRowPosition( nRow
);
920 sal_Int32
Array::GetColWidth( sal_Int32 nFirstCol
, sal_Int32 nLastCol
) const
922 DBG_FRAME_CHECK_COL( nFirstCol
, "GetColWidth" );
923 DBG_FRAME_CHECK_COL( nLastCol
, "GetColWidth" );
924 return GetColPosition( nLastCol
+ 1 ) - GetColPosition( nFirstCol
);
927 sal_Int32
Array::GetRowHeight( sal_Int32 nFirstRow
, sal_Int32 nLastRow
) const
929 DBG_FRAME_CHECK_ROW( nFirstRow
, "GetRowHeight" );
930 DBG_FRAME_CHECK_ROW( nLastRow
, "GetRowHeight" );
931 return GetRowPosition( nLastRow
+ 1 ) - GetRowPosition( nFirstRow
);
934 sal_Int32
Array::GetWidth() const
936 return GetColPosition( mxImpl
->mnWidth
) - GetColPosition( 0 );
939 sal_Int32
Array::GetHeight() const
941 return GetRowPosition( mxImpl
->mnHeight
) - GetRowPosition( 0 );
944 basegfx::B2DRange
Array::GetCellRange( sal_Int32 nCol
, sal_Int32 nRow
) const
946 // get the Range of the fully expanded cell (if merged)
947 const sal_Int32
nFirstCol(mxImpl
->GetMergedFirstCol( nCol
, nRow
));
948 const sal_Int32
nFirstRow(mxImpl
->GetMergedFirstRow( nCol
, nRow
));
949 const sal_Int32
nLastCol(mxImpl
->GetMergedLastCol( nCol
, nRow
));
950 const sal_Int32
nLastRow(mxImpl
->GetMergedLastRow( nCol
, nRow
));
951 const Point
aPoint( GetColPosition( nFirstCol
), GetRowPosition( nFirstRow
) );
952 const Size
aSize( GetColWidth( nFirstCol
, nLastCol
) + 1, GetRowHeight( nFirstRow
, nLastRow
) + 1 );
953 tools::Rectangle
aRect(aPoint
, aSize
);
955 // adjust rectangle for partly visible merged cells
956 const Cell
& rCell
= CELL( nCol
, nRow
);
958 if( rCell
.IsMerged() )
960 // not *sure* what exactly this is good for,
961 // it is just a hard set extension at merged cells,
962 // probably *should* be included in the above extended
963 // GetColPosition/GetColWidth already. This might be
964 // added due to GetColPosition/GetColWidth not working
965 // correctly over PageChanges (if used), but not sure.
966 aRect
.AdjustLeft( -(rCell
.mnAddLeft
) );
967 aRect
.AdjustRight(rCell
.mnAddRight
);
968 aRect
.AdjustTop( -(rCell
.mnAddTop
) );
969 aRect
.AdjustBottom(rCell
.mnAddBottom
);
972 return vcl::unotools::b2DRectangleFromRectangle(aRect
);
975 // return output range of given row/col range in logical coordinates
976 basegfx::B2DRange
Array::GetB2DRange(sal_Int32 nFirstCol
, sal_Int32 nFirstRow
, sal_Int32 nLastCol
, sal_Int32 nLastRow
) const
978 const Point
aPoint( GetColPosition( nFirstCol
), GetRowPosition( nFirstRow
) );
979 const Size
aSize( GetColWidth( nFirstCol
, nLastCol
) + 1, GetRowHeight( nFirstRow
, nLastRow
) + 1 );
981 return vcl::unotools::b2DRectangleFromRectangle(tools::Rectangle(aPoint
, aSize
));
985 void Array::MirrorSelfX()
988 aNewCells
.reserve( GetCellCount() );
990 sal_Int32 nCol
, nRow
;
991 for( nRow
= 0; nRow
< mxImpl
->mnHeight
; ++nRow
)
993 for( nCol
= 0; nCol
< mxImpl
->mnWidth
; ++nCol
)
995 aNewCells
.push_back( CELL( mxImpl
->GetMirrorCol( nCol
), nRow
) );
996 aNewCells
.back().MirrorSelfX();
999 for( nRow
= 0; nRow
< mxImpl
->mnHeight
; ++nRow
)
1001 for( nCol
= 0; nCol
< mxImpl
->mnWidth
; ++nCol
)
1003 if( CELL( nCol
, nRow
).mbMergeOrig
)
1005 sal_Int32 nLastCol
= mxImpl
->GetMergedLastCol( nCol
, nRow
);
1006 sal_Int32 nLastRow
= mxImpl
->GetMergedLastRow( nCol
, nRow
);
1007 lclSetMergedRange( aNewCells
, mxImpl
->mnWidth
,
1008 mxImpl
->GetMirrorCol( nLastCol
), nRow
,
1009 mxImpl
->GetMirrorCol( nCol
), nLastRow
);
1013 mxImpl
->maCells
.swap( aNewCells
);
1015 std::reverse( mxImpl
->maWidths
.begin(), mxImpl
->maWidths
.end() );
1016 mxImpl
->mbXCoordsDirty
= true;
1020 static void HelperCreateHorizontalEntry(
1021 const Array
& rArray
,
1022 const Style
& rStyle
,
1025 const basegfx::B2DPoint
& rOrigin
,
1026 const basegfx::B2DVector
& rX
,
1027 const basegfx::B2DVector
& rY
,
1028 drawinglayer::primitive2d::SdrFrameBorderDataVector
& rData
,
1030 const Color
* pForceColor
)
1032 // prepare SdrFrameBorderData
1034 bUpper
? rOrigin
: basegfx::B2DPoint(rOrigin
+ rY
),
1038 drawinglayer::primitive2d::SdrFrameBorderData
& rInstance(rData
.back());
1040 // get involved styles at start
1041 const Style
& rStartFromTR(rArray
.GetCellStyleBL( col
, row
- 1 ));
1042 const Style
& rStartLFromT(rArray
.GetCellStyleLeft( col
, row
- 1 ));
1043 const Style
& rStartLFromL(rArray
.GetCellStyleTop( col
- 1, row
));
1044 const Style
& rStartLFromB(rArray
.GetCellStyleLeft( col
, row
));
1045 const Style
& rStartFromBR(rArray
.GetCellStyleTL( col
, row
));
1047 rInstance
.addSdrConnectStyleData(true, rStartFromTR
, rX
- rY
, false);
1048 rInstance
.addSdrConnectStyleData(true, rStartLFromT
, -rY
, true);
1049 rInstance
.addSdrConnectStyleData(true, rStartLFromL
, -rX
, true);
1050 rInstance
.addSdrConnectStyleData(true, rStartLFromB
, rY
, false);
1051 rInstance
.addSdrConnectStyleData(true, rStartFromBR
, rX
+ rY
, false);
1053 // get involved styles at end
1054 const Style
& rEndFromTL(rArray
.GetCellStyleBR( col
, row
- 1 ));
1055 const Style
& rEndRFromT(rArray
.GetCellStyleRight( col
, row
- 1 ));
1056 const Style
& rEndRFromR(rArray
.GetCellStyleTop( col
+ 1, row
));
1057 const Style
& rEndRFromB(rArray
.GetCellStyleRight( col
, row
));
1058 const Style
& rEndFromBL(rArray
.GetCellStyleTR( col
, row
));
1060 rInstance
.addSdrConnectStyleData(false, rEndFromTL
, -rX
- rY
, true);
1061 rInstance
.addSdrConnectStyleData(false, rEndRFromT
, -rY
, true);
1062 rInstance
.addSdrConnectStyleData(false, rEndRFromR
, rX
, false);
1063 rInstance
.addSdrConnectStyleData(false, rEndRFromB
, rY
, false);
1064 rInstance
.addSdrConnectStyleData(false, rEndFromBL
, rY
- rX
, true);
1067 static void HelperCreateVerticalEntry(
1068 const Array
& rArray
,
1069 const Style
& rStyle
,
1072 const basegfx::B2DPoint
& rOrigin
,
1073 const basegfx::B2DVector
& rX
,
1074 const basegfx::B2DVector
& rY
,
1075 drawinglayer::primitive2d::SdrFrameBorderDataVector
& rData
,
1077 const Color
* pForceColor
)
1079 // prepare SdrFrameBorderData
1081 bLeft
? rOrigin
: basegfx::B2DPoint(rOrigin
+ rX
),
1085 drawinglayer::primitive2d::SdrFrameBorderData
& rInstance(rData
.back());
1087 // get involved styles at start
1088 const Style
& rStartFromBL(rArray
.GetCellStyleTR( col
- 1, row
));
1089 const Style
& rStartTFromL(rArray
.GetCellStyleTop( col
- 1, row
));
1090 const Style
& rStartTFromT(rArray
.GetCellStyleLeft( col
, row
- 1 ));
1091 const Style
& rStartTFromR(rArray
.GetCellStyleTop( col
, row
));
1092 const Style
& rStartFromBR(rArray
.GetCellStyleTL( col
, row
));
1094 rInstance
.addSdrConnectStyleData(true, rStartFromBR
, rX
+ rY
, false);
1095 rInstance
.addSdrConnectStyleData(true, rStartTFromR
, rX
, false);
1096 rInstance
.addSdrConnectStyleData(true, rStartTFromT
, -rY
, true);
1097 rInstance
.addSdrConnectStyleData(true, rStartTFromL
, -rX
, true);
1098 rInstance
.addSdrConnectStyleData(true, rStartFromBL
, rY
- rX
, true);
1100 // get involved styles at end
1101 const Style
& rEndFromTL(rArray
.GetCellStyleBR( col
- 1, row
));
1102 const Style
& rEndBFromL(rArray
.GetCellStyleBottom( col
- 1, row
));
1103 const Style
& rEndBFromB(rArray
.GetCellStyleLeft( col
, row
+ 1 ));
1104 const Style
& rEndBFromR(rArray
.GetCellStyleBottom( col
, row
));
1105 const Style
& rEndFromTR(rArray
.GetCellStyleBL( col
, row
));
1107 rInstance
.addSdrConnectStyleData(false, rEndFromTR
, rX
- rY
, false);
1108 rInstance
.addSdrConnectStyleData(false, rEndBFromR
, rX
, false);
1109 rInstance
.addSdrConnectStyleData(false, rEndBFromB
, rY
, false);
1110 rInstance
.addSdrConnectStyleData(false, rEndBFromL
, -rX
, true);
1111 rInstance
.addSdrConnectStyleData(false, rEndFromTL
, -rY
- rX
, true);
1114 static void HelperClipLine(
1115 basegfx::B2DPoint
& rStart
,
1116 basegfx::B2DVector
& rDirection
,
1117 const basegfx::B2DRange
& rClipRange
)
1119 basegfx::B2DPolygon
aLine({rStart
, rStart
+ rDirection
});
1120 const basegfx::B2DPolyPolygon
aResultPP(
1121 basegfx::utils::clipPolygonOnRange(
1127 if(aResultPP
.count() > 0)
1129 const basegfx::B2DPolygon
aResultP(aResultPP
.getB2DPolygon(0));
1131 if(aResultP
.count() > 0)
1133 const basegfx::B2DPoint
aResultStart(aResultP
.getB2DPoint(0));
1134 const basegfx::B2DPoint
aResultEnd(aResultP
.getB2DPoint(aResultP
.count() - 1));
1136 if(aResultStart
!= aResultEnd
)
1138 rStart
= aResultStart
;
1139 rDirection
= aResultEnd
- aResultStart
;
1145 static void HelperCreateTLBREntry(
1146 const Array
& rArray
,
1147 const Style
& rStyle
,
1148 drawinglayer::primitive2d::SdrFrameBorderDataVector
& rData
,
1149 const basegfx::B2DPoint
& rOrigin
,
1150 const basegfx::B2DVector
& rX
,
1151 const basegfx::B2DVector
& rY
,
1153 sal_Int32 nColRight
,
1155 sal_Int32 nRowBottom
,
1156 const Color
* pForceColor
,
1157 const basegfx::B2DRange
* pClipRange
)
1161 /// prepare geometry line data
1162 basegfx::B2DPoint
aStart(rOrigin
);
1163 basegfx::B2DVector
aDirection(rX
+ rY
);
1165 /// check if we need to clip geometry line data and do it
1166 if(nullptr != pClipRange
)
1168 HelperClipLine(aStart
, aDirection
, *pClipRange
);
1171 /// top-left and bottom-right Style Tables
1177 drawinglayer::primitive2d::SdrFrameBorderData
& rInstance(rData
.back());
1179 /// Fill top-left Style Table
1180 const Style
& rTLFromRight(rArray
.GetCellStyleTop(nColLeft
, nRowTop
));
1181 const Style
& rTLFromBottom(rArray
.GetCellStyleLeft(nColLeft
, nRowTop
));
1183 rInstance
.addSdrConnectStyleData(true, rTLFromRight
, rX
, false);
1184 rInstance
.addSdrConnectStyleData(true, rTLFromBottom
, rY
, false);
1186 /// Fill bottom-right Style Table
1187 const Style
& rBRFromBottom(rArray
.GetCellStyleRight(nColRight
, nRowBottom
));
1188 const Style
& rBRFromLeft(rArray
.GetCellStyleBottom(nColRight
, nRowBottom
));
1190 rInstance
.addSdrConnectStyleData(false, rBRFromBottom
, -rY
, true);
1191 rInstance
.addSdrConnectStyleData(false, rBRFromLeft
, -rX
, true);
1195 static void HelperCreateBLTREntry(
1196 const Array
& rArray
,
1197 const Style
& rStyle
,
1198 drawinglayer::primitive2d::SdrFrameBorderDataVector
& rData
,
1199 const basegfx::B2DPoint
& rOrigin
,
1200 const basegfx::B2DVector
& rX
,
1201 const basegfx::B2DVector
& rY
,
1203 sal_Int32 nColRight
,
1205 sal_Int32 nRowBottom
,
1206 const Color
* pForceColor
,
1207 const basegfx::B2DRange
* pClipRange
)
1211 /// prepare geometry line data
1212 basegfx::B2DPoint
aStart(rOrigin
+ rY
);
1213 basegfx::B2DVector
aDirection(rX
- rY
);
1215 /// check if we need to clip geometry line data and do it
1216 if(nullptr != pClipRange
)
1218 HelperClipLine(aStart
, aDirection
, *pClipRange
);
1221 /// bottom-left and top-right Style Tables
1227 drawinglayer::primitive2d::SdrFrameBorderData
& rInstance(rData
.back());
1229 /// Fill bottom-left Style Table
1230 const Style
& rBLFromTop(rArray
.GetCellStyleLeft(nColLeft
, nRowBottom
));
1231 const Style
& rBLFromBottom(rArray
.GetCellStyleBottom(nColLeft
, nRowBottom
));
1233 rInstance
.addSdrConnectStyleData(true, rBLFromTop
, -rY
, true);
1234 rInstance
.addSdrConnectStyleData(true, rBLFromBottom
, rX
, false);
1236 /// Fill top-right Style Table
1237 const Style
& rTRFromLeft(rArray
.GetCellStyleTop(nColRight
, nRowTop
));
1238 const Style
& rTRFromBottom(rArray
.GetCellStyleRight(nColRight
, nRowTop
));
1240 rInstance
.addSdrConnectStyleData(false, rTRFromLeft
, -rX
, true);
1241 rInstance
.addSdrConnectStyleData(false, rTRFromBottom
, rY
, false);
1245 drawinglayer::primitive2d::Primitive2DContainer
Array::CreateB2DPrimitiveRange(
1246 sal_Int32 nFirstCol
, sal_Int32 nFirstRow
, sal_Int32 nLastCol
, sal_Int32 nLastRow
,
1247 const Color
* pForceColor
) const
1249 DBG_FRAME_CHECK_COLROW( nFirstCol
, nFirstRow
, "CreateB2DPrimitiveRange" );
1250 DBG_FRAME_CHECK_COLROW( nLastCol
, nLastRow
, "CreateB2DPrimitiveRange" );
1252 #ifdef OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL
1253 std::vector
<basegfx::B2DRange
> aClipRanges
;
1256 // It may be necessary to extend the loop ranges by one cell to the outside,
1257 // when possible. This is needed e.g. when there is in Calc a Cell with an
1258 // upper CellBorder using DoubleLine and that is right/left connected upwards
1259 // to also DoubleLine. These upper DoubleLines will be extended to meet the
1260 // lower of the upper CellBorder and thus have graphical parts that are
1261 // displayed one cell below and right/left of the target cell - analog to
1262 // other examples in all other directions.
1263 // It would be possible to explicitly test this (if possible by indices at all)
1264 // looping and testing the styles in the outer cells to detect this, but since
1265 // for other usages (e.g. UI) usually nFirstRow==0 and nLastRow==GetRowCount()-1
1266 // (and analog for Col) it is okay to just expand the range when available.
1267 // Do *not* change nFirstRow/nLastRow due to these needed to the boolean tests
1269 // Checked usages, this method is used in Calc EditView/Print/Export stuff and
1270 // in UI (Dialog), not for Writer Tables and Draw/Impress tables. All usages
1271 // seem okay with this change, so I will add it.
1272 const sal_Int32
nStartRow(nFirstRow
> 0 ? nFirstRow
- 1 : nFirstRow
);
1273 const sal_Int32
nEndRow(nLastRow
< GetRowCount() - 1 ? nLastRow
+ 1 : nLastRow
);
1274 const sal_Int32
nStartCol(nFirstCol
> 0 ? nFirstCol
- 1 : nFirstCol
);
1275 const sal_Int32
nEndCol(nLastCol
< GetColCount() - 1 ? nLastCol
+ 1 : nLastCol
);
1277 // prepare SdrFrameBorderDataVector
1278 drawinglayer::primitive2d::SdrFrameBorderDataVector aData
;
1280 // remember for which merged cells crossed lines were already created. To
1281 // do so, hold the sal_Int32 cell index in a set for fast check
1282 std::unordered_set
< sal_Int32
> aMergedCells
;
1284 for (sal_Int32
nRow(nStartRow
); nRow
<= nEndRow
; ++nRow
)
1286 for (sal_Int32
nCol(nStartCol
); nCol
<= nEndCol
; ++nCol
)
1288 // get Cell and CoordinateSystem (*only* for this Cell, do *not* expand for
1289 // merged cells (!)), check if used (non-empty vectors)
1290 const Cell
& rCell(CELL(nCol
, nRow
));
1291 basegfx::B2DHomMatrix
aCoordinateSystem(rCell
.CreateCoordinateSystemSingleCell(*this, nCol
, nRow
));
1292 basegfx::B2DVector
aX(basegfx::utils::getColumn(aCoordinateSystem
, 0));
1293 basegfx::B2DVector
aY(basegfx::utils::getColumn(aCoordinateSystem
, 1));
1295 // get needed local values
1296 basegfx::B2DPoint
aOrigin(basegfx::utils::getColumn(aCoordinateSystem
, 2));
1297 const bool bOverlapX(rCell
.mbOverlapX
);
1298 const bool bFirstCol(nCol
== nFirstCol
);
1300 // handle rotation: If cell is rotated, handle lower/right edge inside
1301 // this local geometry due to the created CoordinateSystem already representing
1302 // the needed transformations.
1303 const bool bRotated(rCell
.IsRotated());
1305 // Additionally avoid double-handling by suppressing handling when self not rotated,
1306 // but above/left is rotated and thus already handled. Two directly connected
1307 // rotated will paint/create both edges, they might be rotated differently.
1308 const bool bSuppressLeft(!bRotated
&& nCol
> nFirstCol
&& CELL(nCol
- 1, nRow
).IsRotated());
1309 const bool bSuppressAbove(!bRotated
&& nRow
> nFirstRow
&& CELL(nCol
, nRow
- 1).IsRotated());
1311 if(!aX
.equalZero() && !aY
.equalZero())
1313 // additionally needed local values
1314 const bool bOverlapY(rCell
.mbOverlapY
);
1315 const bool bLastCol(nCol
== nLastCol
);
1316 const bool bFirstRow(nRow
== nFirstRow
);
1317 const bool bLastRow(nRow
== nLastRow
);
1319 // create upper line for this Cell
1320 if ((!bOverlapY
// true for first line in merged cells or cells
1321 || bFirstRow
) // true for non_Calc usages of this tooling
1322 && !bSuppressAbove
) // true when above is not rotated, so edge is already handled (see bRotated)
1324 // get CellStyle - method will take care to get the correct one, e.g.
1325 // for merged cells (it uses ORIGCELL that works with topLeft's of these)
1326 const Style
& rTop(GetCellStyleTop(nCol
, nRow
));
1330 HelperCreateHorizontalEntry(*this, rTop
, nCol
, nRow
, aOrigin
, aX
, aY
, aData
, true, pForceColor
);
1334 // create lower line for this Cell
1335 if (bLastRow
// true for non_Calc usages of this tooling
1336 || bRotated
) // true if cell is rotated, handle lower edge in local geometry
1338 const Style
& rBottom(GetCellStyleBottom(nCol
, nRow
));
1340 if(rBottom
.IsUsed())
1342 HelperCreateHorizontalEntry(*this, rBottom
, nCol
, nRow
+ 1, aOrigin
, aX
, aY
, aData
, false, pForceColor
);
1346 // create left line for this Cell
1347 if ((!bOverlapX
// true for first column in merged cells or cells
1348 || bFirstCol
) // true for non_Calc usages of this tooling
1349 && !bSuppressLeft
) // true when left is not rotated, so edge is already handled (see bRotated)
1351 const Style
& rLeft(GetCellStyleLeft(nCol
, nRow
));
1355 HelperCreateVerticalEntry(*this, rLeft
, nCol
, nRow
, aOrigin
, aX
, aY
, aData
, true, pForceColor
);
1359 // create right line for this Cell
1360 if (bLastCol
// true for non_Calc usages of this tooling
1361 || bRotated
) // true if cell is rotated, handle right edge in local geometry
1363 const Style
& rRight(GetCellStyleRight(nCol
, nRow
));
1367 HelperCreateVerticalEntry(*this, rRight
, nCol
+ 1, nRow
, aOrigin
, aX
, aY
, aData
, false, pForceColor
);
1371 // tdf#126269 check for crossed lines, these need special treatment, especially
1372 // for merged cells (see comments in task). Separate treatment of merged and
1373 // non-merged cells to allow better handling of both types
1374 if(rCell
.IsMerged())
1376 // first check if this merged cell was already handled. To do so,
1377 // calculate and use the index of the TopLeft cell
1378 sal_Int32
nColLeft(nCol
), nRowTop(nRow
), nColRight(nCol
), nRowBottom(nRow
);
1379 GetMergedRange(nColLeft
, nRowTop
, nColRight
, nRowBottom
, nCol
, nRow
);
1380 const sal_Int32
nIndexOfMergedCell(mxImpl
->GetIndex(nColLeft
, nRowTop
));
1382 auto aItInsertedPair
= aMergedCells
.insert(nIndexOfMergedCell
);
1383 if(aItInsertedPair
.second
)
1385 // not found, so not yet handled.
1387 // Get and check if diagonal styles are used
1388 // Note: For GetCellStyleBLTR below I tried to use nRowBottom
1389 // as Y-value what seemed more logical, but that
1390 // is wrong. Despite defining a line starting at
1391 // bottom-left, the Style is defined in the cell at top-left
1392 const Style
& rTLBR(GetCellStyleTLBR(nColLeft
, nRowTop
));
1393 const Style
& rBLTR(GetCellStyleBLTR(nColLeft
, nRowTop
));
1395 if(rTLBR
.IsUsed() || rBLTR
.IsUsed())
1397 // test if merged cell overlaps ClipRange at all (needs visualization)
1398 if(mxImpl
->OverlapsClipRange(nColLeft
, nRowTop
, nColRight
, nRowBottom
))
1400 // when merged, get extended coordinate system and derived values
1401 // for the full range of this merged cell. Only work with rMergedCell
1402 // (which is the top-left single cell of the merged cell) from here on
1403 const Cell
& rMergedCell(CELL(nColLeft
, nRowTop
));
1404 aCoordinateSystem
= rMergedCell
.CreateCoordinateSystemMergedCell(
1405 *this, nColLeft
, nRowTop
, nColRight
, nRowBottom
);
1406 aX
= basegfx::utils::getColumn(aCoordinateSystem
, 0);
1407 aY
= basegfx::utils::getColumn(aCoordinateSystem
, 1);
1408 aOrigin
= basegfx::utils::getColumn(aCoordinateSystem
, 2);
1410 // check if clip is needed
1411 basegfx::B2DRange aClipRange
;
1413 // first use row/col ClipTest for raw check
1415 !mxImpl
->IsColInClipRange(nColLeft
) ||
1416 !mxImpl
->IsRowInClipRange(nRowTop
) ||
1417 !mxImpl
->IsColInClipRange(nColRight
) ||
1418 !mxImpl
->IsRowInClipRange(nRowBottom
));
1422 // now get ClipRange and CellRange in logical coordinates
1423 aClipRange
= GetB2DRange(
1424 mxImpl
->mnFirstClipCol
, mxImpl
->mnFirstClipRow
,
1425 mxImpl
->mnLastClipCol
, mxImpl
->mnLastClipRow
);
1427 basegfx::B2DRange
aCellRange(
1430 nColRight
, nRowBottom
));
1432 // intersect these to get the target ClipRange, ensure
1433 // that clip is needed
1434 aClipRange
.intersect(aCellRange
);
1435 bNeedToClip
= !aClipRange
.isEmpty();
1437 #ifdef OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL
1438 aClipRanges
.push_back(aClipRange
);
1442 // create top-left to bottom-right geometry
1443 HelperCreateTLBREntry(*this, rTLBR
, aData
, aOrigin
, aX
, aY
,
1444 nColLeft
, nRowTop
, nColRight
, nRowBottom
, pForceColor
,
1445 bNeedToClip
? &aClipRange
: nullptr);
1447 // create bottom-left to top-right geometry
1448 HelperCreateBLTREntry(*this, rBLTR
, aData
, aOrigin
, aX
, aY
,
1449 nColLeft
, nRowTop
, nColRight
, nRowBottom
, pForceColor
,
1450 bNeedToClip
? &aClipRange
: nullptr);
1457 // must be in clipping range: else not visible. This
1458 // already clips completely for non-merged cells
1459 if( mxImpl
->IsInClipRange( nCol
, nRow
) )
1461 // get and check if diagonal styles are used
1462 const Style
& rTLBR(GetCellStyleTLBR(nCol
, nRow
));
1463 const Style
& rBLTR(GetCellStyleBLTR(nCol
, nRow
));
1465 if(rTLBR
.IsUsed() || rBLTR
.IsUsed())
1467 HelperCreateTLBREntry(*this, rTLBR
, aData
, aOrigin
, aX
, aY
,
1468 nCol
, nRow
, nCol
, nRow
, pForceColor
, nullptr);
1470 HelperCreateBLTREntry(*this, rBLTR
, aData
, aOrigin
, aX
, aY
,
1471 nCol
, nRow
, nCol
, nRow
, pForceColor
, nullptr);
1476 else if(!aY
.equalZero())
1478 // cell has height, but no width. Create left vertical line for this Cell
1479 if ((!bOverlapX
// true for first column in merged cells or cells
1480 || bFirstCol
) // true for non_Calc usages of this tooling
1481 && !bSuppressLeft
) // true when left is not rotated, so edge is already handled (see bRotated)
1483 const Style
& rLeft(GetCellStyleLeft(nCol
, nRow
));
1487 HelperCreateVerticalEntry(*this, rLeft
, nCol
, nRow
, aOrigin
, aX
, aY
, aData
, true, pForceColor
);
1493 // Cell has *no* size, thus no visualization
1498 // create instance of SdrFrameBorderPrimitive2D if
1499 // SdrFrameBorderDataVector is used
1500 drawinglayer::primitive2d::Primitive2DContainer aSequence
;
1505 drawinglayer::primitive2d::Primitive2DReference(
1506 new drawinglayer::primitive2d::SdrFrameBorderPrimitive2D(
1508 true))); // force visualization to minimal one discrete unit (pixel)
1511 #ifdef OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL
1512 for(auto const& rClipRange
: aClipRanges
)
1514 // draw ClipRange in yellow to allow simple interactive optical control in office
1516 drawinglayer::primitive2d::Primitive2DReference(
1517 new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
1518 basegfx::utils::createPolygonFromRect(rClipRange
),
1519 basegfx::BColor(1.0, 1.0, 0.0))));
1526 drawinglayer::primitive2d::Primitive2DContainer
Array::CreateB2DPrimitiveArray() const
1528 drawinglayer::primitive2d::Primitive2DContainer aPrimitives
;
1530 if (mxImpl
->mnWidth
&& mxImpl
->mnHeight
)
1532 aPrimitives
= CreateB2DPrimitiveRange(0, 0, mxImpl
->mnWidth
- 1, mxImpl
->mnHeight
- 1, nullptr);
1542 #undef DBG_FRAME_CHECK_ROW_1
1543 #undef DBG_FRAME_CHECK_COL_1
1544 #undef DBG_FRAME_CHECK_COLROW
1545 #undef DBG_FRAME_CHECK_ROW
1546 #undef DBG_FRAME_CHECK_COL
1547 #undef DBG_FRAME_CHECK
1551 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */