/src/libreoffice/svx/source/dialog/framelinkarray.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
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/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
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 . |
18 | | */ |
19 | | |
20 | | #include <svx/framelinkarray.hxx> |
21 | | |
22 | | #include <math.h> |
23 | | #include <vector> |
24 | | #include <unordered_set> |
25 | | #include <algorithm> |
26 | | #include <o3tl/hash_combine.hxx> |
27 | | #include <sal/log.hxx> |
28 | | #include <tools/debug.hxx> |
29 | | #include <tools/gen.hxx> |
30 | | #include <vcl/canvastools.hxx> |
31 | | #include <svx/sdr/primitive2d/sdrframeborderprimitive2d.hxx> |
32 | | #include <basegfx/matrix/b2dhommatrixtools.hxx> |
33 | | #include <basegfx/polygon/b2dpolypolygon.hxx> |
34 | | #include <basegfx/polygon/b2dpolygonclipper.hxx> |
35 | | #include <basegfx/polygon/b2dpolygon.hxx> |
36 | | |
37 | | //#define OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL |
38 | | #ifdef OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL |
39 | | #include <basegfx/polygon/b2dpolygontools.hxx> |
40 | | #include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> |
41 | | #endif |
42 | | |
43 | | namespace svx::frame { |
44 | | |
45 | | namespace { |
46 | | |
47 | | class Cell final |
48 | | { |
49 | | private: |
50 | | Style maLeft; |
51 | | Style maRight; |
52 | | Style maTop; |
53 | | Style maBottom; |
54 | | Style maTLBR; |
55 | | Style maBLTR; |
56 | | |
57 | | basegfx::B2DHomMatrix HelperCreateB2DHomMatrixFromB2DRange( |
58 | | const basegfx::B2DRange& rRange ) const; |
59 | | |
60 | | public: |
61 | | sal_Int32 mnAddLeft; |
62 | | sal_Int32 mnAddRight; |
63 | | sal_Int32 mnAddTop; |
64 | | sal_Int32 mnAddBottom; |
65 | | |
66 | | SvxRotateMode meRotMode; |
67 | | double mfOrientation; |
68 | | |
69 | | bool mbMergeOrig; |
70 | | bool mbOverlapX; |
71 | | bool mbOverlapY; |
72 | | |
73 | | public: |
74 | | explicit Cell(); |
75 | | explicit Cell(const Cell&) = default; |
76 | | |
77 | | bool operator==( const Cell& ) const; |
78 | | size_t hashCode() const; |
79 | | |
80 | 0 | void SetStyleLeft(const Style& rStyle) { maLeft = rStyle; } |
81 | 0 | void SetStyleRight(const Style& rStyle) { maRight = rStyle; } |
82 | 0 | void SetStyleTop(const Style& rStyle) { maTop = rStyle; } |
83 | 0 | void SetStyleBottom(const Style& rStyle) { maBottom = rStyle; } |
84 | 0 | void SetStyleTLBR(const Style& rStyle) { maTLBR = rStyle; } |
85 | 0 | void SetStyleBLTR(const Style& rStyle) { maBLTR = rStyle; } |
86 | | |
87 | 0 | const Style& GetStyleLeft() const { return maLeft; } |
88 | 0 | const Style& GetStyleRight() const { return maRight; } |
89 | 0 | const Style& GetStyleTop() const { return maTop; } |
90 | 0 | const Style& GetStyleBottom() const { return maBottom; } |
91 | 0 | const Style& GetStyleTLBR() const { return maTLBR; } |
92 | 0 | const Style& GetStyleBLTR() const { return maBLTR; } |
93 | | |
94 | 0 | bool IsMerged() const { return mbMergeOrig || mbOverlapX || mbOverlapY; } |
95 | 0 | bool IsRotated() const { return mfOrientation != 0.0; } |
96 | | |
97 | | void MirrorSelfX(); |
98 | | |
99 | | basegfx::B2DHomMatrix CreateCoordinateSystemSingleCell( |
100 | | const Array& rArray, sal_Int32 nCol, sal_Int32 nRow ) const; |
101 | | basegfx::B2DHomMatrix CreateCoordinateSystemMergedCell( |
102 | | const Array& rArray, sal_Int32 nColLeft, sal_Int32 nRowTop, sal_Int32 nColRight, sal_Int32 nRowBottom ) const; |
103 | | }; |
104 | | |
105 | | } |
106 | | |
107 | | typedef std::vector< const Cell* > CellVec; |
108 | | |
109 | | basegfx::B2DHomMatrix Cell::HelperCreateB2DHomMatrixFromB2DRange( |
110 | | const basegfx::B2DRange& rRange ) const |
111 | 0 | { |
112 | 0 | if( rRange.isEmpty() ) |
113 | 0 | return basegfx::B2DHomMatrix(); |
114 | | |
115 | 0 | basegfx::B2DPoint aOrigin(rRange.getMinimum()); |
116 | 0 | basegfx::B2DVector aX(rRange.getWidth(), 0.0); |
117 | 0 | basegfx::B2DVector aY(0.0, rRange.getHeight()); |
118 | |
|
119 | 0 | if (IsRotated() && SvxRotateMode::SVX_ROTATE_MODE_STANDARD != meRotMode ) |
120 | 0 | { |
121 | | // tdf#143377 We need to limit applying Skew to geometry since the closer |
122 | | // we get to 0.0 or PI the more sin(mfOrientation) will get to zero and the |
123 | | // huger the Skew effect will be. For that, use an epsilon-radius of 1/2 |
124 | | // degree around the dangerous points 0.0 and PI. |
125 | | |
126 | | // Snap to modulo to [0.0 .. 2PI[ to make compare easier |
127 | 0 | const double fSnapped(::basegfx::snapToZeroRange(mfOrientation, M_PI * 2.0)); |
128 | | |
129 | | // As a compromise, allow up to 1/2 degree |
130 | 0 | static const double fMinAng(M_PI/360.0); |
131 | | |
132 | | // Check if Skew makes sense or would be too huge |
133 | 0 | const bool bForbidSkew( |
134 | 0 | fSnapped < fMinAng || // range [0.0 .. fMinAng] |
135 | 0 | fSnapped > (M_PI * 2.0) - fMinAng || // range [PI-fMinAng .. 2PI[ |
136 | 0 | fabs(fSnapped - M_PI) < fMinAng); // range [PI-fMinAng .. PI+fMinAng] |
137 | |
|
138 | 0 | if(!bForbidSkew) |
139 | 0 | { |
140 | | // when rotated, adapt values. Get Skew (cos/sin == 1/tan) |
141 | 0 | const double fSkew(aY.getY() * (cos(mfOrientation) / sin(mfOrientation))); |
142 | |
|
143 | 0 | switch (meRotMode) |
144 | 0 | { |
145 | 0 | case SvxRotateMode::SVX_ROTATE_MODE_TOP: |
146 | | // shear Y-Axis |
147 | 0 | aY.setX(-fSkew); |
148 | 0 | break; |
149 | 0 | case SvxRotateMode::SVX_ROTATE_MODE_CENTER: |
150 | | // shear origin half, Y full |
151 | 0 | aOrigin.setX(aOrigin.getX() + (fSkew * 0.5)); |
152 | 0 | aY.setX(-fSkew); |
153 | 0 | break; |
154 | 0 | case SvxRotateMode::SVX_ROTATE_MODE_BOTTOM: |
155 | | // shear origin full, Y full |
156 | 0 | aOrigin.setX(aOrigin.getX() + fSkew); |
157 | 0 | aY.setX(-fSkew); |
158 | 0 | break; |
159 | 0 | default: // SvxRotateMode::SVX_ROTATE_MODE_STANDARD, already excluded above |
160 | 0 | break; |
161 | 0 | } |
162 | 0 | } |
163 | 0 | } |
164 | | |
165 | | // use column vectors as coordinate axes, homogen column for translation |
166 | 0 | return basegfx::utils::createCoordinateSystemTransform( aOrigin, aX, aY ); |
167 | 0 | } |
168 | | |
169 | | basegfx::B2DHomMatrix Cell::CreateCoordinateSystemSingleCell( |
170 | | const Array& rArray, sal_Int32 nCol, sal_Int32 nRow) const |
171 | 0 | { |
172 | 0 | const Point aPoint( rArray.GetColPosition( nCol ), rArray.GetRowPosition( nRow ) ); |
173 | 0 | const Size aSize( rArray.GetColWidth( nCol, nCol ) + 1, rArray.GetRowHeight( nRow, nRow ) + 1 ); |
174 | 0 | const basegfx::B2DRange aRange( vcl::unotools::b2DRectangleFromRectangle( tools::Rectangle( aPoint, aSize ) ) ); |
175 | |
|
176 | 0 | return HelperCreateB2DHomMatrixFromB2DRange( aRange ); |
177 | 0 | } |
178 | | |
179 | | basegfx::B2DHomMatrix Cell::CreateCoordinateSystemMergedCell( |
180 | | const Array& rArray, sal_Int32 nColLeft, sal_Int32 nRowTop, sal_Int32 nColRight, sal_Int32 nRowBottom) const |
181 | 0 | { |
182 | 0 | basegfx::B2DRange aRange( rArray.GetB2DRange( |
183 | 0 | nColLeft, nRowTop, nColRight, nRowBottom ) ); |
184 | | |
185 | | // adjust rectangle for partly visible merged cells |
186 | 0 | if( IsMerged() ) |
187 | 0 | { |
188 | | // not *sure* what exactly this is good for, |
189 | | // it is just a hard set extension at merged cells, |
190 | | // probably *should* be included in the above extended |
191 | | // GetColPosition/GetColWidth already. This might be |
192 | | // added due to GetColPosition/GetColWidth not working |
193 | | // correctly over PageChanges (if used), but not sure. |
194 | 0 | aRange.expand( |
195 | 0 | basegfx::B2DRange( |
196 | 0 | aRange.getMinX() - mnAddLeft, |
197 | 0 | aRange.getMinY() - mnAddTop, |
198 | 0 | aRange.getMaxX() + mnAddRight, |
199 | 0 | aRange.getMaxY() + mnAddBottom ) ); |
200 | 0 | } |
201 | |
|
202 | 0 | return HelperCreateB2DHomMatrixFromB2DRange( aRange ); |
203 | 0 | } |
204 | | |
205 | | Cell::Cell() : |
206 | 54 | mnAddLeft( 0 ), |
207 | 54 | mnAddRight( 0 ), |
208 | 54 | mnAddTop( 0 ), |
209 | 54 | mnAddBottom( 0 ), |
210 | 54 | meRotMode(SvxRotateMode::SVX_ROTATE_MODE_STANDARD ), |
211 | 54 | mfOrientation( 0.0 ), |
212 | 54 | mbMergeOrig( false ), |
213 | 54 | mbOverlapX( false ), |
214 | 54 | mbOverlapY( false ) |
215 | 54 | { |
216 | 54 | } |
217 | | |
218 | | bool Cell::operator==(const Cell& rOther) const |
219 | 0 | { |
220 | 0 | if (this == &rOther) |
221 | | // ptr compare (same instance) |
222 | 0 | return true; |
223 | | |
224 | 0 | return maLeft == rOther.maLeft |
225 | 0 | && maRight == rOther.maRight |
226 | 0 | && maTop == rOther.maTop |
227 | 0 | && maBottom == rOther.maBottom |
228 | 0 | && maTLBR == rOther.maTLBR |
229 | 0 | && maBLTR == rOther.maBLTR |
230 | 0 | && mnAddLeft == rOther.mnAddLeft |
231 | 0 | && mnAddRight == rOther.mnAddRight |
232 | 0 | && mnAddTop == rOther.mnAddTop |
233 | 0 | && mnAddBottom == rOther.mnAddBottom |
234 | 0 | && meRotMode == rOther.meRotMode |
235 | 0 | && mfOrientation == rOther.mfOrientation |
236 | 0 | && mbMergeOrig == rOther.mbMergeOrig |
237 | 0 | && mbOverlapX == rOther.mbOverlapX |
238 | 0 | && mbOverlapY == rOther.mbOverlapY; |
239 | 0 | } |
240 | | |
241 | | size_t Cell::hashCode() const |
242 | 0 | { |
243 | 0 | std::size_t seed = 0; |
244 | 0 | o3tl::hash_combine(seed, maLeft.hashCode()); |
245 | 0 | o3tl::hash_combine(seed, maRight.hashCode()); |
246 | 0 | o3tl::hash_combine(seed, maTop.hashCode()); |
247 | 0 | o3tl::hash_combine(seed, maBottom.hashCode()); |
248 | 0 | o3tl::hash_combine(seed, maTLBR.hashCode()); |
249 | 0 | o3tl::hash_combine(seed, maBLTR.hashCode()); |
250 | 0 | o3tl::hash_combine(seed, mnAddLeft); |
251 | 0 | o3tl::hash_combine(seed, mnAddRight); |
252 | 0 | o3tl::hash_combine(seed, mnAddTop); |
253 | 0 | o3tl::hash_combine(seed, mnAddBottom); |
254 | 0 | o3tl::hash_combine(seed, meRotMode); |
255 | 0 | o3tl::hash_combine(seed, mfOrientation); |
256 | 0 | o3tl::hash_combine(seed, mbMergeOrig); |
257 | 0 | o3tl::hash_combine(seed, mbOverlapX); |
258 | 0 | o3tl::hash_combine(seed, mbOverlapY); |
259 | 0 | return seed; |
260 | 0 | } |
261 | | |
262 | | void Cell::MirrorSelfX() |
263 | 0 | { |
264 | 0 | std::swap( maLeft, maRight ); |
265 | 0 | std::swap( mnAddLeft, mnAddRight ); |
266 | 0 | maLeft.MirrorSelf(); |
267 | 0 | maRight.MirrorSelf(); |
268 | 0 | mfOrientation = -mfOrientation; |
269 | 0 | } |
270 | | |
271 | | |
272 | | static void lclRecalcCoordVec( std::vector<sal_Int32>& rCoords, const std::vector<sal_Int32>& rSizes ) |
273 | 0 | { |
274 | 0 | DBG_ASSERT( rCoords.size() == rSizes.size() + 1, "lclRecalcCoordVec - inconsistent vectors" ); |
275 | 0 | auto aCIt = rCoords.begin(); |
276 | 0 | for( const auto& rSize : rSizes ) |
277 | 0 | { |
278 | 0 | *(aCIt + 1) = *aCIt + rSize; |
279 | 0 | ++aCIt; |
280 | 0 | } |
281 | 0 | } |
282 | | |
283 | | const Style OBJ_STYLE_NONE; |
284 | | const Cell OBJ_CELL_NONE; |
285 | | |
286 | | /** use hashing to speed up finding duplicates */ |
287 | | namespace |
288 | | { |
289 | | struct RegisteredCellHash |
290 | | { |
291 | | size_t operator()(const Cell* pCell) const |
292 | 0 | { |
293 | 0 | return pCell->hashCode(); |
294 | 0 | } |
295 | | }; |
296 | | |
297 | | struct RegisteredCellEquals |
298 | | { |
299 | | bool operator()(const Cell* pCell1, const Cell* pCell2) const |
300 | 0 | { |
301 | 0 | return *pCell1 == *pCell2; |
302 | 0 | } |
303 | | }; |
304 | | } |
305 | | |
306 | | struct ArrayImpl |
307 | | { |
308 | | std::unordered_set<Cell*, RegisteredCellHash, RegisteredCellEquals> maRegisteredCells; |
309 | | CellVec maCells; |
310 | | std::vector<sal_Int32> maWidths; |
311 | | std::vector<sal_Int32> maHeights; |
312 | | mutable std::vector<sal_Int32> maXCoords; |
313 | | mutable std::vector<sal_Int32> maYCoords; |
314 | | sal_Int32 mnWidth; |
315 | | sal_Int32 mnHeight; |
316 | | sal_Int32 mnFirstClipCol; |
317 | | sal_Int32 mnFirstClipRow; |
318 | | sal_Int32 mnLastClipCol; |
319 | | sal_Int32 mnLastClipRow; |
320 | | mutable bool mbXCoordsDirty; |
321 | | mutable bool mbYCoordsDirty; |
322 | | bool mbMayHaveCellRotation; |
323 | | bool mbXMirrored = false; |
324 | | |
325 | | explicit ArrayImpl( sal_Int32 nWidth, sal_Int32 nHeight ); |
326 | | ~ArrayImpl(); |
327 | | |
328 | | bool IsValidPos( sal_Int32 nCol, sal_Int32 nRow ) const |
329 | 0 | { return (nCol < mnWidth) && (nRow < mnHeight); } |
330 | | sal_Int32 GetIndex( sal_Int32 nCol, sal_Int32 nRow ) const |
331 | 0 | { return nRow * mnWidth + nCol; } |
332 | | |
333 | | const Cell* GetCell( sal_Int32 nCol, sal_Int32 nRow ) const; |
334 | | void PutCell( sal_Int32 nCol, sal_Int32 nRow, const Cell& ); |
335 | | |
336 | | sal_Int32 GetMergedFirstCol( sal_Int32 nCol, sal_Int32 nRow ) const; |
337 | | sal_Int32 GetMergedFirstRow( sal_Int32 nCol, sal_Int32 nRow ) const; |
338 | | sal_Int32 GetMergedLastCol( sal_Int32 nCol, sal_Int32 nRow ) const; |
339 | | sal_Int32 GetMergedLastRow( sal_Int32 nCol, sal_Int32 nRow ) const; |
340 | | |
341 | | const Cell* GetMergedStyleSourceCell(sal_Int32 nCol, sal_Int32 nRow) const; |
342 | | |
343 | | bool IsMergedOverlappedLeft( sal_Int32 nCol, sal_Int32 nRow ) const; |
344 | | bool IsMergedOverlappedRight( sal_Int32 nCol, sal_Int32 nRow ) const; |
345 | | bool IsMergedOverlappedTop( sal_Int32 nCol, sal_Int32 nRow ) const; |
346 | | bool IsMergedOverlappedBottom( sal_Int32 nCol, sal_Int32 nRow ) const; |
347 | | |
348 | | bool IsInClipRange( sal_Int32 nCol, sal_Int32 nRow ) const; |
349 | | bool IsColInClipRange( sal_Int32 nCol ) const; |
350 | | bool IsRowInClipRange( sal_Int32 nRow ) const; |
351 | | |
352 | | bool OverlapsClipRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow ) const; |
353 | | |
354 | 0 | sal_Int32 GetMirrorCol( sal_Int32 nCol ) const { return mnWidth - nCol - 1; } |
355 | | |
356 | | sal_Int32 GetColPosition( sal_Int32 nCol ) const; |
357 | | sal_Int32 GetRowPosition( sal_Int32 nRow ) const; |
358 | | |
359 | | bool HasCellRotation() const; |
360 | | |
361 | | const Cell* createOrFind(const Cell& rCell); |
362 | | }; |
363 | | |
364 | | static void lclSetMergedRange( ArrayImpl& rImpl, CellVec& rCells, sal_Int32 nWidth, sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow ) |
365 | 0 | { |
366 | 0 | for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol ) |
367 | 0 | { |
368 | 0 | for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow ) |
369 | 0 | { |
370 | 0 | const Cell* pCell = rCells[ nRow * nWidth + nCol ]; |
371 | 0 | Cell aTempCell(*pCell); |
372 | 0 | aTempCell.mbMergeOrig = false; |
373 | 0 | aTempCell.mbOverlapX = nCol > nFirstCol; |
374 | 0 | aTempCell.mbOverlapY = nRow > nFirstRow; |
375 | 0 | rCells[ nRow * nWidth + nCol ] = rImpl.createOrFind(aTempCell); |
376 | 0 | } |
377 | 0 | } |
378 | 0 | Cell aTempCell(*rCells[ nFirstRow * nWidth + nFirstCol ]); |
379 | 0 | aTempCell.mbMergeOrig = true; |
380 | 0 | rCells[ nFirstRow * nWidth + nFirstCol ] = rImpl.createOrFind(aTempCell); |
381 | 0 | } |
382 | | |
383 | | ArrayImpl::ArrayImpl( sal_Int32 nWidth, sal_Int32 nHeight ) : |
384 | 0 | maRegisteredCells(), |
385 | 0 | mnWidth( nWidth ), |
386 | 0 | mnHeight( nHeight ), |
387 | 0 | mnFirstClipCol( 0 ), |
388 | 0 | mnFirstClipRow( 0 ), |
389 | 0 | mnLastClipCol( nWidth - 1 ), |
390 | 0 | mnLastClipRow( nHeight - 1 ), |
391 | 0 | mbXCoordsDirty( false ), |
392 | 0 | mbYCoordsDirty( false ), |
393 | 0 | mbMayHaveCellRotation( false ) |
394 | 0 | { |
395 | 0 | const Cell* pDefaultCell = createOrFind(Cell()); |
396 | | // default-construct all vectors |
397 | 0 | maCells.resize( mnWidth * mnHeight, pDefaultCell ); |
398 | 0 | maWidths.resize( mnWidth, 0 ); |
399 | 0 | maHeights.resize( mnHeight, 0 ); |
400 | 0 | maXCoords.resize( mnWidth + 1, 0 ); |
401 | 0 | maYCoords.resize( mnHeight + 1, 0 ); |
402 | 0 | } |
403 | | |
404 | | ArrayImpl::~ArrayImpl() |
405 | 0 | { |
406 | 0 | for (auto* pCell : maRegisteredCells) |
407 | 0 | delete pCell; |
408 | 0 | } |
409 | | |
410 | | const Cell* ArrayImpl::createOrFind(const Cell& rCell) |
411 | 0 | { |
412 | 0 | auto it = maRegisteredCells.find(const_cast<Cell*>(&rCell)); |
413 | 0 | if (it != maRegisteredCells.end()) |
414 | 0 | return *it; |
415 | | |
416 | 0 | Cell* pRetval(new Cell(rCell)); |
417 | 0 | maRegisteredCells.insert(pRetval); |
418 | 0 | return pRetval; |
419 | 0 | } |
420 | | |
421 | | const Cell* ArrayImpl::GetCell( sal_Int32 nCol, sal_Int32 nRow ) const |
422 | 0 | { |
423 | 0 | return IsValidPos( nCol, nRow ) ? maCells[ GetIndex( nCol, nRow ) ] : &OBJ_CELL_NONE; |
424 | 0 | } |
425 | | |
426 | | void ArrayImpl::PutCell( sal_Int32 nCol, sal_Int32 nRow, const Cell & rCell ) |
427 | 0 | { |
428 | 0 | if (IsValidPos( nCol, nRow )) |
429 | 0 | maCells[ GetIndex( nCol, nRow ) ] = createOrFind(rCell); |
430 | 0 | } |
431 | | |
432 | | sal_Int32 ArrayImpl::GetMergedFirstCol( sal_Int32 nCol, sal_Int32 nRow ) const |
433 | 0 | { |
434 | 0 | sal_Int32 nFirstCol = nCol; |
435 | 0 | while( (nFirstCol > 0) && GetCell( nFirstCol, nRow )->mbOverlapX ) --nFirstCol; |
436 | 0 | return nFirstCol; |
437 | 0 | } |
438 | | |
439 | | sal_Int32 ArrayImpl::GetMergedFirstRow( sal_Int32 nCol, sal_Int32 nRow ) const |
440 | 0 | { |
441 | 0 | sal_Int32 nFirstRow = nRow; |
442 | 0 | while( (nFirstRow > 0) && GetCell( nCol, nFirstRow )->mbOverlapY ) --nFirstRow; |
443 | 0 | return nFirstRow; |
444 | 0 | } |
445 | | |
446 | | sal_Int32 ArrayImpl::GetMergedLastCol( sal_Int32 nCol, sal_Int32 nRow ) const |
447 | 0 | { |
448 | 0 | sal_Int32 nLastCol = nCol + 1; |
449 | 0 | while( (nLastCol < mnWidth) && GetCell( nLastCol, nRow )->mbOverlapX ) ++nLastCol; |
450 | 0 | return nLastCol - 1; |
451 | 0 | } |
452 | | |
453 | | sal_Int32 ArrayImpl::GetMergedLastRow( sal_Int32 nCol, sal_Int32 nRow ) const |
454 | 0 | { |
455 | 0 | sal_Int32 nLastRow = nRow + 1; |
456 | 0 | while( (nLastRow < mnHeight) && GetCell( nCol, nLastRow )->mbOverlapY ) ++nLastRow; |
457 | 0 | return nLastRow - 1; |
458 | 0 | } |
459 | | |
460 | | const Cell* ArrayImpl::GetMergedStyleSourceCell(sal_Int32 nCol, sal_Int32 nRow) const |
461 | 0 | { |
462 | 0 | if (mbXMirrored) |
463 | 0 | { |
464 | 0 | return GetCell(GetMergedLastCol(nCol, nRow), GetMergedFirstRow(nCol, nRow)); |
465 | 0 | } |
466 | | |
467 | 0 | return GetCell(GetMergedFirstCol(nCol, nRow), GetMergedFirstRow(nCol, nRow)); |
468 | 0 | } |
469 | | |
470 | | bool ArrayImpl::IsMergedOverlappedLeft( sal_Int32 nCol, sal_Int32 nRow ) const |
471 | 0 | { |
472 | 0 | const Cell* pCell(GetCell( nCol, nRow )); |
473 | 0 | return pCell->mbOverlapX || (pCell->mnAddLeft > 0); |
474 | 0 | } |
475 | | |
476 | | bool ArrayImpl::IsMergedOverlappedRight( sal_Int32 nCol, sal_Int32 nRow ) const |
477 | 0 | { |
478 | 0 | return GetCell( nCol + 1, nRow )->mbOverlapX || (GetCell( nCol, nRow )->mnAddRight > 0); |
479 | 0 | } |
480 | | |
481 | | bool ArrayImpl::IsMergedOverlappedTop( sal_Int32 nCol, sal_Int32 nRow ) const |
482 | 0 | { |
483 | 0 | const Cell* pCell(GetCell( nCol, nRow )); |
484 | 0 | return pCell->mbOverlapY || (pCell->mnAddTop > 0); |
485 | 0 | } |
486 | | |
487 | | bool ArrayImpl::IsMergedOverlappedBottom( sal_Int32 nCol, sal_Int32 nRow ) const |
488 | 0 | { |
489 | 0 | return GetCell( nCol, nRow + 1 )->mbOverlapY || (GetCell( nCol, nRow )->mnAddBottom > 0); |
490 | 0 | } |
491 | | |
492 | | bool ArrayImpl::IsColInClipRange( sal_Int32 nCol ) const |
493 | 0 | { |
494 | 0 | return (mnFirstClipCol <= nCol) && (nCol <= mnLastClipCol); |
495 | 0 | } |
496 | | |
497 | | bool ArrayImpl::IsRowInClipRange( sal_Int32 nRow ) const |
498 | 0 | { |
499 | 0 | return (mnFirstClipRow <= nRow) && (nRow <= mnLastClipRow); |
500 | 0 | } |
501 | | |
502 | | bool ArrayImpl::OverlapsClipRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow ) const |
503 | 0 | { |
504 | 0 | if(nLastCol < mnFirstClipCol) |
505 | 0 | return false; |
506 | | |
507 | 0 | if(nFirstCol > mnLastClipCol) |
508 | 0 | return false; |
509 | | |
510 | 0 | if(nLastRow < mnFirstClipRow) |
511 | 0 | return false; |
512 | | |
513 | 0 | if(nFirstRow > mnLastClipRow) |
514 | 0 | return false; |
515 | | |
516 | 0 | return true; |
517 | 0 | } |
518 | | |
519 | | bool ArrayImpl::IsInClipRange( sal_Int32 nCol, sal_Int32 nRow ) const |
520 | 0 | { |
521 | 0 | return IsColInClipRange( nCol ) && IsRowInClipRange( nRow ); |
522 | 0 | } |
523 | | |
524 | | sal_Int32 ArrayImpl::GetColPosition( sal_Int32 nCol ) const |
525 | 0 | { |
526 | 0 | if( mbXCoordsDirty ) |
527 | 0 | { |
528 | 0 | lclRecalcCoordVec( maXCoords, maWidths ); |
529 | 0 | mbXCoordsDirty = false; |
530 | 0 | } |
531 | 0 | return maXCoords[ nCol ]; |
532 | 0 | } |
533 | | |
534 | | sal_Int32 ArrayImpl::GetRowPosition( sal_Int32 nRow ) const |
535 | 0 | { |
536 | 0 | if( mbYCoordsDirty ) |
537 | 0 | { |
538 | 0 | lclRecalcCoordVec( maYCoords, maHeights ); |
539 | 0 | mbYCoordsDirty = false; |
540 | 0 | } |
541 | 0 | return maYCoords[ nRow ]; |
542 | 0 | } |
543 | | |
544 | | bool ArrayImpl::HasCellRotation() const |
545 | 0 | { |
546 | | // check cell array |
547 | 0 | for (const auto& aCell : maCells) |
548 | 0 | { |
549 | 0 | if (aCell->IsRotated()) |
550 | 0 | { |
551 | 0 | return true; |
552 | 0 | } |
553 | 0 | } |
554 | | |
555 | 0 | return false; |
556 | 0 | } |
557 | | |
558 | | namespace { |
559 | | |
560 | | class MergedCellIterator |
561 | | { |
562 | | public: |
563 | | explicit MergedCellIterator( const Array& rArray, sal_Int32 nCol, sal_Int32 nRow ); |
564 | | |
565 | 0 | bool Is() const { return (mnCol <= mnLastCol) && (mnRow <= mnLastRow); } |
566 | 0 | sal_Int32 Col() const { return mnCol; } |
567 | 0 | sal_Int32 Row() const { return mnRow; } |
568 | | |
569 | | MergedCellIterator& operator++(); |
570 | | |
571 | | private: |
572 | | sal_Int32 mnFirstCol; |
573 | | sal_Int32 mnFirstRow; |
574 | | sal_Int32 mnLastCol; |
575 | | sal_Int32 mnLastRow; |
576 | | sal_Int32 mnCol; |
577 | | sal_Int32 mnRow; |
578 | | }; |
579 | | |
580 | | } |
581 | | |
582 | | MergedCellIterator::MergedCellIterator( const Array& rArray, sal_Int32 nCol, sal_Int32 nRow ) |
583 | 0 | { |
584 | 0 | rArray.GetMergedRange( mnFirstCol, mnFirstRow, mnLastCol, mnLastRow, nCol, nRow ); |
585 | 0 | mnCol = mnFirstCol; |
586 | 0 | mnRow = mnFirstRow; |
587 | 0 | } |
588 | | |
589 | | MergedCellIterator& MergedCellIterator::operator++() |
590 | 0 | { |
591 | 0 | DBG_ASSERT( Is(), "svx::frame::MergedCellIterator::operator++() - already invalid" ); |
592 | 0 | if( ++mnCol > mnLastCol ) |
593 | 0 | { |
594 | 0 | mnCol = mnFirstCol; |
595 | 0 | ++mnRow; |
596 | 0 | } |
597 | 0 | return *this; |
598 | 0 | } |
599 | | |
600 | 0 | #define DBG_FRAME_CHECK( cond, funcname, error ) DBG_ASSERT( cond, "svx::frame::Array::" funcname " - " error ) |
601 | 0 | #define DBG_FRAME_CHECK_COL( col, funcname ) DBG_FRAME_CHECK( (col) < GetColCount(), funcname, "invalid column index" ) |
602 | 0 | #define DBG_FRAME_CHECK_ROW( row, funcname ) DBG_FRAME_CHECK( (row) < GetRowCount(), funcname, "invalid row index" ) |
603 | 0 | #define DBG_FRAME_CHECK_COLROW( col, row, funcname ) DBG_FRAME_CHECK( ((col) < GetColCount()) && ((row) < GetRowCount()), funcname, "invalid cell index" ) |
604 | 0 | #define DBG_FRAME_CHECK_COL_1( col, funcname ) DBG_FRAME_CHECK( (col) <= GetColCount(), funcname, "invalid column index" ) |
605 | 0 | #define DBG_FRAME_CHECK_ROW_1( row, funcname ) DBG_FRAME_CHECK( (row) <= GetRowCount(), funcname, "invalid row index" ) |
606 | | |
607 | | Array::Array() |
608 | 0 | { |
609 | 0 | Initialize( 0, 0 ); |
610 | 0 | } |
611 | | |
612 | | Array::~Array() |
613 | 0 | { |
614 | 0 | } |
615 | | |
616 | | // array size and column/row indexes |
617 | | void Array::Initialize( sal_Int32 nWidth, sal_Int32 nHeight ) |
618 | 0 | { |
619 | 0 | mxImpl.reset( new ArrayImpl( nWidth, nHeight ) ); |
620 | 0 | } |
621 | | |
622 | | sal_Int32 Array::GetColCount() const |
623 | 0 | { |
624 | 0 | return mxImpl->mnWidth; |
625 | 0 | } |
626 | | |
627 | | sal_Int32 Array::GetRowCount() const |
628 | 0 | { |
629 | 0 | return mxImpl->mnHeight; |
630 | 0 | } |
631 | | |
632 | | sal_Int32 Array::GetCellCount() const |
633 | 0 | { |
634 | 0 | return mxImpl->maCells.size(); |
635 | 0 | } |
636 | | |
637 | | sal_Int32 Array::GetCellIndex( sal_Int32 nCol, sal_Int32 nRow, bool bRTL ) const |
638 | 0 | { |
639 | 0 | DBG_FRAME_CHECK_COLROW( nCol, nRow, "GetCellIndex" ); |
640 | 0 | if (bRTL) |
641 | 0 | nCol = mxImpl->GetMirrorCol(nCol); |
642 | 0 | return mxImpl->GetIndex( nCol, nRow ); |
643 | 0 | } |
644 | | |
645 | | // cell border styles |
646 | | void Array::SetCellStyleLeft( sal_Int32 nCol, sal_Int32 nRow, const Style& rStyle ) |
647 | 0 | { |
648 | 0 | DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleLeft" ); |
649 | 0 | const Cell* pTempCell(mxImpl->GetCell(nCol, nRow)); |
650 | 0 | if (pTempCell->GetStyleLeft() == rStyle) |
651 | 0 | return; |
652 | 0 | Cell aTempCell(*pTempCell); |
653 | 0 | aTempCell.SetStyleLeft(rStyle); |
654 | 0 | mxImpl->PutCell( nCol, nRow, aTempCell ); |
655 | 0 | } |
656 | | |
657 | | void Array::SetCellStyleRight( sal_Int32 nCol, sal_Int32 nRow, const Style& rStyle ) |
658 | 0 | { |
659 | 0 | DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleRight" ); |
660 | 0 | const Cell* pTempCell(mxImpl->GetCell(nCol, nRow)); |
661 | 0 | if (pTempCell->GetStyleRight() == rStyle) |
662 | 0 | return; |
663 | 0 | Cell aTempCell(*pTempCell); |
664 | 0 | aTempCell.SetStyleRight(rStyle); |
665 | 0 | mxImpl->PutCell( nCol, nRow, aTempCell ); |
666 | 0 | } |
667 | | |
668 | | void Array::SetCellStyleTop( sal_Int32 nCol, sal_Int32 nRow, const Style& rStyle ) |
669 | 0 | { |
670 | 0 | DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleTop" ); |
671 | 0 | const Cell* pTempCell(mxImpl->GetCell(nCol, nRow)); |
672 | 0 | if (pTempCell->GetStyleTop() == rStyle) |
673 | 0 | return; |
674 | 0 | Cell aTempCell(*pTempCell); |
675 | 0 | aTempCell.SetStyleTop(rStyle); |
676 | 0 | mxImpl->PutCell( nCol, nRow, aTempCell ); |
677 | 0 | } |
678 | | |
679 | | void Array::SetCellStyleBottom( sal_Int32 nCol, sal_Int32 nRow, const Style& rStyle ) |
680 | 0 | { |
681 | 0 | DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleBottom" ); |
682 | 0 | const Cell* pTempCell(mxImpl->GetCell(nCol, nRow)); |
683 | 0 | if (pTempCell->GetStyleBottom() == rStyle) |
684 | 0 | return; |
685 | 0 | Cell aTempCell(*pTempCell); |
686 | 0 | aTempCell.SetStyleBottom(rStyle); |
687 | 0 | mxImpl->PutCell( nCol, nRow, aTempCell ); |
688 | 0 | } |
689 | | |
690 | | void Array::SetCellStyleTLBR( sal_Int32 nCol, sal_Int32 nRow, const Style& rStyle ) |
691 | 0 | { |
692 | 0 | DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleTLBR" ); |
693 | 0 | const Cell* pTempCell(mxImpl->GetCell(nCol, nRow)); |
694 | 0 | if (pTempCell->GetStyleTLBR() == rStyle) |
695 | 0 | return; |
696 | 0 | Cell aTempCell(*pTempCell); |
697 | 0 | aTempCell.SetStyleTLBR(rStyle); |
698 | 0 | mxImpl->PutCell( nCol, nRow, aTempCell ); |
699 | 0 | } |
700 | | |
701 | | void Array::SetCellStyleBLTR( sal_Int32 nCol, sal_Int32 nRow, const Style& rStyle ) |
702 | 0 | { |
703 | 0 | DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleBLTR" ); |
704 | 0 | const Cell* pTempCell(mxImpl->GetCell(nCol, nRow)); |
705 | 0 | if (pTempCell->GetStyleBLTR() == rStyle) |
706 | 0 | return; |
707 | 0 | Cell aTempCell(*pTempCell); |
708 | 0 | aTempCell.SetStyleBLTR(rStyle); |
709 | 0 | mxImpl->PutCell( nCol, nRow, aTempCell ); |
710 | 0 | } |
711 | | |
712 | | void Array::SetCellStyleDiag( sal_Int32 nCol, sal_Int32 nRow, const Style& rTLBR, const Style& rBLTR ) |
713 | 0 | { |
714 | 0 | DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleDiag" ); |
715 | 0 | const Cell* pTempCell(mxImpl->GetCell(nCol, nRow)); |
716 | 0 | if (pTempCell->GetStyleTLBR() == rTLBR && pTempCell->GetStyleBLTR() == rBLTR) |
717 | 0 | return; |
718 | 0 | Cell aTempCell(*pTempCell); |
719 | 0 | aTempCell.SetStyleTLBR(rTLBR); |
720 | 0 | aTempCell.SetStyleBLTR(rBLTR); |
721 | 0 | mxImpl->PutCell( nCol, nRow, aTempCell ); |
722 | 0 | } |
723 | | |
724 | | void Array::SetColumnStyleLeft( sal_Int32 nCol, const Style& rStyle ) |
725 | 0 | { |
726 | 0 | DBG_FRAME_CHECK_COL( nCol, "SetColumnStyleLeft" ); |
727 | 0 | for( sal_Int32 nRow = 0; nRow < mxImpl->mnHeight; ++nRow ) |
728 | 0 | SetCellStyleLeft( nCol, nRow, rStyle ); |
729 | 0 | } |
730 | | |
731 | | void Array::SetColumnStyleRight( sal_Int32 nCol, const Style& rStyle ) |
732 | 0 | { |
733 | 0 | DBG_FRAME_CHECK_COL( nCol, "SetColumnStyleRight" ); |
734 | 0 | for( sal_Int32 nRow = 0; nRow < mxImpl->mnHeight; ++nRow ) |
735 | 0 | SetCellStyleRight( nCol, nRow, rStyle ); |
736 | 0 | } |
737 | | |
738 | | void Array::SetRowStyleTop( sal_Int32 nRow, const Style& rStyle ) |
739 | 0 | { |
740 | 0 | DBG_FRAME_CHECK_ROW( nRow, "SetRowStyleTop" ); |
741 | 0 | for( sal_Int32 nCol = 0; nCol < mxImpl->mnWidth; ++nCol ) |
742 | 0 | SetCellStyleTop( nCol, nRow, rStyle ); |
743 | 0 | } |
744 | | |
745 | | void Array::SetRowStyleBottom( sal_Int32 nRow, const Style& rStyle ) |
746 | 0 | { |
747 | 0 | DBG_FRAME_CHECK_ROW( nRow, "SetRowStyleBottom" ); |
748 | 0 | for( sal_Int32 nCol = 0; nCol < mxImpl->mnWidth; ++nCol ) |
749 | 0 | SetCellStyleBottom( nCol, nRow, rStyle ); |
750 | 0 | } |
751 | | |
752 | | void Array::SetCellRotation(sal_Int32 nCol, sal_Int32 nRow, SvxRotateMode eRotMode, double fOrientation) |
753 | 0 | { |
754 | 0 | DBG_FRAME_CHECK_COLROW(nCol, nRow, "SetCellRotation"); |
755 | 0 | const Cell* pTempCell(mxImpl->GetCell(nCol, nRow)); |
756 | 0 | if (pTempCell->meRotMode == eRotMode && pTempCell->mfOrientation == fOrientation) |
757 | 0 | return; |
758 | 0 | Cell aTempCell(*pTempCell); |
759 | 0 | aTempCell.meRotMode = eRotMode; |
760 | 0 | aTempCell.mfOrientation = fOrientation; |
761 | 0 | mxImpl->PutCell( nCol, nRow, aTempCell ); |
762 | |
|
763 | 0 | if (!mxImpl->mbMayHaveCellRotation) |
764 | 0 | { |
765 | | // activate once when a cell gets actually rotated to allow fast |
766 | | // answering HasCellRotation() calls |
767 | 0 | mxImpl->mbMayHaveCellRotation = aTempCell.IsRotated(); |
768 | 0 | } |
769 | 0 | } |
770 | | |
771 | | bool Array::HasCellRotation() const |
772 | 0 | { |
773 | 0 | if (!mxImpl->mbMayHaveCellRotation) |
774 | 0 | { |
775 | | // never set, no need to check |
776 | 0 | return false; |
777 | 0 | } |
778 | | |
779 | 0 | return mxImpl->HasCellRotation(); |
780 | 0 | } |
781 | | |
782 | | const Style& Array::GetCellStyleLeft( sal_Int32 nCol, sal_Int32 nRow ) const |
783 | 0 | { |
784 | | // outside clipping rows or overlapped in merged cells: invisible |
785 | 0 | if( !mxImpl->IsRowInClipRange( nRow ) || mxImpl->IsMergedOverlappedLeft( nCol, nRow ) ) |
786 | 0 | return OBJ_STYLE_NONE; |
787 | | // left clipping border: always own left style |
788 | 0 | if( nCol == mxImpl->mnFirstClipCol ) |
789 | 0 | return mxImpl->GetMergedStyleSourceCell(nCol, nRow)->GetStyleLeft(); |
790 | | // right clipping border: always right style of left neighbor cell |
791 | 0 | if( nCol == mxImpl->mnLastClipCol + 1 ) |
792 | 0 | return mxImpl->GetMergedStyleSourceCell(nCol - 1, nRow)->GetStyleRight(); |
793 | | // outside clipping columns: invisible |
794 | 0 | if( !mxImpl->IsColInClipRange( nCol ) ) |
795 | 0 | return OBJ_STYLE_NONE; |
796 | | // inside clipping range: maximum of own left style and right style of left neighbor cell |
797 | 0 | return std::max(mxImpl->GetMergedStyleSourceCell(nCol, nRow)->GetStyleLeft(), |
798 | 0 | mxImpl->GetMergedStyleSourceCell(nCol - 1, nRow)->GetStyleRight()); |
799 | 0 | } |
800 | | |
801 | | const Style& Array::GetCellStyleRight( sal_Int32 nCol, sal_Int32 nRow ) const |
802 | 0 | { |
803 | | // outside clipping rows or overlapped in merged cells: invisible |
804 | 0 | if( !mxImpl->IsRowInClipRange( nRow ) || mxImpl->IsMergedOverlappedRight( nCol, nRow ) ) |
805 | 0 | return OBJ_STYLE_NONE; |
806 | | // left clipping border: always left style of right neighbor cell |
807 | 0 | if( nCol + 1 == mxImpl->mnFirstClipCol ) |
808 | 0 | return mxImpl->GetMergedStyleSourceCell(nCol + 1, nRow)->GetStyleLeft(); |
809 | | // right clipping border: always own right style |
810 | 0 | if( nCol == mxImpl->mnLastClipCol ) |
811 | 0 | return mxImpl->GetMergedStyleSourceCell(nCol, nRow)->GetStyleRight(); |
812 | | // outside clipping columns: invisible |
813 | 0 | if( !mxImpl->IsColInClipRange( nCol ) ) |
814 | 0 | return OBJ_STYLE_NONE; |
815 | | // inside clipping range: maximum of own right style and left style of right neighbor cell |
816 | 0 | return std::max(mxImpl->GetMergedStyleSourceCell(nCol, nRow)->GetStyleRight(), |
817 | 0 | mxImpl->GetMergedStyleSourceCell(nCol + 1, nRow)->GetStyleLeft()); |
818 | 0 | } |
819 | | |
820 | | const Style& Array::GetCellStyleTop( sal_Int32 nCol, sal_Int32 nRow ) const |
821 | 0 | { |
822 | | // outside clipping columns or overlapped in merged cells: invisible |
823 | 0 | if( !mxImpl->IsColInClipRange( nCol ) || mxImpl->IsMergedOverlappedTop( nCol, nRow ) ) |
824 | 0 | return OBJ_STYLE_NONE; |
825 | | // top clipping border: always own top style |
826 | 0 | if( nRow == mxImpl->mnFirstClipRow ) |
827 | 0 | return mxImpl->GetMergedStyleSourceCell(nCol, nRow)->GetStyleTop(); |
828 | | // bottom clipping border: always bottom style of top neighbor cell |
829 | 0 | if( nRow == mxImpl->mnLastClipRow + 1 ) |
830 | 0 | return mxImpl->GetMergedStyleSourceCell(nCol, nRow - 1)->GetStyleBottom(); |
831 | | // outside clipping rows: invisible |
832 | 0 | if( !mxImpl->IsRowInClipRange( nRow ) ) |
833 | 0 | return OBJ_STYLE_NONE; |
834 | | // inside clipping range: maximum of own top style and bottom style of top neighbor cell |
835 | 0 | return std::max(mxImpl->GetMergedStyleSourceCell(nCol, nRow)->GetStyleTop(), |
836 | 0 | mxImpl->GetMergedStyleSourceCell(nCol, nRow - 1)->GetStyleBottom()); |
837 | 0 | } |
838 | | |
839 | | const Style& Array::GetCellStyleBottom( sal_Int32 nCol, sal_Int32 nRow ) const |
840 | 0 | { |
841 | | // outside clipping columns or overlapped in merged cells: invisible |
842 | 0 | if( !mxImpl->IsColInClipRange( nCol ) || mxImpl->IsMergedOverlappedBottom( nCol, nRow ) ) |
843 | 0 | return OBJ_STYLE_NONE; |
844 | | // top clipping border: always top style of bottom neighbor cell |
845 | 0 | if( nRow + 1 == mxImpl->mnFirstClipRow ) |
846 | 0 | return mxImpl->GetMergedStyleSourceCell(nCol, nRow + 1)->GetStyleTop(); |
847 | | // bottom clipping border: always own bottom style |
848 | 0 | if( nRow == mxImpl->mnLastClipRow ) |
849 | 0 | return mxImpl->GetMergedStyleSourceCell(nCol, nRow)->GetStyleBottom(); |
850 | | // outside clipping rows: invisible |
851 | 0 | if( !mxImpl->IsRowInClipRange( nRow ) ) |
852 | 0 | return OBJ_STYLE_NONE; |
853 | | // inside clipping range: maximum of own bottom style and top style of bottom neighbor cell |
854 | 0 | return std::max(mxImpl->GetMergedStyleSourceCell(nCol, nRow)->GetStyleBottom(), |
855 | 0 | mxImpl->GetMergedStyleSourceCell(nCol, nRow + 1)->GetStyleTop()); |
856 | 0 | } |
857 | | |
858 | | const Style& Array::GetCellStyleTLBR( sal_Int32 nCol, sal_Int32 nRow ) const |
859 | 0 | { |
860 | 0 | return mxImpl->GetCell( nCol, nRow )->GetStyleTLBR(); |
861 | 0 | } |
862 | | |
863 | | const Style& Array::GetCellStyleBLTR( sal_Int32 nCol, sal_Int32 nRow ) const |
864 | 0 | { |
865 | 0 | return mxImpl->GetCell( nCol, nRow )->GetStyleBLTR(); |
866 | 0 | } |
867 | | |
868 | | const Style& Array::GetCellStyleTL( sal_Int32 nCol, sal_Int32 nRow ) const |
869 | 0 | { |
870 | | // not in clipping range: always invisible |
871 | 0 | if( !mxImpl->IsInClipRange( nCol, nRow ) ) |
872 | 0 | return OBJ_STYLE_NONE; |
873 | | // return style only for top-left cell |
874 | 0 | sal_Int32 nFirstCol = mxImpl->GetMergedFirstCol( nCol, nRow ); |
875 | 0 | sal_Int32 nFirstRow = mxImpl->GetMergedFirstRow( nCol, nRow ); |
876 | 0 | return ((nCol == nFirstCol) && (nRow == nFirstRow)) ? |
877 | 0 | mxImpl->GetCell( nFirstCol, nFirstRow )->GetStyleTLBR() : OBJ_STYLE_NONE; |
878 | 0 | } |
879 | | |
880 | | const Style& Array::GetCellStyleBR( sal_Int32 nCol, sal_Int32 nRow ) const |
881 | 0 | { |
882 | | // not in clipping range: always invisible |
883 | 0 | if( !mxImpl->IsInClipRange( nCol, nRow ) ) |
884 | 0 | return OBJ_STYLE_NONE; |
885 | | // return style only for bottom-right cell |
886 | 0 | sal_Int32 nLastCol = mxImpl->GetMergedLastCol( nCol, nRow ); |
887 | 0 | sal_Int32 nLastRow = mxImpl->GetMergedLastRow( nCol, nRow ); |
888 | 0 | return ((nCol == nLastCol) && (nRow == nLastRow)) ? |
889 | 0 | mxImpl->GetCell( mxImpl->GetMergedFirstCol( nCol, nRow ), mxImpl->GetMergedFirstRow( nCol, nRow ) )->GetStyleTLBR() : OBJ_STYLE_NONE; |
890 | 0 | } |
891 | | |
892 | | const Style& Array::GetCellStyleBL( sal_Int32 nCol, sal_Int32 nRow ) const |
893 | 0 | { |
894 | | // not in clipping range: always invisible |
895 | 0 | if( !mxImpl->IsInClipRange( nCol, nRow ) ) |
896 | 0 | return OBJ_STYLE_NONE; |
897 | | // return style only for bottom-left cell |
898 | 0 | sal_Int32 nFirstCol = mxImpl->GetMergedFirstCol( nCol, nRow ); |
899 | 0 | sal_Int32 nLastRow = mxImpl->GetMergedLastRow( nCol, nRow ); |
900 | 0 | return ((nCol == nFirstCol) && (nRow == nLastRow)) ? |
901 | 0 | mxImpl->GetCell( nFirstCol, mxImpl->GetMergedFirstRow( nCol, nRow ) )->GetStyleBLTR() : OBJ_STYLE_NONE; |
902 | 0 | } |
903 | | |
904 | | const Style& Array::GetCellStyleTR( sal_Int32 nCol, sal_Int32 nRow ) const |
905 | 0 | { |
906 | | // not in clipping range: always invisible |
907 | 0 | if( !mxImpl->IsInClipRange( nCol, nRow ) ) |
908 | 0 | return OBJ_STYLE_NONE; |
909 | | // return style only for top-right cell |
910 | 0 | sal_Int32 nFirstRow = mxImpl->GetMergedFirstRow( nCol, nRow ); |
911 | 0 | sal_Int32 nLastCol = mxImpl->GetMergedLastCol( nCol, nRow ); |
912 | 0 | return ((nCol == nLastCol) && (nRow == nFirstRow)) ? |
913 | 0 | mxImpl->GetCell( mxImpl->GetMergedFirstCol( nCol, nRow ), nFirstRow )->GetStyleBLTR() : OBJ_STYLE_NONE; |
914 | 0 | } |
915 | | |
916 | | // cell merging |
917 | | void Array::SetMergedRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow ) |
918 | 0 | { |
919 | 0 | DBG_FRAME_CHECK_COLROW( nFirstCol, nFirstRow, "SetMergedRange" ); |
920 | 0 | DBG_FRAME_CHECK_COLROW( nLastCol, nLastRow, "SetMergedRange" ); |
921 | | #if OSL_DEBUG_LEVEL >= 2 |
922 | | { |
923 | | bool bFound = false; |
924 | | for( sal_Int32 nCurrCol = nFirstCol; !bFound && (nCurrCol <= nLastCol); ++nCurrCol ) |
925 | | for( sal_Int32 nCurrRow = nFirstRow; !bFound && (nCurrRow <= nLastRow); ++nCurrRow ) |
926 | | bFound = mxImpl->GetCell( nCurrCol, nCurrRow )->IsMerged(); |
927 | | DBG_FRAME_CHECK( !bFound, "SetMergedRange", "overlapping merged ranges" ); |
928 | | } |
929 | | #endif |
930 | 0 | if( mxImpl->IsValidPos( nFirstCol, nFirstRow ) && mxImpl->IsValidPos( nLastCol, nLastRow ) ) |
931 | 0 | lclSetMergedRange( *mxImpl, mxImpl->maCells, mxImpl->mnWidth, nFirstCol, nFirstRow, nLastCol, nLastRow ); |
932 | 0 | } |
933 | | |
934 | | void Array::SetAddMergedLeftSize( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nAddSize ) |
935 | 0 | { |
936 | 0 | DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetAddMergedLeftSize" ); |
937 | 0 | DBG_FRAME_CHECK( mxImpl->GetMergedFirstCol( nCol, nRow ) == 0, "SetAddMergedLeftSize", "additional border inside array" ); |
938 | 0 | for( MergedCellIterator aIt( *this, nCol, nRow ); aIt.Is(); ++aIt ) |
939 | 0 | { |
940 | 0 | const Cell* pTempCell(mxImpl->GetCell(aIt.Col(), aIt.Row())); |
941 | 0 | if (pTempCell->mnAddLeft == nAddSize) |
942 | 0 | return; |
943 | 0 | Cell aTempCell(*pTempCell); |
944 | 0 | aTempCell.mnAddLeft = nAddSize; |
945 | 0 | mxImpl->PutCell( aIt.Col(), aIt.Row(), aTempCell ); |
946 | 0 | } |
947 | 0 | } |
948 | | |
949 | | void Array::SetAddMergedRightSize( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nAddSize ) |
950 | 0 | { |
951 | 0 | DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetAddMergedRightSize" ); |
952 | 0 | DBG_FRAME_CHECK( mxImpl->GetMergedLastCol( nCol, nRow ) + 1 == mxImpl->mnWidth, "SetAddMergedRightSize", "additional border inside array" ); |
953 | 0 | for( MergedCellIterator aIt( *this, nCol, nRow ); aIt.Is(); ++aIt ) |
954 | 0 | { |
955 | 0 | const Cell* pTempCell(mxImpl->GetCell(aIt.Col(), aIt.Row())); |
956 | 0 | if (pTempCell->mnAddRight == nAddSize) |
957 | 0 | return; |
958 | 0 | Cell aTempCell(*pTempCell); |
959 | 0 | aTempCell.mnAddRight = nAddSize; |
960 | 0 | mxImpl->PutCell( aIt.Col(), aIt.Row(), aTempCell ); |
961 | 0 | } |
962 | 0 | } |
963 | | |
964 | | void Array::SetAddMergedTopSize( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nAddSize ) |
965 | 0 | { |
966 | 0 | DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetAddMergedTopSize" ); |
967 | 0 | DBG_FRAME_CHECK( mxImpl->GetMergedFirstRow( nCol, nRow ) == 0, "SetAddMergedTopSize", "additional border inside array" ); |
968 | 0 | for( MergedCellIterator aIt( *this, nCol, nRow ); aIt.Is(); ++aIt ) |
969 | 0 | { |
970 | 0 | const Cell* pTempCell(mxImpl->GetCell(aIt.Col(), aIt.Row())); |
971 | 0 | if (pTempCell->mnAddTop == nAddSize) |
972 | 0 | return; |
973 | 0 | Cell aTempCell(*pTempCell); |
974 | 0 | aTempCell.mnAddTop = nAddSize; |
975 | 0 | mxImpl->PutCell( aIt.Col(), aIt.Row(), aTempCell ); |
976 | 0 | } |
977 | 0 | } |
978 | | |
979 | | void Array::SetAddMergedBottomSize( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nAddSize ) |
980 | 0 | { |
981 | 0 | DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetAddMergedBottomSize" ); |
982 | 0 | DBG_FRAME_CHECK( mxImpl->GetMergedLastRow( nCol, nRow ) + 1 == mxImpl->mnHeight, "SetAddMergedBottomSize", "additional border inside array" ); |
983 | 0 | for( MergedCellIterator aIt( *this, nCol, nRow ); aIt.Is(); ++aIt ) |
984 | 0 | { |
985 | 0 | const Cell* pTempCell(mxImpl->GetCell(aIt.Col(), aIt.Row())); |
986 | 0 | if (pTempCell->mnAddBottom == nAddSize) |
987 | 0 | return; |
988 | 0 | Cell aTempCell(*pTempCell); |
989 | 0 | aTempCell.mnAddBottom = nAddSize; |
990 | 0 | mxImpl->PutCell( aIt.Col(), aIt.Row(), aTempCell ); |
991 | 0 | } |
992 | 0 | } |
993 | | |
994 | | bool Array::IsMerged( sal_Int32 nCol, sal_Int32 nRow ) const |
995 | 0 | { |
996 | 0 | DBG_FRAME_CHECK_COLROW( nCol, nRow, "IsMerged" ); |
997 | 0 | return mxImpl->GetCell( nCol, nRow )->IsMerged(); |
998 | 0 | } |
999 | | |
1000 | | void Array::GetMergedOrigin( sal_Int32& rnFirstCol, sal_Int32& rnFirstRow, sal_Int32 nCol, sal_Int32 nRow ) const |
1001 | 0 | { |
1002 | 0 | DBG_FRAME_CHECK_COLROW( nCol, nRow, "GetMergedOrigin" ); |
1003 | 0 | rnFirstCol = mxImpl->GetMergedFirstCol( nCol, nRow ); |
1004 | 0 | rnFirstRow = mxImpl->GetMergedFirstRow( nCol, nRow ); |
1005 | 0 | } |
1006 | | |
1007 | | void Array::GetMergedRange( sal_Int32& rnFirstCol, sal_Int32& rnFirstRow, |
1008 | | sal_Int32& rnLastCol, sal_Int32& rnLastRow, sal_Int32 nCol, sal_Int32 nRow ) const |
1009 | 0 | { |
1010 | 0 | GetMergedOrigin( rnFirstCol, rnFirstRow, nCol, nRow ); |
1011 | 0 | rnLastCol = mxImpl->GetMergedLastCol( nCol, nRow ); |
1012 | 0 | rnLastRow = mxImpl->GetMergedLastRow( nCol, nRow ); |
1013 | 0 | } |
1014 | | |
1015 | | // clipping |
1016 | | void Array::SetClipRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow ) |
1017 | 0 | { |
1018 | 0 | DBG_FRAME_CHECK_COLROW( nFirstCol, nFirstRow, "SetClipRange" ); |
1019 | 0 | DBG_FRAME_CHECK_COLROW( nLastCol, nLastRow, "SetClipRange" ); |
1020 | 0 | mxImpl->mnFirstClipCol = nFirstCol; |
1021 | 0 | mxImpl->mnFirstClipRow = nFirstRow; |
1022 | 0 | mxImpl->mnLastClipCol = nLastCol; |
1023 | 0 | mxImpl->mnLastClipRow = nLastRow; |
1024 | 0 | } |
1025 | | |
1026 | | // cell coordinates |
1027 | | void Array::SetXOffset( sal_Int32 nXOffset ) |
1028 | 0 | { |
1029 | 0 | mxImpl->maXCoords[ 0 ] = nXOffset; |
1030 | 0 | mxImpl->mbXCoordsDirty = true; |
1031 | 0 | } |
1032 | | |
1033 | | void Array::SetYOffset( sal_Int32 nYOffset ) |
1034 | 0 | { |
1035 | 0 | mxImpl->maYCoords[ 0 ] = nYOffset; |
1036 | 0 | mxImpl->mbYCoordsDirty = true; |
1037 | 0 | } |
1038 | | |
1039 | | void Array::SetColWidth( sal_Int32 nCol, sal_Int32 nWidth ) |
1040 | 0 | { |
1041 | 0 | DBG_FRAME_CHECK_COL( nCol, "SetColWidth" ); |
1042 | 0 | mxImpl->maWidths[ nCol ] = nWidth; |
1043 | 0 | mxImpl->mbXCoordsDirty = true; |
1044 | 0 | } |
1045 | | |
1046 | | void Array::SetRowHeight( sal_Int32 nRow, sal_Int32 nHeight ) |
1047 | 0 | { |
1048 | 0 | DBG_FRAME_CHECK_ROW( nRow, "SetRowHeight" ); |
1049 | 0 | mxImpl->maHeights[ nRow ] = nHeight; |
1050 | 0 | mxImpl->mbYCoordsDirty = true; |
1051 | 0 | } |
1052 | | |
1053 | | void Array::SetAllColWidths( sal_Int32 nWidth ) |
1054 | 0 | { |
1055 | 0 | std::fill( mxImpl->maWidths.begin(), mxImpl->maWidths.end(), nWidth ); |
1056 | 0 | mxImpl->mbXCoordsDirty = true; |
1057 | 0 | } |
1058 | | |
1059 | | void Array::SetAllRowHeights( sal_Int32 nHeight ) |
1060 | 0 | { |
1061 | 0 | std::fill( mxImpl->maHeights.begin(), mxImpl->maHeights.end(), nHeight ); |
1062 | 0 | mxImpl->mbYCoordsDirty = true; |
1063 | 0 | } |
1064 | | |
1065 | | sal_Int32 Array::GetColPosition( sal_Int32 nCol ) const |
1066 | 0 | { |
1067 | 0 | DBG_FRAME_CHECK_COL_1( nCol, "GetColPosition" ); |
1068 | 0 | return mxImpl->GetColPosition( nCol ); |
1069 | 0 | } |
1070 | | |
1071 | | sal_Int32 Array::GetRowPosition( sal_Int32 nRow ) const |
1072 | 0 | { |
1073 | 0 | DBG_FRAME_CHECK_ROW_1( nRow, "GetRowPosition" ); |
1074 | 0 | return mxImpl->GetRowPosition( nRow ); |
1075 | 0 | } |
1076 | | |
1077 | | sal_Int32 Array::GetColWidth( sal_Int32 nFirstCol, sal_Int32 nLastCol ) const |
1078 | 0 | { |
1079 | 0 | DBG_FRAME_CHECK_COL( nFirstCol, "GetColWidth" ); |
1080 | 0 | DBG_FRAME_CHECK_COL( nLastCol, "GetColWidth" ); |
1081 | 0 | return GetColPosition( nLastCol + 1 ) - GetColPosition( nFirstCol ); |
1082 | 0 | } |
1083 | | |
1084 | | sal_Int32 Array::GetRowHeight( sal_Int32 nFirstRow, sal_Int32 nLastRow ) const |
1085 | 0 | { |
1086 | 0 | DBG_FRAME_CHECK_ROW( nFirstRow, "GetRowHeight" ); |
1087 | 0 | DBG_FRAME_CHECK_ROW( nLastRow, "GetRowHeight" ); |
1088 | 0 | return GetRowPosition( nLastRow + 1 ) - GetRowPosition( nFirstRow ); |
1089 | 0 | } |
1090 | | |
1091 | | sal_Int32 Array::GetWidth() const |
1092 | 0 | { |
1093 | 0 | return GetColPosition( mxImpl->mnWidth ) - GetColPosition( 0 ); |
1094 | 0 | } |
1095 | | |
1096 | | sal_Int32 Array::GetHeight() const |
1097 | 0 | { |
1098 | 0 | return GetRowPosition( mxImpl->mnHeight ) - GetRowPosition( 0 ); |
1099 | 0 | } |
1100 | | |
1101 | | basegfx::B2DRange Array::GetCellRange( sal_Int32 nCol, sal_Int32 nRow ) const |
1102 | 0 | { |
1103 | | // get the Range of the fully expanded cell (if merged) |
1104 | 0 | const sal_Int32 nFirstCol(mxImpl->GetMergedFirstCol( nCol, nRow )); |
1105 | 0 | const sal_Int32 nFirstRow(mxImpl->GetMergedFirstRow( nCol, nRow )); |
1106 | 0 | const sal_Int32 nLastCol(mxImpl->GetMergedLastCol( nCol, nRow )); |
1107 | 0 | const sal_Int32 nLastRow(mxImpl->GetMergedLastRow( nCol, nRow )); |
1108 | 0 | const Point aPoint( GetColPosition( nFirstCol ), GetRowPosition( nFirstRow ) ); |
1109 | 0 | const Size aSize( GetColWidth( nFirstCol, nLastCol ) + 1, GetRowHeight( nFirstRow, nLastRow ) + 1 ); |
1110 | 0 | tools::Rectangle aRect(aPoint, aSize); |
1111 | | |
1112 | | // adjust rectangle for partly visible merged cells |
1113 | 0 | const Cell* pCell(mxImpl->GetCell( nCol, nRow )); |
1114 | |
|
1115 | 0 | if( pCell->IsMerged() ) |
1116 | 0 | { |
1117 | | // not *sure* what exactly this is good for, |
1118 | | // it is just a hard set extension at merged cells, |
1119 | | // probably *should* be included in the above extended |
1120 | | // GetColPosition/GetColWidth already. This might be |
1121 | | // added due to GetColPosition/GetColWidth not working |
1122 | | // correctly over PageChanges (if used), but not sure. |
1123 | 0 | aRect.AdjustLeft( -(pCell->mnAddLeft) ); |
1124 | 0 | aRect.AdjustRight(pCell->mnAddRight ); |
1125 | 0 | aRect.AdjustTop( -(pCell->mnAddTop) ); |
1126 | 0 | aRect.AdjustBottom(pCell->mnAddBottom ); |
1127 | 0 | } |
1128 | |
|
1129 | 0 | return vcl::unotools::b2DRectangleFromRectangle(aRect); |
1130 | 0 | } |
1131 | | |
1132 | | // return output range of given row/col range in logical coordinates |
1133 | | basegfx::B2DRange Array::GetB2DRange(sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow) const |
1134 | 0 | { |
1135 | 0 | const Point aPoint( GetColPosition( nFirstCol ), GetRowPosition( nFirstRow ) ); |
1136 | 0 | const Size aSize( GetColWidth( nFirstCol, nLastCol ) + 1, GetRowHeight( nFirstRow, nLastRow ) + 1 ); |
1137 | |
|
1138 | 0 | return vcl::unotools::b2DRectangleFromRectangle(tools::Rectangle(aPoint, aSize)); |
1139 | 0 | } |
1140 | | |
1141 | | // mirroring |
1142 | | void Array::MirrorSelfX() |
1143 | 0 | { |
1144 | 0 | CellVec aNewCells; |
1145 | 0 | aNewCells.reserve( GetCellCount() ); |
1146 | |
|
1147 | 0 | sal_Int32 nCol, nRow; |
1148 | 0 | for( nRow = 0; nRow < mxImpl->mnHeight; ++nRow ) |
1149 | 0 | { |
1150 | 0 | for( nCol = 0; nCol < mxImpl->mnWidth; ++nCol ) |
1151 | 0 | { |
1152 | 0 | Cell aTempCell(*mxImpl->GetCell(mxImpl->GetMirrorCol( nCol ), nRow)); |
1153 | 0 | aTempCell.MirrorSelfX(); |
1154 | 0 | aNewCells.push_back( mxImpl->createOrFind(aTempCell) ); |
1155 | 0 | } |
1156 | 0 | } |
1157 | 0 | for( nRow = 0; nRow < mxImpl->mnHeight; ++nRow ) |
1158 | 0 | { |
1159 | 0 | for( nCol = 0; nCol < mxImpl->mnWidth; ++nCol ) |
1160 | 0 | { |
1161 | 0 | if( mxImpl->GetCell( nCol, nRow )->mbMergeOrig ) |
1162 | 0 | { |
1163 | 0 | sal_Int32 nLastCol = mxImpl->GetMergedLastCol( nCol, nRow ); |
1164 | 0 | sal_Int32 nLastRow = mxImpl->GetMergedLastRow( nCol, nRow ); |
1165 | 0 | lclSetMergedRange( *mxImpl, aNewCells, mxImpl->mnWidth, |
1166 | 0 | mxImpl->GetMirrorCol( nLastCol ), nRow, |
1167 | 0 | mxImpl->GetMirrorCol( nCol ), nLastRow ); |
1168 | 0 | } |
1169 | 0 | } |
1170 | 0 | } |
1171 | 0 | mxImpl->maCells.swap( aNewCells ); |
1172 | |
|
1173 | 0 | std::reverse( mxImpl->maWidths.begin(), mxImpl->maWidths.end() ); |
1174 | 0 | mxImpl->mbXCoordsDirty = true; |
1175 | 0 | mxImpl->mbXMirrored = !mxImpl->mbXMirrored; |
1176 | 0 | } |
1177 | | |
1178 | | // drawing |
1179 | | static void HelperCreateHorizontalEntry( |
1180 | | const Array& rArray, |
1181 | | const Style& rStyle, |
1182 | | sal_Int32 col, |
1183 | | sal_Int32 row, |
1184 | | const basegfx::B2DPoint& rOrigin, |
1185 | | const basegfx::B2DVector& rX, |
1186 | | const basegfx::B2DVector& rY, |
1187 | | drawinglayer::primitive2d::SdrFrameBorderDataVector& rData, |
1188 | | bool bUpper, |
1189 | | const Color* pForceColor) |
1190 | 0 | { |
1191 | | // prepare SdrFrameBorderData |
1192 | 0 | rData.emplace_back( |
1193 | 0 | bUpper ? rOrigin : basegfx::B2DPoint(rOrigin + rY), |
1194 | 0 | rX, |
1195 | 0 | rStyle, |
1196 | 0 | pForceColor); |
1197 | 0 | drawinglayer::primitive2d::SdrFrameBorderData& rInstance(rData.back()); |
1198 | | |
1199 | | // get involved styles at start |
1200 | 0 | const Style& rStartFromTR(rArray.GetCellStyleBL( col, row - 1 )); |
1201 | 0 | const Style& rStartLFromT(rArray.GetCellStyleLeft( col, row - 1 )); |
1202 | 0 | const Style& rStartLFromL(rArray.GetCellStyleTop( col - 1, row )); |
1203 | 0 | const Style& rStartLFromB(rArray.GetCellStyleLeft( col, row )); |
1204 | 0 | const Style& rStartFromBR(rArray.GetCellStyleTL( col, row )); |
1205 | |
|
1206 | 0 | rInstance.addSdrConnectStyleData(true, rStartFromTR, rX - rY, false); |
1207 | 0 | rInstance.addSdrConnectStyleData(true, rStartLFromT, -rY, true); |
1208 | 0 | rInstance.addSdrConnectStyleData(true, rStartLFromL, -rX, true); |
1209 | 0 | rInstance.addSdrConnectStyleData(true, rStartLFromB, rY, false); |
1210 | 0 | rInstance.addSdrConnectStyleData(true, rStartFromBR, rX + rY, false); |
1211 | | |
1212 | | // get involved styles at end |
1213 | 0 | const Style& rEndFromTL(rArray.GetCellStyleBR( col, row - 1 )); |
1214 | 0 | const Style& rEndRFromT(rArray.GetCellStyleRight( col, row - 1 )); |
1215 | 0 | const Style& rEndRFromR(rArray.GetCellStyleTop( col + 1, row )); |
1216 | 0 | const Style& rEndRFromB(rArray.GetCellStyleRight( col, row )); |
1217 | 0 | const Style& rEndFromBL(rArray.GetCellStyleTR( col, row )); |
1218 | |
|
1219 | 0 | rInstance.addSdrConnectStyleData(false, rEndFromTL, -rX - rY, true); |
1220 | 0 | rInstance.addSdrConnectStyleData(false, rEndRFromT, -rY, true); |
1221 | 0 | rInstance.addSdrConnectStyleData(false, rEndRFromR, rX, false); |
1222 | 0 | rInstance.addSdrConnectStyleData(false, rEndRFromB, rY, false); |
1223 | 0 | rInstance.addSdrConnectStyleData(false, rEndFromBL, rY - rX, true); |
1224 | 0 | } |
1225 | | |
1226 | | static void HelperCreateVerticalEntry( |
1227 | | const Array& rArray, |
1228 | | const Style& rStyle, |
1229 | | sal_Int32 col, |
1230 | | sal_Int32 row, |
1231 | | const basegfx::B2DPoint& rOrigin, |
1232 | | const basegfx::B2DVector& rX, |
1233 | | const basegfx::B2DVector& rY, |
1234 | | drawinglayer::primitive2d::SdrFrameBorderDataVector& rData, |
1235 | | bool bLeft, |
1236 | | const Color* pForceColor) |
1237 | 0 | { |
1238 | | // prepare SdrFrameBorderData |
1239 | 0 | rData.emplace_back( |
1240 | 0 | bLeft ? rOrigin : basegfx::B2DPoint(rOrigin + rX), |
1241 | 0 | rY, |
1242 | 0 | rStyle, |
1243 | 0 | pForceColor); |
1244 | 0 | drawinglayer::primitive2d::SdrFrameBorderData& rInstance(rData.back()); |
1245 | | |
1246 | | // get involved styles at start |
1247 | 0 | const Style& rStartFromBL(rArray.GetCellStyleTR( col - 1, row )); |
1248 | 0 | const Style& rStartTFromL(rArray.GetCellStyleTop( col - 1, row )); |
1249 | 0 | const Style& rStartTFromT(rArray.GetCellStyleLeft( col, row - 1 )); |
1250 | 0 | const Style& rStartTFromR(rArray.GetCellStyleTop( col, row )); |
1251 | 0 | const Style& rStartFromBR(rArray.GetCellStyleTL( col, row )); |
1252 | |
|
1253 | 0 | rInstance.addSdrConnectStyleData(true, rStartFromBR, rX + rY, false); |
1254 | 0 | rInstance.addSdrConnectStyleData(true, rStartTFromR, rX, false); |
1255 | 0 | rInstance.addSdrConnectStyleData(true, rStartTFromT, -rY, true); |
1256 | 0 | rInstance.addSdrConnectStyleData(true, rStartTFromL, -rX, true); |
1257 | 0 | rInstance.addSdrConnectStyleData(true, rStartFromBL, rY - rX, true); |
1258 | | |
1259 | | // get involved styles at end |
1260 | 0 | const Style& rEndFromTL(rArray.GetCellStyleBR( col - 1, row )); |
1261 | 0 | const Style& rEndBFromL(rArray.GetCellStyleBottom( col - 1, row )); |
1262 | 0 | const Style& rEndBFromB(rArray.GetCellStyleLeft( col, row + 1 )); |
1263 | 0 | const Style& rEndBFromR(rArray.GetCellStyleBottom( col, row )); |
1264 | 0 | const Style& rEndFromTR(rArray.GetCellStyleBL( col, row )); |
1265 | |
|
1266 | 0 | rInstance.addSdrConnectStyleData(false, rEndFromTR, rX - rY, false); |
1267 | 0 | rInstance.addSdrConnectStyleData(false, rEndBFromR, rX, false); |
1268 | 0 | rInstance.addSdrConnectStyleData(false, rEndBFromB, rY, false); |
1269 | 0 | rInstance.addSdrConnectStyleData(false, rEndBFromL, -rX, true); |
1270 | 0 | rInstance.addSdrConnectStyleData(false, rEndFromTL, -rY - rX, true); |
1271 | 0 | } |
1272 | | |
1273 | | static void HelperClipLine( |
1274 | | basegfx::B2DPoint& rStart, |
1275 | | basegfx::B2DVector& rDirection, |
1276 | | const basegfx::B2DRange& rClipRange) |
1277 | 0 | { |
1278 | 0 | basegfx::B2DPolygon aLine({rStart, rStart + rDirection}); |
1279 | 0 | const basegfx::B2DPolyPolygon aResultPP( |
1280 | 0 | basegfx::utils::clipPolygonOnRange( |
1281 | 0 | aLine, |
1282 | 0 | rClipRange, |
1283 | 0 | true, // bInside |
1284 | 0 | true)); // bStroke |
1285 | |
|
1286 | 0 | if(aResultPP.count() > 0) |
1287 | 0 | { |
1288 | 0 | const basegfx::B2DPolygon& aResultP(aResultPP.getB2DPolygon(0)); |
1289 | |
|
1290 | 0 | if(aResultP.count() > 0) |
1291 | 0 | { |
1292 | 0 | const basegfx::B2DPoint aResultStart(aResultP.getB2DPoint(0)); |
1293 | 0 | const basegfx::B2DPoint aResultEnd(aResultP.getB2DPoint(aResultP.count() - 1)); |
1294 | |
|
1295 | 0 | if(aResultStart != aResultEnd) |
1296 | 0 | { |
1297 | 0 | rStart = aResultStart; |
1298 | 0 | rDirection = aResultEnd - aResultStart; |
1299 | 0 | } |
1300 | 0 | } |
1301 | 0 | } |
1302 | 0 | } |
1303 | | |
1304 | | static void HelperCreateTLBREntry( |
1305 | | const Array& rArray, |
1306 | | const Style& rStyle, |
1307 | | drawinglayer::primitive2d::SdrFrameBorderDataVector& rData, |
1308 | | const basegfx::B2DPoint& rOrigin, |
1309 | | const basegfx::B2DVector& rX, |
1310 | | const basegfx::B2DVector& rY, |
1311 | | sal_Int32 nColLeft, |
1312 | | sal_Int32 nColRight, |
1313 | | sal_Int32 nRowTop, |
1314 | | sal_Int32 nRowBottom, |
1315 | | const Color* pForceColor, |
1316 | | const basegfx::B2DRange* pClipRange) |
1317 | 0 | { |
1318 | 0 | if(rStyle.IsUsed()) |
1319 | 0 | { |
1320 | | /// prepare geometry line data |
1321 | 0 | basegfx::B2DPoint aStart(rOrigin); |
1322 | 0 | basegfx::B2DVector aDirection(rX + rY); |
1323 | | |
1324 | | /// check if we need to clip geometry line data and do it |
1325 | 0 | if(nullptr != pClipRange) |
1326 | 0 | { |
1327 | 0 | HelperClipLine(aStart, aDirection, *pClipRange); |
1328 | 0 | } |
1329 | | |
1330 | | /// top-left and bottom-right Style Tables |
1331 | 0 | rData.emplace_back( |
1332 | 0 | aStart, |
1333 | 0 | aDirection, |
1334 | 0 | rStyle, |
1335 | 0 | pForceColor); |
1336 | 0 | drawinglayer::primitive2d::SdrFrameBorderData& rInstance(rData.back()); |
1337 | | |
1338 | | /// Fill top-left Style Table |
1339 | 0 | const Style& rTLFromRight(rArray.GetCellStyleTop(nColLeft, nRowTop)); |
1340 | 0 | const Style& rTLFromBottom(rArray.GetCellStyleLeft(nColLeft, nRowTop)); |
1341 | |
|
1342 | 0 | rInstance.addSdrConnectStyleData(true, rTLFromRight, rX, false); |
1343 | 0 | rInstance.addSdrConnectStyleData(true, rTLFromBottom, rY, false); |
1344 | | |
1345 | | /// Fill bottom-right Style Table |
1346 | 0 | const Style& rBRFromBottom(rArray.GetCellStyleRight(nColRight, nRowBottom)); |
1347 | 0 | const Style& rBRFromLeft(rArray.GetCellStyleBottom(nColRight, nRowBottom)); |
1348 | |
|
1349 | 0 | rInstance.addSdrConnectStyleData(false, rBRFromBottom, -rY, true); |
1350 | 0 | rInstance.addSdrConnectStyleData(false, rBRFromLeft, -rX, true); |
1351 | 0 | } |
1352 | 0 | } |
1353 | | |
1354 | | static void HelperCreateBLTREntry( |
1355 | | const Array& rArray, |
1356 | | const Style& rStyle, |
1357 | | drawinglayer::primitive2d::SdrFrameBorderDataVector& rData, |
1358 | | const basegfx::B2DPoint& rOrigin, |
1359 | | const basegfx::B2DVector& rX, |
1360 | | const basegfx::B2DVector& rY, |
1361 | | sal_Int32 nColLeft, |
1362 | | sal_Int32 nColRight, |
1363 | | sal_Int32 nRowTop, |
1364 | | sal_Int32 nRowBottom, |
1365 | | const Color* pForceColor, |
1366 | | const basegfx::B2DRange* pClipRange) |
1367 | 0 | { |
1368 | 0 | if(rStyle.IsUsed()) |
1369 | 0 | { |
1370 | | /// prepare geometry line data |
1371 | 0 | basegfx::B2DPoint aStart(rOrigin + rY); |
1372 | 0 | basegfx::B2DVector aDirection(rX - rY); |
1373 | | |
1374 | | /// check if we need to clip geometry line data and do it |
1375 | 0 | if(nullptr != pClipRange) |
1376 | 0 | { |
1377 | 0 | HelperClipLine(aStart, aDirection, *pClipRange); |
1378 | 0 | } |
1379 | | |
1380 | | /// bottom-left and top-right Style Tables |
1381 | 0 | rData.emplace_back( |
1382 | 0 | aStart, |
1383 | 0 | aDirection, |
1384 | 0 | rStyle, |
1385 | 0 | pForceColor); |
1386 | 0 | drawinglayer::primitive2d::SdrFrameBorderData& rInstance(rData.back()); |
1387 | | |
1388 | | /// Fill bottom-left Style Table |
1389 | 0 | const Style& rBLFromTop(rArray.GetCellStyleLeft(nColLeft, nRowBottom)); |
1390 | 0 | const Style& rBLFromBottom(rArray.GetCellStyleBottom(nColLeft, nRowBottom)); |
1391 | |
|
1392 | 0 | rInstance.addSdrConnectStyleData(true, rBLFromTop, -rY, true); |
1393 | 0 | rInstance.addSdrConnectStyleData(true, rBLFromBottom, rX, false); |
1394 | | |
1395 | | /// Fill top-right Style Table |
1396 | 0 | const Style& rTRFromLeft(rArray.GetCellStyleTop(nColRight, nRowTop)); |
1397 | 0 | const Style& rTRFromBottom(rArray.GetCellStyleRight(nColRight, nRowTop)); |
1398 | |
|
1399 | 0 | rInstance.addSdrConnectStyleData(false, rTRFromLeft, -rX, true); |
1400 | 0 | rInstance.addSdrConnectStyleData(false, rTRFromBottom, rY, false); |
1401 | 0 | } |
1402 | 0 | } |
1403 | | |
1404 | | drawinglayer::primitive2d::Primitive2DContainer Array::CreateB2DPrimitiveRange( |
1405 | | sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow, |
1406 | | const Color* pForceColor ) const |
1407 | 0 | { |
1408 | 0 | DBG_FRAME_CHECK_COLROW( nFirstCol, nFirstRow, "CreateB2DPrimitiveRange" ); |
1409 | 0 | DBG_FRAME_CHECK_COLROW( nLastCol, nLastRow, "CreateB2DPrimitiveRange" ); |
1410 | | |
1411 | | // Bail out if indices are out of range |
1412 | 0 | if (nFirstCol < 0 || nFirstRow < 0 || nLastCol >= GetColCount() || nLastRow >= GetRowCount()) |
1413 | 0 | { |
1414 | 0 | SAL_WARN("svx.dialog", "CreateB2DPrimitiveRange indices out of range: " |
1415 | 0 | "nFirstCol=" << nFirstCol << " nFirstRow=" << nFirstRow |
1416 | 0 | << " nLastCol=" << nLastCol << " nLastRow=" << nLastRow |
1417 | 0 | << " ColCount=" << GetColCount() << " RowCount=" << GetRowCount()); |
1418 | 0 | return drawinglayer::primitive2d::Primitive2DContainer(); |
1419 | 0 | } |
1420 | | |
1421 | | #ifdef OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL |
1422 | | std::vector<basegfx::B2DRange> aClipRanges; |
1423 | | #endif |
1424 | | |
1425 | | // It may be necessary to extend the loop ranges by one cell to the outside, |
1426 | | // when possible. This is needed e.g. when there is in Calc a Cell with an |
1427 | | // upper CellBorder using DoubleLine and that is right/left connected upwards |
1428 | | // to also DoubleLine. These upper DoubleLines will be extended to meet the |
1429 | | // lower of the upper CellBorder and thus have graphical parts that are |
1430 | | // displayed one cell below and right/left of the target cell - analog to |
1431 | | // other examples in all other directions. |
1432 | | // It would be possible to explicitly test this (if possible by indices at all) |
1433 | | // looping and testing the styles in the outer cells to detect this, but since |
1434 | | // for other usages (e.g. UI) usually nFirstRow==0 and nLastRow==GetRowCount()-1 |
1435 | | // (and analog for Col) it is okay to just expand the range when available. |
1436 | | // Do *not* change nFirstRow/nLastRow due to these needed to the boolean tests |
1437 | | // below (!) |
1438 | | // Checked usages, this method is used in Calc EditView/Print/Export stuff and |
1439 | | // in UI (Dialog), not for Writer Tables and Draw/Impress tables. All usages |
1440 | | // seem okay with this change, so I will add it. |
1441 | 0 | const sal_Int32 nStartRow(nFirstRow > 0 ? nFirstRow - 1 : nFirstRow); |
1442 | 0 | const sal_Int32 nEndRow(nLastRow < GetRowCount() - 1 ? nLastRow + 1 : nLastRow); |
1443 | 0 | const sal_Int32 nStartCol(nFirstCol > 0 ? nFirstCol - 1 : nFirstCol); |
1444 | 0 | const sal_Int32 nEndCol(nLastCol < GetColCount() - 1 ? nLastCol + 1 : nLastCol); |
1445 | | |
1446 | | // prepare SdrFrameBorderDataVector |
1447 | 0 | drawinglayer::primitive2d::SdrFrameBorderDataVector aData; |
1448 | | |
1449 | | // remember for which merged cells crossed lines were already created. To |
1450 | | // do so, hold the sal_Int32 cell index in a set for fast check |
1451 | 0 | std::unordered_set< sal_Int32 > aMergedCells; |
1452 | |
|
1453 | 0 | for (sal_Int32 nRow(nStartRow); nRow <= nEndRow; ++nRow) |
1454 | 0 | { |
1455 | 0 | for (sal_Int32 nCol(nStartCol); nCol <= nEndCol; ++nCol) |
1456 | 0 | { |
1457 | | // get Cell and CoordinateSystem (*only* for this Cell, do *not* expand for |
1458 | | // merged cells (!)), check if used (non-empty vectors) |
1459 | 0 | const Cell* pCell(mxImpl->GetCell(nCol, nRow)); |
1460 | 0 | basegfx::B2DHomMatrix aCoordinateSystem(pCell->CreateCoordinateSystemSingleCell(*this, nCol, nRow)); |
1461 | 0 | basegfx::B2DVector aX(basegfx::utils::getColumn(aCoordinateSystem, 0)); |
1462 | 0 | basegfx::B2DVector aY(basegfx::utils::getColumn(aCoordinateSystem, 1)); |
1463 | | |
1464 | | // get needed local values |
1465 | 0 | basegfx::B2DPoint aOrigin(basegfx::utils::getColumn(aCoordinateSystem, 2)); |
1466 | 0 | const bool bOverlapX(pCell->mbOverlapX); |
1467 | 0 | const bool bFirstCol(nCol == nFirstCol); |
1468 | | |
1469 | | // handle rotation: If cell is rotated, handle lower/right edge inside |
1470 | | // this local geometry due to the created CoordinateSystem already representing |
1471 | | // the needed transformations. |
1472 | 0 | const bool bRotated(pCell->IsRotated()); |
1473 | | |
1474 | | // Additionally avoid double-handling by suppressing handling when self not rotated, |
1475 | | // but above/left is rotated and thus already handled. Two directly connected |
1476 | | // rotated will paint/create both edges, they might be rotated differently. |
1477 | 0 | const bool bSuppressLeft(!bRotated && nCol > nFirstCol && mxImpl->GetCell(nCol - 1, nRow)->IsRotated()); |
1478 | 0 | const bool bSuppressAbove(!bRotated && nRow > nFirstRow && mxImpl->GetCell(nCol, nRow - 1)->IsRotated()); |
1479 | |
|
1480 | 0 | if(!aX.equalZero() && !aY.equalZero()) |
1481 | 0 | { |
1482 | | // additionally needed local values |
1483 | 0 | const bool bOverlapY(pCell->mbOverlapY); |
1484 | 0 | const bool bLastCol(nCol == nLastCol); |
1485 | 0 | const bool bFirstRow(nRow == nFirstRow); |
1486 | 0 | const bool bLastRow(nRow == nLastRow); |
1487 | | |
1488 | | // create upper line for this Cell |
1489 | 0 | if ((!bOverlapY // true for first line in merged cells or cells |
1490 | 0 | || bFirstRow) // true for non_Calc usages of this tooling |
1491 | 0 | && !bSuppressAbove) // true when above is not rotated, so edge is already handled (see bRotated) |
1492 | 0 | { |
1493 | | // get CellStyle - method will take care to get the correct one, e.g. |
1494 | | // for merged cells (it uses mxImpl->GetMergedOriginCell that works with topLeft's of these) |
1495 | 0 | const Style& rTop(GetCellStyleTop(nCol, nRow)); |
1496 | |
|
1497 | 0 | if(rTop.IsUsed()) |
1498 | 0 | { |
1499 | 0 | HelperCreateHorizontalEntry(*this, rTop, nCol, nRow, aOrigin, aX, aY, aData, true, pForceColor); |
1500 | 0 | } |
1501 | 0 | } |
1502 | | |
1503 | | // create lower line for this Cell |
1504 | 0 | if (bLastRow // true for non_Calc usages of this tooling |
1505 | 0 | || bRotated) // true if cell is rotated, handle lower edge in local geometry |
1506 | 0 | { |
1507 | 0 | const Style& rBottom(GetCellStyleBottom(nCol, nRow)); |
1508 | |
|
1509 | 0 | if(rBottom.IsUsed()) |
1510 | 0 | { |
1511 | 0 | HelperCreateHorizontalEntry(*this, rBottom, nCol, nRow + 1, aOrigin, aX, aY, aData, false, pForceColor); |
1512 | 0 | } |
1513 | 0 | } |
1514 | | |
1515 | | // create left line for this Cell |
1516 | 0 | if ((!bOverlapX // true for first column in merged cells or cells |
1517 | 0 | || bFirstCol) // true for non_Calc usages of this tooling |
1518 | 0 | && !bSuppressLeft) // true when left is not rotated, so edge is already handled (see bRotated) |
1519 | 0 | { |
1520 | 0 | const Style& rLeft(GetCellStyleLeft(nCol, nRow)); |
1521 | |
|
1522 | 0 | if(rLeft.IsUsed()) |
1523 | 0 | { |
1524 | 0 | HelperCreateVerticalEntry(*this, rLeft, nCol, nRow, aOrigin, aX, aY, aData, true, pForceColor); |
1525 | 0 | } |
1526 | 0 | } |
1527 | | |
1528 | | // create right line for this Cell |
1529 | 0 | if (bLastCol // true for non_Calc usages of this tooling |
1530 | 0 | || bRotated) // true if cell is rotated, handle right edge in local geometry |
1531 | 0 | { |
1532 | 0 | const Style& rRight(GetCellStyleRight(nCol, nRow)); |
1533 | |
|
1534 | 0 | if(rRight.IsUsed()) |
1535 | 0 | { |
1536 | 0 | HelperCreateVerticalEntry(*this, rRight, nCol + 1, nRow, aOrigin, aX, aY, aData, false, pForceColor); |
1537 | 0 | } |
1538 | 0 | } |
1539 | | |
1540 | | // tdf#126269 check for crossed lines, these need special treatment, especially |
1541 | | // for merged cells (see comments in task). Separate treatment of merged and |
1542 | | // non-merged cells to allow better handling of both types |
1543 | 0 | if(pCell->IsMerged()) |
1544 | 0 | { |
1545 | | // first check if this merged cell was already handled. To do so, |
1546 | | // calculate and use the index of the TopLeft cell |
1547 | 0 | sal_Int32 nColLeft(nCol), nRowTop(nRow), nColRight(nCol), nRowBottom(nRow); |
1548 | 0 | GetMergedRange(nColLeft, nRowTop, nColRight, nRowBottom, nCol, nRow); |
1549 | 0 | const sal_Int32 nIndexOfMergedCell(mxImpl->GetIndex(nColLeft, nRowTop)); |
1550 | |
|
1551 | 0 | auto aItInsertedPair = aMergedCells.insert(nIndexOfMergedCell); |
1552 | 0 | if(aItInsertedPair.second) |
1553 | 0 | { |
1554 | | // not found, so not yet handled. |
1555 | | |
1556 | | // Get and check if diagonal styles are used |
1557 | | // Note: For GetCellStyleBLTR below I tried to use nRowBottom |
1558 | | // as Y-value what seemed more logical, but that |
1559 | | // is wrong. Despite defining a line starting at |
1560 | | // bottom-left, the Style is defined in the cell at top-left |
1561 | 0 | const Style& rTLBR(GetCellStyleTLBR(nColLeft, nRowTop)); |
1562 | 0 | const Style& rBLTR(GetCellStyleBLTR(nColLeft, nRowTop)); |
1563 | |
|
1564 | 0 | if(rTLBR.IsUsed() || rBLTR.IsUsed()) |
1565 | 0 | { |
1566 | | // test if merged cell overlaps ClipRange at all (needs visualization) |
1567 | 0 | if(mxImpl->OverlapsClipRange(nColLeft, nRowTop, nColRight, nRowBottom)) |
1568 | 0 | { |
1569 | | // when merged, get extended coordinate system and derived values |
1570 | | // for the full range of this merged cell. Only work with rMergedCell |
1571 | | // (which is the top-left single cell of the merged cell) from here on |
1572 | 0 | aCoordinateSystem = mxImpl->GetCell(nColLeft, nRowTop)->CreateCoordinateSystemMergedCell( |
1573 | 0 | *this, nColLeft, nRowTop, nColRight, nRowBottom); |
1574 | 0 | aX = basegfx::utils::getColumn(aCoordinateSystem, 0); |
1575 | 0 | aY = basegfx::utils::getColumn(aCoordinateSystem, 1); |
1576 | 0 | aOrigin = basegfx::utils::getColumn(aCoordinateSystem, 2); |
1577 | | |
1578 | | // check if clip is needed |
1579 | 0 | basegfx::B2DRange aClipRange; |
1580 | | |
1581 | | // first use row/col ClipTest for raw check |
1582 | 0 | bool bNeedToClip( |
1583 | 0 | !mxImpl->IsColInClipRange(nColLeft) || |
1584 | 0 | !mxImpl->IsRowInClipRange(nRowTop) || |
1585 | 0 | !mxImpl->IsColInClipRange(nColRight) || |
1586 | 0 | !mxImpl->IsRowInClipRange(nRowBottom)); |
1587 | |
|
1588 | 0 | if(bNeedToClip) |
1589 | 0 | { |
1590 | | // now get ClipRange and CellRange in logical coordinates |
1591 | 0 | aClipRange = GetB2DRange( |
1592 | 0 | mxImpl->mnFirstClipCol, mxImpl->mnFirstClipRow, |
1593 | 0 | mxImpl->mnLastClipCol, mxImpl->mnLastClipRow); |
1594 | |
|
1595 | 0 | basegfx::B2DRange aCellRange( |
1596 | 0 | GetB2DRange( |
1597 | 0 | nColLeft, nRowTop, |
1598 | 0 | nColRight, nRowBottom)); |
1599 | | |
1600 | | // intersect these to get the target ClipRange, ensure |
1601 | | // that clip is needed |
1602 | 0 | aClipRange.intersect(aCellRange); |
1603 | 0 | bNeedToClip = !aClipRange.isEmpty(); |
1604 | |
|
1605 | | #ifdef OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL |
1606 | | aClipRanges.push_back(aClipRange); |
1607 | | #endif |
1608 | 0 | } |
1609 | | |
1610 | | // create top-left to bottom-right geometry |
1611 | 0 | HelperCreateTLBREntry(*this, rTLBR, aData, aOrigin, aX, aY, |
1612 | 0 | nColLeft, nRowTop, nColRight, nRowBottom, pForceColor, |
1613 | 0 | bNeedToClip ? &aClipRange : nullptr); |
1614 | | |
1615 | | // create bottom-left to top-right geometry |
1616 | 0 | HelperCreateBLTREntry(*this, rBLTR, aData, aOrigin, aX, aY, |
1617 | 0 | nColLeft, nRowTop, nColRight, nRowBottom, pForceColor, |
1618 | 0 | bNeedToClip ? &aClipRange : nullptr); |
1619 | 0 | } |
1620 | 0 | } |
1621 | 0 | } |
1622 | 0 | } |
1623 | 0 | else |
1624 | 0 | { |
1625 | | // must be in clipping range: else not visible. This |
1626 | | // already clips completely for non-merged cells |
1627 | 0 | if( mxImpl->IsInClipRange( nCol, nRow ) ) |
1628 | 0 | { |
1629 | | // get and check if diagonal styles are used |
1630 | 0 | const Style& rTLBR(GetCellStyleTLBR(nCol, nRow)); |
1631 | 0 | const Style& rBLTR(GetCellStyleBLTR(nCol, nRow)); |
1632 | |
|
1633 | 0 | if(rTLBR.IsUsed() || rBLTR.IsUsed()) |
1634 | 0 | { |
1635 | 0 | HelperCreateTLBREntry(*this, rTLBR, aData, aOrigin, aX, aY, |
1636 | 0 | nCol, nRow, nCol, nRow, pForceColor, nullptr); |
1637 | |
|
1638 | 0 | HelperCreateBLTREntry(*this, rBLTR, aData, aOrigin, aX, aY, |
1639 | 0 | nCol, nRow, nCol, nRow, pForceColor, nullptr); |
1640 | 0 | } |
1641 | 0 | } |
1642 | 0 | } |
1643 | 0 | } |
1644 | 0 | else if(!aY.equalZero()) |
1645 | 0 | { |
1646 | | // cell has height, but no width. Create left vertical line for this Cell |
1647 | 0 | if ((!bOverlapX // true for first column in merged cells or cells |
1648 | 0 | || bFirstCol) // true for non_Calc usages of this tooling |
1649 | 0 | && !bSuppressLeft) // true when left is not rotated, so edge is already handled (see bRotated) |
1650 | 0 | { |
1651 | 0 | const Style& rLeft(GetCellStyleLeft(nCol, nRow)); |
1652 | |
|
1653 | 0 | if (rLeft.IsUsed()) |
1654 | 0 | { |
1655 | 0 | HelperCreateVerticalEntry(*this, rLeft, nCol, nRow, aOrigin, aX, aY, aData, true, pForceColor); |
1656 | 0 | } |
1657 | 0 | } |
1658 | 0 | } |
1659 | 0 | else |
1660 | 0 | { |
1661 | | // Cell has *no* size, thus no visualization |
1662 | 0 | } |
1663 | 0 | } |
1664 | 0 | } |
1665 | | |
1666 | | // create instance of SdrFrameBorderPrimitive2D if |
1667 | | // SdrFrameBorderDataVector is used |
1668 | 0 | drawinglayer::primitive2d::Primitive2DContainer aSequence; |
1669 | |
|
1670 | 0 | if(!aData.empty()) |
1671 | 0 | { |
1672 | 0 | aSequence.append( |
1673 | 0 | new drawinglayer::primitive2d::SdrFrameBorderPrimitive2D( |
1674 | 0 | std::move(aData), |
1675 | 0 | true)); // force visualization to minimal one discrete unit (pixel) |
1676 | 0 | } |
1677 | |
|
1678 | | #ifdef OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL |
1679 | | for(auto const& rClipRange : aClipRanges) |
1680 | | { |
1681 | | // draw ClipRange in yellow to allow simple interactive optical control in office |
1682 | | aSequence.append( |
1683 | | new drawinglayer::primitive2d::PolygonHairlinePrimitive2D( |
1684 | | basegfx::utils::createPolygonFromRect(rClipRange), |
1685 | | basegfx::BColor(1.0, 1.0, 0.0))); |
1686 | | } |
1687 | | #endif |
1688 | |
|
1689 | 0 | return aSequence; |
1690 | 0 | } |
1691 | | |
1692 | | drawinglayer::primitive2d::Primitive2DContainer Array::CreateB2DPrimitiveArray() const |
1693 | 0 | { |
1694 | 0 | drawinglayer::primitive2d::Primitive2DContainer aPrimitives; |
1695 | |
|
1696 | 0 | if (mxImpl->mnWidth && mxImpl->mnHeight) |
1697 | 0 | { |
1698 | 0 | aPrimitives = CreateB2DPrimitiveRange(0, 0, mxImpl->mnWidth - 1, mxImpl->mnHeight - 1, nullptr); |
1699 | 0 | } |
1700 | |
|
1701 | 0 | return aPrimitives; |
1702 | 0 | } |
1703 | | |
1704 | | #undef DBG_FRAME_CHECK_ROW_1 |
1705 | | #undef DBG_FRAME_CHECK_COL_1 |
1706 | | #undef DBG_FRAME_CHECK_COLROW |
1707 | | #undef DBG_FRAME_CHECK_ROW |
1708 | | #undef DBG_FRAME_CHECK_COL |
1709 | | #undef DBG_FRAME_CHECK |
1710 | | |
1711 | | } |
1712 | | |
1713 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |