1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <hintids.hxx>
21 #include <tblrwcl.hxx>
23 #include <editeng/borderline.hxx>
24 #include <editeng/boxitem.hxx>
25 #include <osl/diagnose.h>
27 using namespace ::editeng
;
29 static const SvxBorderLine
* GetLineTB( const SvxBoxItem
* pBox
, bool bTop
)
31 return bTop
? pBox
->GetTop() : pBox
->GetBottom();
34 bool SwGCBorder_BoxBrd::CheckLeftBorderOfFormat( const SwFrameFormat
& rFormat
)
36 if( const SvxBoxItem
* pItem
= rFormat
.GetItemIfSet( RES_BOX
) )
38 const SvxBorderLine
* pBrd
= pItem
->GetLeft();
41 if( *m_pBorderLine
== *pBrd
)
42 m_bAnyBorderFind
= true;
49 static bool lcl_GCBorder_ChkBoxBrd_B( const SwTableBox
* pBox
, SwGCBorder_BoxBrd
* pPara
);
51 static bool lcl_GCBorder_ChkBoxBrd_L( const SwTableLine
* pLine
, SwGCBorder_BoxBrd
* pPara
)
53 const SwTableBox
* pBox
= pLine
->GetTabBoxes().front();
54 return lcl_GCBorder_ChkBoxBrd_B( pBox
, pPara
);
57 static bool lcl_GCBorder_ChkBoxBrd_B( const SwTableBox
* pBox
, SwGCBorder_BoxBrd
* pPara
)
59 if( !pBox
->GetTabLines().empty() )
61 for( auto pLine
: pBox
->GetTabLines() )
63 if (!lcl_GCBorder_ChkBoxBrd_L( pLine
, pPara
))
71 return pPara
->CheckLeftBorderOfFormat( *pBox
->GetFrameFormat() );
74 static void lcl_GCBorder_GetLastBox_B( const SwTableBox
* pBox
, SwTableBoxes
* pPara
);
76 static void lcl_GCBorder_GetLastBox_L( const SwTableLine
* pLine
, SwTableBoxes
* pPara
)
78 const SwTableBoxes
& rBoxes
= pLine
->GetTabBoxes();
79 SwTableBox
* pBox
= rBoxes
.back();
80 lcl_GCBorder_GetLastBox_B( pBox
, pPara
);
83 static void lcl_GCBorder_GetLastBox_B( const SwTableBox
* pBox
, SwTableBoxes
* pPara
)
85 const SwTableLines
& rLines
= pBox
->GetTabLines();
88 for( const SwTableLine
* pLine
: rLines
)
89 lcl_GCBorder_GetLastBox_L( pLine
, pPara
);
92 pPara
->push_back( const_cast<SwTableBox
*>(pBox
) );
95 // Find the "end" of the passed BorderLine. Returns the "Layout"Pos!
96 static sal_uInt16
lcl_FindEndPosOfBorder( const SwCollectTableLineBoxes
& rCollTLB
,
97 const SvxBorderLine
& rBrdLn
, size_t& rStt
, bool bTop
)
99 sal_uInt16 nPos
, nLastPos
= 0;
100 for( size_t nEnd
= rCollTLB
.Count(); rStt
< nEnd
; ++rStt
)
102 const SvxBorderLine
* pBrd
;
103 const SwTableBox
& rBox
= rCollTLB
.GetBox( rStt
, &nPos
);
104 const SvxBoxItem
* pItem
= rBox
.GetFrameFormat()->GetItemIfSet(RES_BOX
);
108 pBrd
= GetLineTB( pItem
, bTop
);
109 if( !pBrd
|| *pBrd
!= rBrdLn
)
116 static const SvxBorderLine
* lcl_GCBorder_GetBorder( const SwTableBox
& rBox
,
118 const SvxBoxItem
** ppItem
)
120 *ppItem
= rBox
.GetFrameFormat()->GetItemIfSet( RES_BOX
);
122 return GetLineTB( *ppItem
, bTop
);
126 static void lcl_GCBorder_DelBorder( const SwCollectTableLineBoxes
& rCollTLB
,
127 size_t& rStt
, bool bTop
,
128 const SvxBorderLine
& rLine
,
129 const SvxBoxItem
* pItem
,
131 SwShareBoxFormats
* pShareFormats
)
133 SwTableBox
* pBox
= const_cast<SwTableBox
*>(&rCollTLB
.GetBox( rStt
));
135 const SvxBorderLine
* pLn
= &rLine
;
138 if( pLn
&& *pLn
== rLine
)
140 SvxBoxItem
aBox( *pItem
);
142 aBox
.SetLine( nullptr, SvxBoxItemLine::TOP
);
144 aBox
.SetLine( nullptr, SvxBoxItemLine::BOTTOM
);
147 pShareFormats
->SetAttr( *pBox
, aBox
);
149 pBox
->ClaimFrameFormat()->SetFormatAttr( aBox
);
152 if( ++rStt
>= rCollTLB
.Count() )
155 pBox
= const_cast<SwTableBox
*>(&rCollTLB
.GetBox( rStt
, &nNextPos
));
156 if( nNextPos
> nEndPos
)
159 pLn
= lcl_GCBorder_GetBorder( *pBox
, bTop
, &pItem
);
164 static void lcl_GC_Box_Border( const SwTableBox
* pBox
, SwGCLineBorder
* pPara
);
166 void sw_GC_Line_Border( const SwTableLine
* pLine
, SwGCLineBorder
* pGCPara
)
168 // First the right edge with the left edge of the succeeding Box within this Line
170 SwGCBorder_BoxBrd aBPara
;
171 const SvxBorderLine
* pBrd
;
172 const SwTableBoxes
& rBoxes
= pLine
->GetTabBoxes();
173 for( SwTableBoxes::size_type n
= 0, nBoxes
= rBoxes
.size() - 1; n
< nBoxes
; ++n
)
177 SwTableBox
* pBox
= rBoxes
[ n
];
178 if( pBox
->GetSttNd() )
179 aBoxes
.insert( aBoxes
.begin(), pBox
);
181 lcl_GCBorder_GetLastBox_B( pBox
, &aBoxes
);
184 for( SwTableBoxes::size_type i
= aBoxes
.size(); i
; )
186 SwTableBox
* pBox
= aBoxes
[ --i
];
187 if( const SvxBoxItem
* pItem
= pBox
->GetFrameFormat()->GetItemIfSet( RES_BOX
) )
189 pBrd
= pItem
->GetRight();
192 aBPara
.SetBorder( *pBrd
);
193 const SwTableBox
* pNextBox
= rBoxes
[n
+1];
194 if( lcl_GCBorder_ChkBoxBrd_B( pNextBox
, &aBPara
) &&
195 aBPara
.IsAnyBorderFound() )
197 SvxBoxItem
aBox( *pItem
);
198 aBox
.SetLine( nullptr, SvxBoxItemLine::RIGHT
);
199 if( pGCPara
->pShareFormats
)
200 pGCPara
->pShareFormats
->SetAttr( *pBox
, aBox
);
202 pBox
->ClaimFrameFormat()->SetFormatAttr( aBox
);
212 // And now the own bottom edge with the succeeding top edge
213 if( !pGCPara
->IsLastLine() )
215 SwCollectTableLineBoxes
aBottom( false );
216 SwCollectTableLineBoxes
aTop( true );
218 sw_Line_CollectBox( pLine
, &aBottom
);
220 const SwTableLine
* pNextLine
= (*pGCPara
->pLines
)[ pGCPara
->nLinePos
+1 ];
221 sw_Line_CollectBox( pNextLine
, &aTop
);
223 // remove all "duplicated" Lines that are the same
224 sal_uInt16 nBtmPos
, nTopPos
;
228 const size_t nEndBtm
{aBottom
.Count()};
229 const size_t nEndTop
{aTop
.Count()};
231 const SwTableBox
*pBtmBox
= &aBottom
.GetBox( nSttBtm
++, &nBtmPos
);
232 const SwTableBox
*pTopBox
= &aTop
.GetBox( nSttTop
++, &nTopPos
);
233 const SvxBoxItem
*pBtmItem
= nullptr, *pTopItem
= nullptr;
234 const SvxBorderLine
*pBtmLine(nullptr), *pTopLine(nullptr);
235 bool bGetTopItem
= true, bGetBtmItem
= true;
239 pBtmLine
= lcl_GCBorder_GetBorder( *pBtmBox
, false, &pBtmItem
);
241 pTopLine
= lcl_GCBorder_GetBorder( *pTopBox
, true, &pTopItem
);
243 if( pTopLine
&& pBtmLine
&& *pTopLine
== *pBtmLine
)
245 // We can remove one, but which one?
246 const size_t nSavSttBtm
{nSttBtm
};
247 const size_t nSavSttTop
{nSttTop
};
248 sal_uInt16 nBtmEndPos
= ::lcl_FindEndPosOfBorder( aBottom
,
249 *pTopLine
, nSttBtm
, false );
250 if( !nBtmEndPos
) nBtmEndPos
= nBtmPos
;
251 sal_uInt16 nTopEndPos
= ::lcl_FindEndPosOfBorder( aTop
,
252 *pTopLine
, nSttTop
, true );
253 if( !nTopEndPos
) nTopEndPos
= nTopPos
;
255 if( nTopEndPos
<= nBtmEndPos
)
257 // Delete the TopBorders until BottomEndPos
258 nSttTop
= nSavSttTop
;
259 if( nTopPos
<= nBtmEndPos
)
260 lcl_GCBorder_DelBorder( aTop
, --nSttTop
, true,
261 *pBtmLine
, pTopItem
, nBtmEndPos
,
262 pGCPara
->pShareFormats
);
264 nSttBtm
= nSavSttBtm
;
268 // Else delete the BottomBorders until TopEndPos
269 nSttBtm
= nSavSttBtm
;
270 if( nBtmPos
<= nTopEndPos
)
271 lcl_GCBorder_DelBorder( aBottom
, --nSttBtm
, false,
272 *pTopLine
, pBtmItem
, nTopEndPos
,
273 pGCPara
->pShareFormats
);
275 nSttTop
= nSavSttTop
;
280 if( nTopPos
== nBtmPos
)
282 if( nSttBtm
>= nEndBtm
|| nSttTop
>= nEndTop
)
285 pBtmBox
= &aBottom
.GetBox( nSttBtm
++, &nBtmPos
);
286 pTopBox
= &aTop
.GetBox( nSttTop
++, &nTopPos
);
287 bGetTopItem
= bGetBtmItem
= true;
289 else if( nTopPos
< nBtmPos
)
291 if( nSttTop
>= nEndTop
)
293 pTopBox
= &aTop
.GetBox( nSttTop
++, &nTopPos
);
299 if( nSttBtm
>= nEndBtm
)
301 pBtmBox
= &aBottom
.GetBox( nSttBtm
++, &nBtmPos
);
309 for( const auto& rpBox
: pLine
->GetTabBoxes() )
310 lcl_GC_Box_Border(rpBox
, pGCPara
);
315 static void lcl_GC_Box_Border( const SwTableBox
* pBox
, SwGCLineBorder
* pPara
)
317 if( !pBox
->GetTabLines().empty() )
319 SwGCLineBorder
aPara( *pBox
);
320 aPara
.pShareFormats
= pPara
->pShareFormats
;
321 for( const SwTableLine
* pLine
: pBox
->GetTabLines() )
322 sw_GC_Line_Border( pLine
, &aPara
);
331 SwShareBoxFormats
* pShareFormats
;
333 GCLinePara( SwTableLines
& rLns
, GCLinePara
* pPara
= nullptr )
334 : pLns( &rLns
), pShareFormats( pPara
? pPara
->pShareFormats
: nullptr )
340 static bool lcl_MergeGCLine(SwTableLine
* pLine
, GCLinePara
* pPara
);
342 static bool lcl_MergeGCBox(SwTableBox
* pTableBox
, GCLinePara
* pPara
)
344 if( !pTableBox
->GetTabLines().empty() )
346 // ATTENTION: The Line count can change!
347 GCLinePara
aPara( pTableBox
->GetTabLines(), pPara
);
348 for( SwTableLines::size_type n
= 0;
349 n
< pTableBox
->GetTabLines().size() && lcl_MergeGCLine( pTableBox
->GetTabLines()[n
], &aPara
);
353 if( 1 == pTableBox
->GetTabLines().size() )
355 // we have a box with a single line, so we just replace it by the line's boxes
356 SwTableLine
* pInsLine
= pTableBox
->GetUpper();
357 SwTableLine
* pCpyLine
= pTableBox
->GetTabLines()[0];
358 SwTableBoxes::iterator it
= std::find( pInsLine
->GetTabBoxes().begin(), pInsLine
->GetTabBoxes().end(), pTableBox
);
359 for( auto pTabBox
: pCpyLine
->GetTabBoxes() )
360 pTabBox
->SetUpper( pInsLine
);
362 SfxPoolItem
const* pRowBrush(nullptr);
363 pCpyLine
->GetFrameFormat()->GetItemState(RES_BACKGROUND
, true, &pRowBrush
);
366 for (auto pBox
: pCpyLine
->GetTabBoxes())
368 if (pBox
->GetFrameFormat()->GetItemState(RES_BACKGROUND
) != SfxItemState::SET
)
369 { // set inner row background on inner cell
370 pBox
->ClaimFrameFormat();
371 pBox
->GetFrameFormat()->SetFormatAttr(*pRowBrush
);
376 // remove the old box from its parent line
377 it
= pInsLine
->GetTabBoxes().erase( it
);
378 // insert the nested line's boxes in its place
379 pInsLine
->GetTabBoxes().insert( it
, pCpyLine
->GetTabBoxes().begin(), pCpyLine
->GetTabBoxes().end());
380 pCpyLine
->GetTabBoxes().clear();
381 // destroy the removed box
384 return false; // set up anew
390 static bool lcl_MergeGCLine(SwTableLine
* pLn
, GCLinePara
* pGCPara
)
392 SwTableBoxes::size_type nBoxes
= pLn
->GetTabBoxes().size();
397 // We have a Box with Lines
398 SwTableBox
* pBox
= pLn
->GetTabBoxes().front();
399 if( pBox
->GetTabLines().empty() )
402 SwTableLine
* pLine
= pBox
->GetTabLines()[0];
404 // pLine turns into the current Line (that is rpLine), the rest is moved
405 // into the LinesArray past the current one.
406 // The LinesArray is in pPara!
407 SwTableLines::size_type nLines
= pBox
->GetTabLines().size();
409 SwTableLines
& rLns
= *pGCPara
->pLns
;
410 sal_uInt16 nInsPos
= rLns
.GetPos( pLn
);
411 OSL_ENSURE( USHRT_MAX
!= nInsPos
, "Could not find Line!" );
413 SwTableBox
* pUpper
= pLn
->GetUpper();
415 rLns
.erase( rLns
.begin() + nInsPos
); // remove the Line from the array
416 rLns
.insert( rLns
.begin() + nInsPos
, pBox
->GetTabLines().begin(), pBox
->GetTabLines().end() );
418 // JP 31.03.99: Bug 60000
419 // Pass the attributes of the to-be-deleted Lines to the "inserted" one
420 const SfxPoolItem
* pItem
;
421 if( SfxItemState::SET
== pLn
->GetFrameFormat()->GetItemState(
422 RES_BACKGROUND
, true, &pItem
))
424 SwTableLines
& rBoxLns
= pBox
->GetTabLines();
425 for( auto pBoxLine
: rBoxLns
)
426 if( SfxItemState::SET
!= pBoxLine
->GetFrameFormat()->
427 GetItemState( RES_BACKGROUND
))
428 pGCPara
->pShareFormats
->SetAttr( *pBoxLine
, *pItem
);
431 pBox
->GetTabLines().erase( pBox
->GetTabLines().begin(), pBox
->GetTabLines().begin() + nLines
); // Remove Lines from the array
435 // Set the dependency anew
437 rLns
[ nInsPos
++ ]->SetUpper( pUpper
);
439 pLn
= pLine
; // and set up anew
440 nBoxes
= pLn
->GetTabBoxes().size();
443 // ATTENTION: The number of boxes can change!
444 for( SwTableBoxes::size_type nLen
= 0; nLen
< pLn
->GetTabBoxes().size(); ++nLen
)
445 if( !lcl_MergeGCBox( pLn
->GetTabBoxes()[nLen
], pGCPara
))
451 // Clean structure a bit
452 void SwTable::GCLines()
454 // ATTENTION: The Line count can change!
455 GCLinePara
aPara( GetTabLines() );
456 SwShareBoxFormats aShareFormats
;
457 aPara
.pShareFormats
= &aShareFormats
;
458 for( SwTableLines::size_type n
= 0; n
< GetTabLines().size() &&
459 lcl_MergeGCLine( GetTabLines()[n
], &aPara
); ++n
)
463 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */