1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_svx.hxx"
30 #include <svx/framelinkarray.hxx>
31 
32 #include <math.h>
33 #include <vector>
34 #include <algorithm>
35 #include <vcl/outdev.hxx>
36 
37 namespace svx {
38 namespace frame {
39 
40 // ============================================================================
41 
42 
43 Cell::Cell() :
44     mnAddLeft( 0 ),
45     mnAddRight( 0 ),
46     mnAddTop( 0 ),
47     mnAddBottom( 0 ),
48     mbMergeOrig( false ),
49     mbOverlapX( false ),
50     mbOverlapY( false )
51 {
52 }
53 
54 void Cell::MirrorSelfX( bool bMirrorStyles, bool bSwapDiag )
55 {
56     std::swap( maLeft, maRight );
57     std::swap( mnAddLeft, mnAddRight );
58     if( bMirrorStyles )
59     {
60         maLeft.MirrorSelf();
61         maRight.MirrorSelf();
62     }
63     if( bSwapDiag )
64     {
65         std::swap( maTLBR, maBLTR );
66         if( bMirrorStyles )
67         {
68             maTLBR.MirrorSelf();
69             maBLTR.MirrorSelf();
70         }
71     }
72 }
73 
74 void Cell::MirrorSelfY( bool bMirrorStyles, bool bSwapDiag )
75 {
76     std::swap( maTop, maBottom );
77     std::swap( mnAddTop, mnAddBottom );
78     if( bMirrorStyles )
79     {
80         maTop.MirrorSelf();
81         maBottom.MirrorSelf();
82     }
83     if( bSwapDiag )
84         std::swap( maTLBR, maBLTR );
85     /*  Do not mirror diagonal styles, because they are oriented vertical.
86         Therefore swapping the styles is sufficient for correct behaviour. */
87 }
88 
89 // ----------------------------------------------------------------------------
90 
91 
92 void lclRecalcCoordVec( LongVec& rCoords, const LongVec& rSizes )
93 {
94     DBG_ASSERT( rCoords.size() == rSizes.size() + 1, "lclRecalcCoordVec - inconsistent vectors" );
95     LongVec::iterator aCIt = rCoords.begin();
96     LongVec::const_iterator aSIt = rSizes.begin(), aSEnd = rSizes.end();
97     for( ; aSIt != aSEnd; ++aCIt, ++aSIt )
98         *(aCIt + 1) = *aCIt + *aSIt;
99 }
100 
101 void lclSetMergedRange( CellVec& rCells, size_t nWidth, size_t nFirstCol, size_t nFirstRow, size_t nLastCol, size_t nLastRow )
102 {
103     for( size_t nCol = nFirstCol; nCol <= nLastCol; ++nCol )
104     {
105         for( size_t nRow = nFirstRow; nRow <= nLastRow; ++nRow )
106         {
107             Cell& rCell = rCells[ nRow * nWidth + nCol ];
108             rCell.mbMergeOrig = false;
109             rCell.mbOverlapX = nCol > nFirstCol;
110             rCell.mbOverlapY = nRow > nFirstRow;
111         }
112     }
113     rCells[ nFirstRow * nWidth + nFirstCol ].mbMergeOrig = true;
114 }
115 
116 // ----------------------------------------------------------------------------
117 
118 static const Style OBJ_STYLE_NONE;
119 static const Cell OBJ_CELL_NONE;
120 
121 const bool DIAG_DBL_CLIP_DEFAULT = false;
122 
123 // ============================================================================
124 
125 ArrayImpl::ArrayImpl( size_t nWidth, size_t nHeight, bool bDiagDblClip ) :
126     mnWidth( nWidth ),
127     mnHeight( nHeight ),
128     mnFirstClipCol( 0 ),
129     mnFirstClipRow( 0 ),
130     mnLastClipCol( nWidth - 1 ),
131     mnLastClipRow( nHeight - 1 ),
132     mbXCoordsDirty( false ),
133     mbYCoordsDirty( false ),
134     mbDiagDblClip( bDiagDblClip )
135 {
136     // default-construct all vectors
137     maCells.resize( mnWidth * mnHeight );
138     maWidths.resize( mnWidth, 0L );
139     maHeights.resize( mnHeight, 0L );
140     maXCoords.resize( mnWidth + 1, 0L );
141     maYCoords.resize( mnHeight + 1, 0L );
142 }
143 
144 const Cell& ArrayImpl::GetCell( size_t nCol, size_t nRow ) const
145 {
146     return IsValidPos( nCol, nRow ) ? maCells[ GetIndex( nCol, nRow ) ] : OBJ_CELL_NONE;
147 }
148 
149 Cell& ArrayImpl::GetCellAcc( size_t nCol, size_t nRow )
150 {
151     static Cell aDummy;
152     return IsValidPos( nCol, nRow ) ? maCells[ GetIndex( nCol, nRow ) ] : aDummy;
153 }
154 
155 size_t ArrayImpl::GetMergedFirstCol( size_t nCol, size_t nRow ) const
156 {
157     size_t nFirstCol = nCol;
158     while( (nFirstCol > 0) && GetCell( nFirstCol, nRow ).mbOverlapX ) --nFirstCol;
159     return nFirstCol;
160 }
161 
162 size_t ArrayImpl::GetMergedFirstRow( size_t nCol, size_t nRow ) const
163 {
164     size_t nFirstRow = nRow;
165     while( (nFirstRow > 0) && GetCell( nCol, nFirstRow ).mbOverlapY ) --nFirstRow;
166     return nFirstRow;
167 }
168 
169 size_t ArrayImpl::GetMergedLastCol( size_t nCol, size_t nRow ) const
170 {
171     size_t nLastCol = nCol + 1;
172     while( (nLastCol < mnWidth) && GetCell( nLastCol, nRow ).mbOverlapX ) ++nLastCol;
173     return nLastCol - 1;
174 }
175 
176 size_t ArrayImpl::GetMergedLastRow( size_t nCol, size_t nRow ) const
177 {
178     size_t nLastRow = nRow + 1;
179     while( (nLastRow < mnHeight) && GetCell( nCol, nLastRow ).mbOverlapY ) ++nLastRow;
180     return nLastRow - 1;
181 }
182 
183 const Cell& ArrayImpl::GetMergedOriginCell( size_t nCol, size_t nRow ) const
184 {
185     return GetCell( GetMergedFirstCol( nCol, nRow ), GetMergedFirstRow( nCol, nRow ) );
186 }
187 
188 Cell& ArrayImpl::GetMergedOriginCellAcc( size_t nCol, size_t nRow )
189 {
190     return GetCellAcc( GetMergedFirstCol( nCol, nRow ), GetMergedFirstRow( nCol, nRow ) );
191 }
192 
193 bool ArrayImpl::IsMergedOverlappedLeft( size_t nCol, size_t nRow ) const
194 {
195     const Cell& rCell = GetCell( nCol, nRow );
196     return rCell.mbOverlapX || (rCell.mnAddLeft > 0);
197 }
198 
199 bool ArrayImpl::IsMergedOverlappedRight( size_t nCol, size_t nRow ) const
200 {
201     return GetCell( nCol + 1, nRow ).mbOverlapX || (GetCell( nCol, nRow ).mnAddRight > 0);
202 }
203 
204 bool ArrayImpl::IsMergedOverlappedTop( size_t nCol, size_t nRow ) const
205 {
206     const Cell& rCell = GetCell( nCol, nRow );
207     return rCell.mbOverlapY || (rCell.mnAddTop > 0);
208 }
209 
210 bool ArrayImpl::IsMergedOverlappedBottom( size_t nCol, size_t nRow ) const
211 {
212     return GetCell( nCol, nRow + 1 ).mbOverlapY || (GetCell( nCol, nRow ).mnAddBottom > 0);
213 }
214 
215 bool ArrayImpl::IsColInClipRange( size_t nCol ) const
216 {
217     return (mnFirstClipCol <= nCol) && (nCol <= mnLastClipCol);
218 }
219 
220 bool ArrayImpl::IsRowInClipRange( size_t nRow ) const
221 {
222     return (mnFirstClipRow <= nRow) && (nRow <= mnLastClipRow);
223 }
224 
225 bool ArrayImpl::IsInClipRange( size_t nCol, size_t nRow ) const
226 {
227     return IsColInClipRange( nCol ) && IsRowInClipRange( nRow );
228 }
229 
230 long ArrayImpl::GetColPosition( size_t nCol ) const
231 {
232     if( mbXCoordsDirty )
233     {
234         lclRecalcCoordVec( maXCoords, maWidths );
235         mbXCoordsDirty = false;
236     }
237     return maXCoords[ nCol ];
238 }
239 
240 long ArrayImpl::GetRowPosition( size_t nRow ) const
241 {
242     if( mbYCoordsDirty )
243     {
244         lclRecalcCoordVec( maYCoords, maHeights );
245         mbYCoordsDirty = false;
246     }
247     return maYCoords[ nRow ];
248 }
249 
250 long ArrayImpl::GetColWidth( size_t nFirstCol, size_t nLastCol ) const
251 {
252     return GetColPosition( nLastCol + 1 ) - GetColPosition( nFirstCol );
253 }
254 
255 long ArrayImpl::GetRowHeight( size_t nFirstRow, size_t nLastRow ) const
256 {
257     return GetRowPosition( nLastRow + 1 ) - GetRowPosition( nFirstRow );
258 }
259 
260 double ArrayImpl::GetHorDiagAngle( size_t nCol, size_t nRow, bool bSimple ) const
261 {
262     double fAngle = 0.0;
263     if( IsValidPos( nCol, nRow ) )
264     {
265         if( bSimple || !GetCell( nCol, nRow ).IsMerged() )
266         {
267             fAngle = frame::GetHorDiagAngle( maWidths[ nCol ] + 1, maHeights[ nRow ] + 1 );
268         }
269         else
270         {
271             // return correct angle for each cell in the merged range
272             size_t nFirstCol = GetMergedFirstCol( nCol, nRow );
273             size_t nFirstRow = GetMergedFirstRow( nCol, nRow );
274             const Cell& rCell = GetCell( nFirstCol, nFirstRow );
275             long nWidth = GetColWidth( nFirstCol, GetMergedLastCol( nCol, nRow ) ) + rCell.mnAddLeft + rCell.mnAddRight;
276             long nHeight = GetRowHeight( nFirstRow, GetMergedLastRow( nCol, nRow ) ) + rCell.mnAddTop + rCell.mnAddBottom;
277             fAngle = frame::GetHorDiagAngle( nWidth + 1, nHeight + 1 );
278         }
279     }
280     return fAngle;
281 }
282 
283 double ArrayImpl::GetVerDiagAngle( size_t nCol, size_t nRow, bool bSimple ) const
284 {
285     double fAngle = GetHorDiagAngle( nCol, nRow, bSimple );
286     return (fAngle > 0.0) ? (F_PI2 - fAngle) : 0.0;
287 }
288 
289 // ============================================================================
290 
291 class MergedCellIterator
292 {
293 public:
294     explicit            MergedCellIterator( const Array& rArray, size_t nCol, size_t nRow );
295 
296     inline bool         Is() const { return (mnCol <= mnLastCol) && (mnRow <= mnLastRow); }
297     inline size_t       Col() const { return mnCol; }
298     inline size_t       Row() const { return mnRow; }
299 
300     MergedCellIterator& operator++();
301 
302 private:
303     size_t              mnFirstCol;
304     size_t              mnFirstRow;
305     size_t              mnLastCol;
306     size_t              mnLastRow;
307     size_t              mnCol;
308     size_t              mnRow;
309 };
310 
311 // ----------------------------------------------------------------------------
312 
313 MergedCellIterator::MergedCellIterator( const Array& rArray, size_t nCol, size_t nRow )
314 {
315     DBG_ASSERT( rArray.IsMerged( nCol, nRow ), "svx::frame::MergedCellIterator::MergedCellIterator - not in merged range" );
316     rArray.GetMergedRange( mnFirstCol, mnFirstRow, mnLastCol, mnLastRow, nCol, nRow );
317     mnCol = mnFirstCol;
318     mnRow = mnFirstRow;
319 }
320 
321 MergedCellIterator& MergedCellIterator::operator++()
322 {
323     DBG_ASSERT( Is(), "svx::frame::MergedCellIterator::operator++() - already invalid" );
324     if( ++mnCol > mnLastCol )
325     {
326         mnCol = mnFirstCol;
327         ++mnRow;
328     }
329     return *this;
330 }
331 
332 // ============================================================================
333 
334 #define DBG_FRAME_ERROR( funcname, error )              DBG_ERRORFILE( "svx::frame::Array::" funcname " - " error )
335 #define DBG_FRAME_CHECK( cond, funcname, error )        DBG_ASSERT( cond, "svx::frame::Array::" funcname " - " error )
336 #define DBG_FRAME_CHECK_COL( col, funcname )            DBG_FRAME_CHECK( (col) < GetColCount(), funcname, "invalid column index" )
337 #define DBG_FRAME_CHECK_ROW( row, funcname )            DBG_FRAME_CHECK( (row) < GetRowCount(), funcname, "invalid row index" )
338 #define DBG_FRAME_CHECK_COLROW( col, row, funcname )    DBG_FRAME_CHECK( ((col) < GetColCount()) && ((row) < GetRowCount()), funcname, "invalid cell index" )
339 #define DBG_FRAME_CHECK_INDEX( index, funcname )        DBG_FRAME_CHECK( (index) < GetCellCount(), funcname, "invalid cell index" )
340 #define DBG_FRAME_CHECK_COL_1( col, funcname )          DBG_FRAME_CHECK( (col) <= GetColCount(), funcname, "invalid column index" )
341 #define DBG_FRAME_CHECK_ROW_1( row, funcname )          DBG_FRAME_CHECK( (row) <= GetRowCount(), funcname, "invalid row index" )
342 
343 // ----------------------------------------------------------------------------
344 
345 #define CELL( col, row )        mxImpl->GetCell( col, row )
346 #define CELLACC( col, row )     mxImpl->GetCellAcc( col, row )
347 #define ORIGCELL( col, row )    mxImpl->GetMergedOriginCell( col, row )
348 #define ORIGCELLACC( col, row ) mxImpl->GetMergedOriginCellAcc( col, row )
349 
350 // ----------------------------------------------------------------------------
351 
352 Array::Array()
353 {
354     Initialize( 0, 0 );
355 }
356 
357 Array::Array( size_t nWidth, size_t nHeight )
358 {
359     Initialize( nWidth, nHeight );
360 }
361 
362 Array::~Array()
363 {
364 }
365 
366 // array size and column/row indexes ------------------------------------------
367 
368 void Array::Initialize( size_t nWidth, size_t nHeight )
369 {
370     bool bDiagDblClip = mxImpl.get() ? mxImpl->mbDiagDblClip : DIAG_DBL_CLIP_DEFAULT;
371     mxImpl.reset( new ArrayImpl( nWidth, nHeight, bDiagDblClip ) );
372 }
373 
374 void Array::Clear()
375 {
376     Initialize( mxImpl->mnWidth, mxImpl->mnHeight );
377 }
378 
379 size_t Array::GetColCount() const
380 {
381     return mxImpl->mnWidth;
382 }
383 
384 size_t Array::GetRowCount() const
385 {
386     return mxImpl->mnHeight;
387 }
388 
389 size_t Array::GetCellCount() const
390 {
391     return mxImpl->maCells.size();
392 }
393 
394 size_t Array::GetColFromIndex( size_t nCellIndex ) const
395 {
396     DBG_FRAME_CHECK_INDEX( nCellIndex, "GetColFromIndex" );
397     return mxImpl->mnWidth ? (nCellIndex % mxImpl->mnWidth) : 0;
398 }
399 
400 size_t Array::GetRowFromIndex( size_t nCellIndex ) const
401 {
402     DBG_FRAME_CHECK_INDEX( nCellIndex, "GetRowFromIndex" );
403     return mxImpl->mnWidth ? (nCellIndex / mxImpl->mnWidth) : 0;
404 }
405 
406 size_t Array::GetCellIndex( size_t nCol, size_t nRow, bool bRTL ) const
407 {
408     DBG_FRAME_CHECK_COLROW( nCol, nRow, "GetCellIndex" );
409     if (bRTL)
410         nCol = mxImpl->GetMirrorCol(nCol);
411     return mxImpl->GetIndex( nCol, nRow );
412 }
413 
414 // cell border styles ---------------------------------------------------------
415 
416 void Array::SetCellStyleLeft( size_t nCol, size_t nRow, const Style& rStyle )
417 {
418     DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleLeft" );
419     CELLACC( nCol, nRow ).maLeft = rStyle;
420 }
421 
422 void Array::SetCellStyleRight( size_t nCol, size_t nRow, const Style& rStyle )
423 {
424     DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleRight" );
425     CELLACC( nCol, nRow ).maRight = rStyle;
426 }
427 
428 void Array::SetCellStyleTop( size_t nCol, size_t nRow, const Style& rStyle )
429 {
430     DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleTop" );
431     CELLACC( nCol, nRow ).maTop = rStyle;
432 }
433 
434 void Array::SetCellStyleBottom( size_t nCol, size_t nRow, const Style& rStyle )
435 {
436     DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleBottom" );
437     CELLACC( nCol, nRow ).maBottom = rStyle;
438 }
439 
440 void Array::SetCellStyleTLBR( size_t nCol, size_t nRow, const Style& rStyle )
441 {
442     DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleTLBR" );
443     CELLACC( nCol, nRow ).maTLBR = rStyle;
444 }
445 
446 void Array::SetCellStyleBLTR( size_t nCol, size_t nRow, const Style& rStyle )
447 {
448     DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleBLTR" );
449     CELLACC( nCol, nRow ).maBLTR = rStyle;
450 }
451 
452 void Array::SetCellStyleDiag( size_t nCol, size_t nRow, const Style& rTLBR, const Style& rBLTR )
453 {
454     DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleDiag" );
455     Cell& rCell = CELLACC( nCol, nRow );
456     rCell.maTLBR = rTLBR;
457     rCell.maBLTR = rBLTR;
458 }
459 
460 void Array::SetColumnStyleLeft( size_t nCol, const Style& rStyle )
461 {
462     DBG_FRAME_CHECK_COL( nCol, "SetColumnStyleLeft" );
463     for( size_t nRow = 0; nRow < mxImpl->mnHeight; ++nRow )
464         SetCellStyleLeft( nCol, nRow, rStyle );
465 }
466 
467 void Array::SetColumnStyleRight( size_t nCol, const Style& rStyle )
468 {
469     DBG_FRAME_CHECK_COL( nCol, "SetColumnStyleRight" );
470     for( size_t nRow = 0; nRow < mxImpl->mnHeight; ++nRow )
471         SetCellStyleRight( nCol, nRow, rStyle );
472 }
473 
474 void Array::SetRowStyleTop( size_t nRow, const Style& rStyle )
475 {
476     DBG_FRAME_CHECK_ROW( nRow, "SetRowStyleTop" );
477     for( size_t nCol = 0; nCol < mxImpl->mnWidth; ++nCol )
478         SetCellStyleTop( nCol, nRow, rStyle );
479 }
480 
481 void Array::SetRowStyleBottom( size_t nRow, const Style& rStyle )
482 {
483     DBG_FRAME_CHECK_ROW( nRow, "SetRowStyleBottom" );
484     for( size_t nCol = 0; nCol < mxImpl->mnWidth; ++nCol )
485         SetCellStyleBottom( nCol, nRow, rStyle );
486 }
487 
488 const Style& Array::GetCellStyleLeft( size_t nCol, size_t nRow, bool bSimple ) const
489 {
490     // simple: always return own left style
491     if( bSimple )
492         return CELL( nCol, nRow ).maLeft;
493     // outside clipping rows or overlapped in merged cells: invisible
494     if( !mxImpl->IsRowInClipRange( nRow ) || mxImpl->IsMergedOverlappedLeft( nCol, nRow ) )
495         return OBJ_STYLE_NONE;
496     // left clipping border: always own left style
497     if( nCol == mxImpl->mnFirstClipCol )
498         return ORIGCELL( nCol, nRow ).maLeft;
499     // right clipping border: always right style of left neighbor cell
500     if( nCol == mxImpl->mnLastClipCol + 1 )
501         return ORIGCELL( nCol - 1, nRow ).maRight;
502     // outside clipping columns: invisible
503     if( !mxImpl->IsColInClipRange( nCol ) )
504         return OBJ_STYLE_NONE;
505     // inside clipping range: maximum of own left style and right style of left neighbor cell
506     return std::max( ORIGCELL( nCol, nRow ).maLeft, ORIGCELL( nCol - 1, nRow ).maRight );
507 }
508 
509 const Style& Array::GetCellStyleRight( size_t nCol, size_t nRow, bool bSimple ) const
510 {
511     // simple: always return own right style
512     if( bSimple )
513         return CELL( nCol, nRow ).maRight;
514     // outside clipping rows or overlapped in merged cells: invisible
515     if( !mxImpl->IsRowInClipRange( nRow ) || mxImpl->IsMergedOverlappedRight( nCol, nRow ) )
516         return OBJ_STYLE_NONE;
517     // left clipping border: always left style of right neighbor cell
518     if( nCol + 1 == mxImpl->mnFirstClipCol )
519         return ORIGCELL( nCol + 1, nRow ).maLeft;
520     // right clipping border: always own right style
521     if( nCol == mxImpl->mnLastClipCol )
522         return ORIGCELL( nCol, nRow ).maRight;
523     // outside clipping columns: invisible
524     if( !mxImpl->IsColInClipRange( nCol ) )
525         return OBJ_STYLE_NONE;
526     // inside clipping range: maximum of own right style and left style of right neighbor cell
527     return std::max( ORIGCELL( nCol, nRow ).maRight, ORIGCELL( nCol + 1, nRow ).maLeft );
528 }
529 
530 const Style& Array::GetCellStyleTop( size_t nCol, size_t nRow, bool bSimple ) const
531 {
532     // simple: always return own top style
533     if( bSimple )
534         return CELL( nCol, nRow ).maTop;
535     // outside clipping columns or overlapped in merged cells: invisible
536     if( !mxImpl->IsColInClipRange( nCol ) || mxImpl->IsMergedOverlappedTop( nCol, nRow ) )
537         return OBJ_STYLE_NONE;
538     // top clipping border: always own top style
539     if( nRow == mxImpl->mnFirstClipRow )
540         return ORIGCELL( nCol, nRow ).maTop;
541     // bottom clipping border: always bottom style of top neighbor cell
542     if( nRow == mxImpl->mnLastClipRow + 1 )
543         return ORIGCELL( nCol, nRow - 1 ).maBottom;
544     // outside clipping rows: invisible
545     if( !mxImpl->IsRowInClipRange( nRow ) )
546         return OBJ_STYLE_NONE;
547     // inside clipping range: maximum of own top style and bottom style of top neighbor cell
548     return std::max( ORIGCELL( nCol, nRow ).maTop, ORIGCELL( nCol, nRow - 1 ).maBottom );
549 }
550 
551 const Style& Array::GetCellStyleBottom( size_t nCol, size_t nRow, bool bSimple ) const
552 {
553     // simple: always return own bottom style
554     if( bSimple )
555         return CELL( nCol, nRow ).maBottom;
556     // outside clipping columns or overlapped in merged cells: invisible
557     if( !mxImpl->IsColInClipRange( nCol ) || mxImpl->IsMergedOverlappedBottom( nCol, nRow ) )
558         return OBJ_STYLE_NONE;
559     // top clipping border: always top style of bottom neighbor cell
560     if( nRow + 1 == mxImpl->mnFirstClipRow )
561         return ORIGCELL( nCol, nRow + 1 ).maTop;
562     // bottom clipping border: always own bottom style
563     if( nRow == mxImpl->mnLastClipRow )
564         return ORIGCELL( nCol, nRow ).maBottom;
565     // outside clipping rows: invisible
566     if( !mxImpl->IsRowInClipRange( nRow ) )
567         return OBJ_STYLE_NONE;
568     // inside clipping range: maximum of own bottom style and top style of bottom neighbor cell
569     return std::max( ORIGCELL( nCol, nRow ).maBottom, ORIGCELL( nCol, nRow + 1 ).maTop );
570 }
571 
572 const Style& Array::GetCellStyleTLBR( size_t nCol, size_t nRow, bool bSimple ) const
573 {
574     return bSimple ? CELL( nCol, nRow ).maTLBR :
575         (mxImpl->IsInClipRange( nCol, nRow ) ? ORIGCELL( nCol, nRow ).maTLBR : OBJ_STYLE_NONE);
576 }
577 
578 const Style& Array::GetCellStyleBLTR( size_t nCol, size_t nRow, bool bSimple ) const
579 {
580     return bSimple ? CELL( nCol, nRow ).maBLTR :
581         (mxImpl->IsInClipRange( nCol, nRow ) ? ORIGCELL( nCol, nRow ).maBLTR : OBJ_STYLE_NONE);
582 }
583 
584 const Style& Array::GetCellStyleTL( size_t nCol, size_t nRow ) const
585 {
586     // not in clipping range: always invisible
587     if( !mxImpl->IsInClipRange( nCol, nRow ) )
588         return OBJ_STYLE_NONE;
589     // return style only for top-left cell
590     size_t nFirstCol = mxImpl->GetMergedFirstCol( nCol, nRow );
591     size_t nFirstRow = mxImpl->GetMergedFirstRow( nCol, nRow );
592     return ((nCol == nFirstCol) && (nRow == nFirstRow)) ?
593         CELL( nFirstCol, nFirstRow ).maTLBR : OBJ_STYLE_NONE;
594 }
595 
596 const Style& Array::GetCellStyleBR( size_t nCol, size_t nRow ) const
597 {
598     // not in clipping range: always invisible
599     if( !mxImpl->IsInClipRange( nCol, nRow ) )
600         return OBJ_STYLE_NONE;
601     // return style only for bottom-right cell
602     size_t nLastCol = mxImpl->GetMergedLastCol( nCol, nRow );
603     size_t nLastRow = mxImpl->GetMergedLastRow( nCol, nRow );
604     return ((nCol == nLastCol) && (nRow == nLastRow)) ?
605         CELL( mxImpl->GetMergedFirstCol( nCol, nRow ), mxImpl->GetMergedFirstRow( nCol, nRow ) ).maTLBR : OBJ_STYLE_NONE;
606 }
607 
608 const Style& Array::GetCellStyleBL( size_t nCol, size_t nRow ) const
609 {
610     // not in clipping range: always invisible
611     if( !mxImpl->IsInClipRange( nCol, nRow ) )
612         return OBJ_STYLE_NONE;
613     // return style only for bottom-left cell
614     size_t nFirstCol = mxImpl->GetMergedFirstCol( nCol, nRow );
615     size_t nLastRow = mxImpl->GetMergedLastRow( nCol, nRow );
616     return ((nCol == nFirstCol) && (nRow == nLastRow)) ?
617         CELL( nFirstCol, mxImpl->GetMergedFirstRow( nCol, nRow ) ).maBLTR : OBJ_STYLE_NONE;
618 }
619 
620 const Style& Array::GetCellStyleTR( size_t nCol, size_t nRow ) const
621 {
622     // not in clipping range: always invisible
623     if( !mxImpl->IsInClipRange( nCol, nRow ) )
624         return OBJ_STYLE_NONE;
625     // return style only for top-right cell
626     size_t nFirstRow = mxImpl->GetMergedFirstRow( nCol, nRow );
627     size_t nLastCol = mxImpl->GetMergedLastCol( nCol, nRow );
628     return ((nCol == nLastCol) && (nRow == nFirstRow)) ?
629         CELL( mxImpl->GetMergedFirstCol( nCol, nRow ), nFirstRow ).maBLTR : OBJ_STYLE_NONE;
630 }
631 
632 // cell merging ---------------------------------------------------------------
633 
634 void Array::SetMergedRange( size_t nFirstCol, size_t nFirstRow, size_t nLastCol, size_t nLastRow )
635 {
636     DBG_FRAME_CHECK_COLROW( nFirstCol, nFirstRow, "SetMergedRange" );
637     DBG_FRAME_CHECK_COLROW( nLastCol, nLastRow, "SetMergedRange" );
638 #if OSL_DEBUG_LEVEL >= 2
639     {
640         bool bFound = false;
641         for( size_t nCurrCol = nFirstCol; !bFound && (nCurrCol <= nLastCol); ++nCurrCol )
642             for( size_t nCurrRow = nFirstRow; !bFound && (nCurrRow <= nLastRow); ++nCurrRow )
643                 bFound = CELL( nCurrCol, nCurrRow ).IsMerged();
644         DBG_FRAME_CHECK( !bFound, "SetMergedRange", "overlapping merged ranges" );
645     }
646 #endif
647     if( mxImpl->IsValidPos( nFirstCol, nFirstRow ) && mxImpl->IsValidPos( nLastCol, nLastRow ) )
648         lclSetMergedRange( mxImpl->maCells, mxImpl->mnWidth, nFirstCol, nFirstRow, nLastCol, nLastRow );
649 }
650 
651 void Array::RemoveMergedRange( size_t nCol, size_t nRow )
652 {
653     DBG_FRAME_CHECK_COLROW( nCol, nRow, "RemoveMergedRange" );
654     for( MergedCellIterator aIt( *this, nCol, nRow ); aIt.Is(); ++aIt )
655     {
656         Cell& rCell = CELLACC( aIt.Col(), aIt.Row() );
657         rCell.mbMergeOrig = rCell.mbOverlapX = rCell.mbOverlapY = false;
658         rCell.mnAddLeft = rCell.mnAddRight = rCell.mnAddTop = rCell.mnAddBottom = 0;
659     }
660 }
661 
662 void Array::SetAddMergedLeftSize( size_t nCol, size_t nRow, long nAddSize )
663 {
664     DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetAddMergedLeftSize" );
665     DBG_FRAME_CHECK( mxImpl->GetMergedFirstCol( nCol, nRow ) == 0, "SetAddMergedLeftSize", "additional border inside array" );
666     for( MergedCellIterator aIt( *this, nCol, nRow ); aIt.Is(); ++aIt )
667         CELLACC( aIt.Col(), aIt.Row() ).mnAddLeft = nAddSize;
668 }
669 
670 void Array::SetAddMergedRightSize( size_t nCol, size_t nRow, long nAddSize )
671 {
672     DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetAddMergedRightSize" );
673     DBG_FRAME_CHECK( mxImpl->GetMergedLastCol( nCol, nRow ) + 1 == mxImpl->mnWidth, "SetAddMergedRightSize", "additional border inside array" );
674     for( MergedCellIterator aIt( *this, nCol, nRow ); aIt.Is(); ++aIt )
675         CELLACC( aIt.Col(), aIt.Row() ).mnAddRight = nAddSize;
676 }
677 
678 void Array::SetAddMergedTopSize( size_t nCol, size_t nRow, long nAddSize )
679 {
680     DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetAddMergedTopSize" );
681     DBG_FRAME_CHECK( mxImpl->GetMergedFirstRow( nCol, nRow ) == 0, "SetAddMergedTopSize", "additional border inside array" );
682     for( MergedCellIterator aIt( *this, nCol, nRow ); aIt.Is(); ++aIt )
683         CELLACC( aIt.Col(), aIt.Row() ).mnAddTop = nAddSize;
684 }
685 
686 void Array::SetAddMergedBottomSize( size_t nCol, size_t nRow, long nAddSize )
687 {
688     DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetAddMergedBottomSize" );
689     DBG_FRAME_CHECK( mxImpl->GetMergedLastRow( nCol, nRow ) + 1 == mxImpl->mnHeight, "SetAddMergedBottomSize", "additional border inside array" );
690     for( MergedCellIterator aIt( *this, nCol, nRow ); aIt.Is(); ++aIt )
691         CELLACC( aIt.Col(), aIt.Row() ).mnAddBottom = nAddSize;
692 }
693 
694 bool Array::IsMerged( size_t nCol, size_t nRow ) const
695 {
696     DBG_FRAME_CHECK_COLROW( nCol, nRow, "IsMerged" );
697     return CELL( nCol, nRow ).IsMerged();
698 }
699 
700 bool Array::IsMergedOrigin( size_t nCol, size_t nRow ) const
701 {
702     DBG_FRAME_CHECK_COLROW( nCol, nRow, "IsMergedOrigin" );
703     return CELL( nCol, nRow ).mbMergeOrig;
704 }
705 
706 bool Array::IsMergedOverlapped( size_t nCol, size_t nRow ) const
707 {
708     DBG_FRAME_CHECK_COLROW( nCol, nRow, "IsMergedOverlapped" );
709     return CELL( nCol, nRow ).IsOverlapped();
710 }
711 
712 bool Array::IsMergedOverlappedLeft( size_t nCol, size_t nRow ) const
713 {
714     DBG_FRAME_CHECK_COLROW( nCol, nRow, "IsMergedOverlappedLeft" );
715     return mxImpl->IsMergedOverlappedLeft( nCol, nRow );
716 }
717 
718 bool Array::IsMergedOverlappedRight( size_t nCol, size_t nRow ) const
719 {
720     DBG_FRAME_CHECK_COLROW( nCol, nRow, "IsMergedOverlappedRight" );
721     return mxImpl->IsMergedOverlappedRight( nCol, nRow );
722 }
723 
724 bool Array::IsMergedOverlappedTop( size_t nCol, size_t nRow ) const
725 {
726     DBG_FRAME_CHECK_COLROW( nCol, nRow, "IsMergedOverlappedTop" );
727     return mxImpl->IsMergedOverlappedTop( nCol, nRow );
728 }
729 
730 bool Array::IsMergedOverlappedBottom( size_t nCol, size_t nRow ) const
731 {
732     DBG_FRAME_CHECK_COLROW( nCol, nRow, "IsMergedOverlappedBottom" );
733     return mxImpl->IsMergedOverlappedBottom( nCol, nRow );
734 }
735 
736 void Array::GetMergedOrigin( size_t& rnFirstCol, size_t& rnFirstRow, size_t nCol, size_t nRow ) const
737 {
738     DBG_FRAME_CHECK_COLROW( nCol, nRow, "GetMergedOrigin" );
739     rnFirstCol = mxImpl->GetMergedFirstCol( nCol, nRow );
740     rnFirstRow = mxImpl->GetMergedFirstRow( nCol, nRow );
741 }
742 
743 void Array::GetMergedSize( size_t& rnWidth, size_t& rnHeight, size_t nCol, size_t nRow ) const
744 {
745     size_t nFirstCol, nFirstRow, nLastCol, nLastRow;
746     GetMergedRange( nFirstCol, nFirstRow, nLastCol, nLastRow, nCol, nRow );
747     rnWidth = nLastCol - nFirstCol + 1;
748     rnHeight = nLastRow - nFirstRow + 1;
749 }
750 
751 void Array::GetMergedRange( size_t& rnFirstCol, size_t& rnFirstRow,
752         size_t& rnLastCol, size_t& rnLastRow, size_t nCol, size_t nRow ) const
753 {
754     GetMergedOrigin( rnFirstCol, rnFirstRow, nCol, nRow );
755     rnLastCol = mxImpl->GetMergedLastCol( nCol, nRow );
756     rnLastRow = mxImpl->GetMergedLastRow( nCol, nRow );
757 }
758 
759 // clipping -------------------------------------------------------------------
760 
761 void Array::SetClipRange( size_t nFirstCol, size_t nFirstRow, size_t nLastCol, size_t nLastRow )
762 {
763     DBG_FRAME_CHECK_COLROW( nFirstCol, nFirstRow, "SetClipRange" );
764     DBG_FRAME_CHECK_COLROW( nLastCol, nLastRow, "SetClipRange" );
765     mxImpl->mnFirstClipCol = nFirstCol;
766     mxImpl->mnFirstClipRow = nFirstRow;
767     mxImpl->mnLastClipCol = nLastCol;
768     mxImpl->mnLastClipRow = nLastRow;
769 }
770 
771 void Array::RemoveClipRange()
772 {
773     if( !mxImpl->maCells.empty() )
774         SetClipRange( 0, 0, mxImpl->mnWidth - 1, mxImpl->mnHeight - 1 );
775 }
776 
777 bool Array::IsInClipRange( size_t nCol, size_t nRow ) const
778 {
779     DBG_FRAME_CHECK_COLROW( nCol, nRow, "IsInClipRange" );
780     return mxImpl->IsInClipRange( nCol, nRow );
781 }
782 
783 Rectangle Array::GetClipRangeRectangle() const
784 {
785     return Rectangle(
786         mxImpl->GetColPosition( mxImpl->mnFirstClipCol ),
787         mxImpl->GetRowPosition( mxImpl->mnFirstClipRow ),
788         mxImpl->GetColPosition( mxImpl->mnLastClipCol + 1 ),
789         mxImpl->GetRowPosition( mxImpl->mnLastClipRow + 1 ) );
790 }
791 
792 // cell coordinates -----------------------------------------------------------
793 
794 void Array::SetXOffset( long nXOffset )
795 {
796     mxImpl->maXCoords[ 0 ] = nXOffset;
797     mxImpl->mbXCoordsDirty = true;
798 }
799 
800 void Array::SetYOffset( long nYOffset )
801 {
802     mxImpl->maYCoords[ 0 ] = nYOffset;
803     mxImpl->mbYCoordsDirty = true;
804 }
805 
806 void Array::SetColWidth( size_t nCol, long nWidth )
807 {
808     DBG_FRAME_CHECK_COL( nCol, "SetColWidth" );
809     mxImpl->maWidths[ nCol ] = nWidth;
810     mxImpl->mbXCoordsDirty = true;
811 }
812 
813 void Array::SetRowHeight( size_t nRow, long nHeight )
814 {
815     DBG_FRAME_CHECK_ROW( nRow, "SetRowHeight" );
816     mxImpl->maHeights[ nRow ] = nHeight;
817     mxImpl->mbYCoordsDirty = true;
818 }
819 
820 void Array::SetAllColWidths( long nWidth )
821 {
822     std::fill( mxImpl->maWidths.begin(), mxImpl->maWidths.end(), nWidth );
823     mxImpl->mbXCoordsDirty = true;
824 }
825 
826 void Array::SetAllRowHeights( long nHeight )
827 {
828     std::fill( mxImpl->maHeights.begin(), mxImpl->maHeights.end(), nHeight );
829     mxImpl->mbYCoordsDirty = true;
830 }
831 
832 long Array::GetColPosition( size_t nCol ) const
833 {
834     DBG_FRAME_CHECK_COL_1( nCol, "GetColPosition" );
835     return mxImpl->GetColPosition( nCol );
836 }
837 
838 long Array::GetRowPosition( size_t nRow ) const
839 {
840     DBG_FRAME_CHECK_ROW_1( nRow, "GetRowPosition" );
841     return mxImpl->GetRowPosition( nRow );
842 }
843 
844 long Array::GetColWidth( size_t nCol ) const
845 {
846     DBG_FRAME_CHECK_COL( nCol, "GetColWidth" );
847     return mxImpl->maWidths[ nCol ];
848 }
849 
850 long Array::GetColWidth( size_t nFirstCol, size_t nLastCol ) const
851 {
852     DBG_FRAME_CHECK_COL( nFirstCol, "GetColWidth" );
853     DBG_FRAME_CHECK_COL( nLastCol, "GetColWidth" );
854     return GetColPosition( nLastCol + 1 ) - GetColPosition( nFirstCol );
855 }
856 
857 long Array::GetRowHeight( size_t nRow ) const
858 {
859     DBG_FRAME_CHECK_ROW( nRow, "GetRowHeight" );
860     return mxImpl->maHeights[ nRow ];
861 }
862 
863 long Array::GetRowHeight( size_t nFirstRow, size_t nLastRow ) const
864 {
865     DBG_FRAME_CHECK_ROW( nFirstRow, "GetRowHeight" );
866     DBG_FRAME_CHECK_ROW( nLastRow, "GetRowHeight" );
867     return GetRowPosition( nLastRow + 1 ) - GetRowPosition( nFirstRow );
868 }
869 
870 long Array::GetWidth() const
871 {
872     return GetColPosition( mxImpl->mnWidth ) - GetColPosition( 0 );
873 }
874 
875 long Array::GetHeight() const
876 {
877     return GetRowPosition( mxImpl->mnHeight ) - GetRowPosition( 0 );
878 }
879 
880 Point Array::GetCellPosition( size_t nCol, size_t nRow, bool bSimple ) const
881 {
882     size_t nFirstCol = bSimple ? nCol : mxImpl->GetMergedFirstCol( nCol, nRow );
883     size_t nFirstRow = bSimple ? nRow : mxImpl->GetMergedFirstRow( nCol, nRow );
884     return Point( GetColPosition( nFirstCol ), GetRowPosition( nFirstRow ) );
885 }
886 
887 Size Array::GetCellSize( size_t nCol, size_t nRow, bool bSimple ) const
888 {
889     size_t nFirstCol = bSimple ? nCol : mxImpl->GetMergedFirstCol( nCol, nRow );
890     size_t nFirstRow = bSimple ? nRow : mxImpl->GetMergedFirstRow( nCol, nRow );
891     size_t nLastCol = bSimple ? nCol : mxImpl->GetMergedLastCol( nCol, nRow );
892     size_t nLastRow = bSimple ? nRow : mxImpl->GetMergedLastRow( nCol, nRow );
893     return Size( GetColWidth( nFirstCol, nLastCol ) + 1, GetRowHeight( nFirstRow, nLastRow ) + 1 );
894 }
895 
896 Rectangle Array::GetCellRect( size_t nCol, size_t nRow, bool bSimple ) const
897 {
898     Rectangle aRect( GetCellPosition( nCol, nRow, bSimple ), GetCellSize( nCol, nRow, bSimple ) );
899 
900     // adjust rectangle for partly visible merged cells
901     const Cell& rCell = CELL( nCol, nRow );
902     if( !bSimple && rCell.IsMerged() )
903     {
904         aRect.Left() -= rCell.mnAddLeft;
905         aRect.Right() += rCell.mnAddRight;
906         aRect.Top() -= rCell.mnAddTop;
907         aRect.Bottom() += rCell.mnAddBottom;
908     }
909     return aRect;
910 }
911 
912 // diagonal frame borders -----------------------------------------------------
913 
914 double Array::GetHorDiagAngle( size_t nCol, size_t nRow, bool bSimple ) const
915 {
916     DBG_FRAME_CHECK_COLROW( nCol, nRow, "GetHorDiagAngle" );
917     return mxImpl->GetHorDiagAngle( nCol, nRow, bSimple );
918 }
919 
920 double Array::GetVerDiagAngle( size_t nCol, size_t nRow, bool bSimple ) const
921 {
922     DBG_FRAME_CHECK_COLROW( nCol, nRow, "GetVerDiagAngle" );
923     return mxImpl->GetVerDiagAngle( nCol, nRow, bSimple );
924 }
925 
926 void Array::SetUseDiagDoubleClipping( bool bSet )
927 {
928     mxImpl->mbDiagDblClip = bSet;
929 }
930 
931 bool Array::GetUseDiagDoubleClipping() const
932 {
933     return mxImpl->mbDiagDblClip;
934 }
935 
936 // mirroring ------------------------------------------------------------------
937 
938 void Array::MirrorSelfX( bool bMirrorStyles, bool bSwapDiag )
939 {
940     CellVec aNewCells;
941     aNewCells.reserve( GetCellCount() );
942 
943     size_t nCol, nRow;
944     for( nRow = 0; nRow < mxImpl->mnHeight; ++nRow )
945     {
946         for( nCol = 0; nCol < mxImpl->mnWidth; ++nCol )
947         {
948             aNewCells.push_back( CELL( mxImpl->GetMirrorCol( nCol ), nRow ) );
949             aNewCells.back().MirrorSelfX( bMirrorStyles, bSwapDiag );
950         }
951     }
952     for( nRow = 0; nRow < mxImpl->mnHeight; ++nRow )
953     {
954         for( nCol = 0; nCol < mxImpl->mnWidth; ++nCol )
955         {
956             if( CELL( nCol, nRow ).mbMergeOrig )
957             {
958                 size_t nLastCol = mxImpl->GetMergedLastCol( nCol, nRow );
959                 size_t nLastRow = mxImpl->GetMergedLastRow( nCol, nRow );
960                 lclSetMergedRange( aNewCells, mxImpl->mnWidth,
961                     mxImpl->GetMirrorCol( nLastCol ), nRow,
962                     mxImpl->GetMirrorCol( nCol ), nLastRow );
963             }
964         }
965     }
966     mxImpl->maCells.swap( aNewCells );
967 
968     std::reverse( mxImpl->maWidths.begin(), mxImpl->maWidths.end() );
969     mxImpl->mbXCoordsDirty = true;
970 }
971 
972 void Array::MirrorSelfY( bool bMirrorStyles, bool bSwapDiag )
973 {
974     CellVec aNewCells;
975     aNewCells.reserve( GetCellCount() );
976 
977     size_t nCol, nRow;
978     for( nRow = 0; nRow < mxImpl->mnHeight; ++nRow )
979     {
980         for( nCol = 0; nCol < mxImpl->mnWidth; ++nCol )
981         {
982             aNewCells.push_back( CELL( nCol, mxImpl->GetMirrorRow( nRow ) ) );
983             aNewCells.back().MirrorSelfY( bMirrorStyles, bSwapDiag );
984         }
985     }
986     for( nRow = 0; nRow < mxImpl->mnHeight; ++nRow )
987     {
988         for( nCol = 0; nCol < mxImpl->mnWidth; ++nCol )
989         {
990             if( CELL( nCol, nRow ).mbMergeOrig )
991             {
992                 size_t nLastCol = mxImpl->GetMergedLastCol( nCol, nRow );
993                 size_t nLastRow = mxImpl->GetMergedLastRow( nCol, nRow );
994                 lclSetMergedRange( aNewCells, mxImpl->mnWidth,
995                     nCol, mxImpl->GetMirrorRow( nLastRow ),
996                     nLastCol, mxImpl->GetMirrorRow( nRow ) );
997             }
998         }
999     }
1000     mxImpl->maCells.swap( aNewCells );
1001 
1002     std::reverse( mxImpl->maHeights.begin(), mxImpl->maHeights.end() );
1003     mxImpl->mbYCoordsDirty = true;
1004 }
1005 
1006 // drawing --------------------------------------------------------------------
1007 
1008 void Array::DrawCell( OutputDevice& rDev, size_t nCol, size_t nRow, const Color* pForceColor ) const
1009 {
1010     DrawRange( rDev, nCol, nRow, nCol, nRow, pForceColor );
1011 }
1012 
1013 void Array::DrawRange( OutputDevice& rDev,
1014         size_t nFirstCol, size_t nFirstRow, size_t nLastCol, size_t nLastRow,
1015         const Color* pForceColor ) const
1016 {
1017     DBG_FRAME_CHECK_COLROW( nFirstCol, nFirstRow, "DrawRange" );
1018     DBG_FRAME_CHECK_COLROW( nLastCol, nLastRow, "DrawRange" );
1019 
1020     size_t nCol, nRow;
1021 
1022     // *** diagonal frame borders ***
1023 
1024     // set clipping region to clip partly visible merged cells
1025     rDev.Push( PUSH_CLIPREGION );
1026     rDev.IntersectClipRegion( GetClipRangeRectangle() );
1027     for( nRow = nFirstRow; nRow <= nLastRow; ++nRow )
1028     {
1029         for( nCol = nFirstCol; nCol <= nLastCol; ++nCol )
1030         {
1031             const Cell& rCell = CELL( nCol, nRow );
1032             bool bOverlapX = rCell.mbOverlapX;
1033             bool bOverlapY = rCell.mbOverlapY;
1034             bool bFirstCol = nCol == nFirstCol;
1035             bool bFirstRow = nRow == nFirstRow;
1036             if( (!bOverlapX && !bOverlapY) || (bFirstCol && bFirstRow) ||
1037                 (!bOverlapY && bFirstCol) || (!bOverlapX && bFirstRow) )
1038             {
1039                 Rectangle aRect( GetCellRect( nCol, nRow ) );
1040                 if( (aRect.GetWidth() > 1) && (aRect.GetHeight() > 1) )
1041                 {
1042                     size_t _nFirstCol = mxImpl->GetMergedFirstCol( nCol, nRow );
1043                     size_t _nFirstRow = mxImpl->GetMergedFirstRow( nCol, nRow );
1044                     size_t _nLastCol = mxImpl->GetMergedLastCol( nCol, nRow );
1045                     size_t _nLastRow = mxImpl->GetMergedLastRow( nCol, nRow );
1046 
1047                     DrawDiagFrameBorders( rDev, aRect,
1048                         GetCellStyleTLBR( _nFirstCol, _nFirstRow, true ), GetCellStyleBLTR( _nFirstCol, _nFirstRow, true ),
1049                         GetCellStyleLeft( _nFirstCol, _nFirstRow ), GetCellStyleTop( _nFirstCol, _nFirstRow ),
1050                         GetCellStyleRight( _nLastCol, _nLastRow ), GetCellStyleBottom( _nLastCol, _nLastRow ),
1051                         GetCellStyleLeft( _nFirstCol, _nLastRow ), GetCellStyleBottom( _nFirstCol, _nLastRow ),
1052                         GetCellStyleRight( _nLastCol, _nFirstRow ), GetCellStyleTop( _nLastCol, _nFirstRow ),
1053                         pForceColor, mxImpl->mbDiagDblClip );
1054                 }
1055             }
1056         }
1057     }
1058     rDev.Pop(); // clip region
1059 
1060     // *** horizontal frame borders ***
1061 
1062     for( nRow = nFirstRow; nRow <= nLastRow + 1; ++nRow )
1063     {
1064         double fAngle = mxImpl->GetHorDiagAngle( nFirstCol, nRow );
1065         double fTAngle = mxImpl->GetHorDiagAngle( nFirstCol, nRow - 1 );
1066 
1067         // *Start*** variables store the data of the left end of the cached frame border
1068         Point aStartPos( mxImpl->GetColPosition( nFirstCol ), mxImpl->GetRowPosition( nRow ) );
1069         const Style* pStart = &GetCellStyleTop( nFirstCol, nRow );
1070         DiagStyle aStartLFromTR( GetCellStyleBL( nFirstCol, nRow - 1 ), fTAngle );
1071         const Style* pStartLFromT = &GetCellStyleLeft( nFirstCol, nRow - 1 );
1072         const Style* pStartLFromL = &GetCellStyleTop( nFirstCol - 1, nRow );
1073         const Style* pStartLFromB = &GetCellStyleLeft( nFirstCol, nRow );
1074         DiagStyle aStartLFromBR( GetCellStyleTL( nFirstCol, nRow ), fAngle );
1075 
1076         // *End*** variables store the data of the right end of the cached frame border
1077         DiagStyle aEndRFromTL( GetCellStyleBR( nFirstCol, nRow - 1 ), fTAngle );
1078         const Style* pEndRFromT = &GetCellStyleRight( nFirstCol, nRow - 1 );
1079         const Style* pEndRFromR = &GetCellStyleTop( nFirstCol + 1, nRow );
1080         const Style* pEndRFromB = &GetCellStyleRight( nFirstCol, nRow );
1081         DiagStyle aEndRFromBL( GetCellStyleTR( nFirstCol, nRow ), fAngle );
1082 
1083         for( nCol = nFirstCol + 1; nCol <= nLastCol; ++nCol )
1084         {
1085             fAngle = mxImpl->GetHorDiagAngle( nCol, nRow );
1086             fTAngle = mxImpl->GetHorDiagAngle( nCol, nRow - 1 );
1087 
1088             const Style& rCurr = *pEndRFromR;
1089 
1090             DiagStyle aLFromTR( GetCellStyleBL( nCol, nRow - 1 ), fTAngle );
1091             const Style& rLFromT = *pEndRFromT;
1092             const Style& rLFromL = *pStart;
1093             const Style& rLFromB = *pEndRFromB;
1094             DiagStyle aLFromBR( GetCellStyleTL( nCol, nRow ), fAngle );
1095 
1096             DiagStyle aRFromTL( GetCellStyleBR( nCol, nRow - 1 ), fTAngle );
1097             const Style& rRFromT = GetCellStyleRight( nCol, nRow - 1 );
1098             const Style& rRFromR = GetCellStyleTop( nCol + 1, nRow );
1099             const Style& rRFromB = GetCellStyleRight( nCol, nRow );
1100             DiagStyle aRFromBL( GetCellStyleTR( nCol, nRow ), fAngle );
1101 
1102             // check if current frame border can be connected to cached frame border
1103             if( !CheckFrameBorderConnectable( *pStart, rCurr,
1104                     aEndRFromTL, rLFromT, aLFromTR, aEndRFromBL, rLFromB, aLFromBR ) )
1105             {
1106                 // draw previous frame border
1107                 Point aEndPos( mxImpl->GetColPosition( nCol ), aStartPos.Y() );
1108                 if( pStart->Prim() && (aStartPos.X() <= aEndPos.X()) )
1109                     DrawHorFrameBorder( rDev, aStartPos, aEndPos, *pStart,
1110                         aStartLFromTR, *pStartLFromT, *pStartLFromL, *pStartLFromB, aStartLFromBR,
1111                         aEndRFromTL, *pEndRFromT, *pEndRFromR, *pEndRFromB, aEndRFromBL, pForceColor );
1112 
1113                 // re-init "*Start***" variables
1114                 aStartPos = aEndPos;
1115                 pStart = &rCurr;
1116                 aStartLFromTR = aLFromTR;
1117                 pStartLFromT = &rLFromT;
1118                 pStartLFromL = &rLFromL;
1119                 pStartLFromB = &rLFromB;
1120                 aStartLFromBR = aLFromBR;
1121             }
1122 
1123             // store current styles in "*End***" variables
1124             aEndRFromTL = aRFromTL;
1125             pEndRFromT = &rRFromT;
1126             pEndRFromR = &rRFromR;
1127             pEndRFromB = &rRFromB;
1128             aEndRFromBL = aRFromBL;
1129         }
1130 
1131         // draw last frame border
1132         Point aEndPos( mxImpl->GetColPosition( nCol ), aStartPos.Y() );
1133         if( pStart->Prim() && (aStartPos.X() <= aEndPos.X()) )
1134             DrawHorFrameBorder( rDev, aStartPos, aEndPos, *pStart,
1135                 aStartLFromTR, *pStartLFromT, *pStartLFromL, *pStartLFromB, aStartLFromBR,
1136                 aEndRFromTL, *pEndRFromT, *pEndRFromR, *pEndRFromB, aEndRFromBL, pForceColor );
1137     }
1138 
1139     // *** vertical frame borders ***
1140 
1141     for( nCol = nFirstCol; nCol <= nLastCol + 1; ++nCol )
1142     {
1143         double fAngle = mxImpl->GetVerDiagAngle( nCol, nFirstRow );
1144         double fLAngle = mxImpl->GetVerDiagAngle( nCol - 1, nFirstRow );
1145 
1146         // *Start*** variables store the data of the top end of the cached frame border
1147         Point aStartPos( mxImpl->GetColPosition( nCol ), mxImpl->GetRowPosition( nFirstRow ) );
1148         const Style* pStart = &GetCellStyleLeft( nCol, nFirstRow );
1149         DiagStyle aStartTFromBL( GetCellStyleTR( nCol - 1, nFirstRow ), fLAngle );
1150         const Style* pStartTFromL = &GetCellStyleTop( nCol - 1, nFirstRow );
1151         const Style* pStartTFromT = &GetCellStyleLeft( nCol, nFirstRow - 1 );
1152         const Style* pStartTFromR = &GetCellStyleTop( nCol, nFirstRow );
1153         DiagStyle aStartTFromBR( GetCellStyleTL( nCol, nFirstRow ), fAngle );
1154 
1155         // *End*** variables store the data of the bottom end of the cached frame border
1156         DiagStyle aEndBFromTL( GetCellStyleBR( nCol - 1, nFirstRow ), fLAngle );
1157         const Style* pEndBFromL = &GetCellStyleBottom( nCol - 1, nFirstRow );
1158         const Style* pEndBFromB = &GetCellStyleLeft( nCol, nFirstRow + 1 );
1159         const Style* pEndBFromR = &GetCellStyleBottom( nCol, nFirstRow );
1160         DiagStyle aEndBFromTR( GetCellStyleBL( nCol, nFirstRow ), fAngle );
1161 
1162         for( nRow = nFirstRow + 1; nRow <= nLastRow; ++nRow )
1163         {
1164             fAngle = mxImpl->GetVerDiagAngle( nCol, nRow );
1165             fLAngle = mxImpl->GetVerDiagAngle( nCol - 1, nRow );
1166 
1167             const Style& rCurr = *pEndBFromB;
1168 
1169             DiagStyle aTFromBL( GetCellStyleTR( nCol - 1, nRow ), fLAngle );
1170             const Style& rTFromL = *pEndBFromL;
1171             const Style& rTFromT = *pStart;
1172             const Style& rTFromR = *pEndBFromR;
1173             DiagStyle aTFromBR( GetCellStyleTL( nCol, nRow ), fAngle );
1174 
1175             DiagStyle aBFromTL( GetCellStyleBR( nCol - 1, nRow ), fLAngle );
1176             const Style& rBFromL = GetCellStyleBottom( nCol - 1, nRow );
1177             const Style& rBFromB = GetCellStyleLeft( nCol, nRow + 1 );
1178             const Style& rBFromR = GetCellStyleBottom( nCol, nRow );
1179             DiagStyle aBFromTR( GetCellStyleBL( nCol, nRow ), fAngle );
1180 
1181             // check if current frame border can be connected to cached frame border
1182             if( !CheckFrameBorderConnectable( *pStart, rCurr,
1183                     aEndBFromTL, rTFromL, aTFromBL, aEndBFromTR, rTFromR, aTFromBR ) )
1184             {
1185                 // draw previous frame border
1186                 Point aEndPos( aStartPos.X(), mxImpl->GetRowPosition( nRow ) );
1187                 if( pStart->Prim() && (aStartPos.Y() <= aEndPos.Y()) )
1188                     DrawVerFrameBorder( rDev, aStartPos, aEndPos, *pStart,
1189                         aStartTFromBL, *pStartTFromL, *pStartTFromT, *pStartTFromR, aStartTFromBR,
1190                         aEndBFromTL, *pEndBFromL, *pEndBFromB, *pEndBFromR, aEndBFromTR, pForceColor );
1191 
1192                 // re-init "*Start***" variables
1193                 aStartPos = aEndPos;
1194                 pStart = &rCurr;
1195                 aStartTFromBL = aTFromBL;
1196                 pStartTFromL = &rTFromL;
1197                 pStartTFromT = &rTFromT;
1198                 pStartTFromR = &rTFromR;
1199                 aStartTFromBR = aTFromBR;
1200             }
1201 
1202             // store current styles in "*End***" variables
1203             aEndBFromTL = aBFromTL;
1204             pEndBFromL = &rBFromL;
1205             pEndBFromB = &rBFromB;
1206             pEndBFromR = &rBFromR;
1207             aEndBFromTR = aBFromTR;
1208         }
1209 
1210         // draw last frame border
1211         Point aEndPos( aStartPos.X(), mxImpl->GetRowPosition( nRow ) );
1212         if( pStart->Prim() && (aStartPos.Y() <= aEndPos.Y()) )
1213             DrawVerFrameBorder( rDev, aStartPos, aEndPos, *pStart,
1214                 aStartTFromBL, *pStartTFromL, *pStartTFromT, *pStartTFromR, aStartTFromBR,
1215                 aEndBFromTL, *pEndBFromL, *pEndBFromB, *pEndBFromR, aEndBFromTR, pForceColor );
1216     }
1217 }
1218 
1219 void Array::DrawArray( OutputDevice& rDev, const Color* pForceColor ) const
1220 {
1221     if( mxImpl->mnWidth && mxImpl->mnHeight )
1222         DrawRange( rDev, 0, 0, mxImpl->mnWidth - 1, mxImpl->mnHeight - 1, pForceColor );
1223 }
1224 
1225 // ----------------------------------------------------------------------------
1226 
1227 #undef ORIGCELLACC
1228 #undef ORIGCELL
1229 #undef CELLACC
1230 #undef CELL
1231 
1232 // ----------------------------------------------------------------------------
1233 
1234 #undef DBG_FRAME_CHECK_ROW_1
1235 #undef DBG_FRAME_CHECK_COL_1
1236 #undef DBG_FRAME_CHECK_INDEX
1237 #undef DBG_FRAME_CHECK_COLROW
1238 #undef DBG_FRAME_CHECK_ROW
1239 #undef DBG_FRAME_CHECK_COL
1240 #undef DBG_FRAME_CHECK
1241 #undef DBG_FRAME_ERROR
1242 
1243 // ============================================================================
1244 
1245 } // namespace frame
1246 } // namespace svx
1247 
1248