File: | home/maarten/src/libreoffice/core/sw/source/core/table/swtable.cxx |
Warning: | line 1583, column 38 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 <hintids.hxx> | |||
21 | #include <hints.hxx> | |||
22 | #include <editeng/lrspitem.hxx> | |||
23 | #include <editeng/shaditem.hxx> | |||
24 | #include <editeng/adjustitem.hxx> | |||
25 | #include <editeng/colritem.hxx> | |||
26 | #include <sfx2/linkmgr.hxx> | |||
27 | #include <fmtfsize.hxx> | |||
28 | #include <fmtornt.hxx> | |||
29 | #include <fmtpdsc.hxx> | |||
30 | #include <fldbas.hxx> | |||
31 | #include <fmtfld.hxx> | |||
32 | #include <frmatr.hxx> | |||
33 | #include <doc.hxx> | |||
34 | #include <IDocumentLinksAdministration.hxx> | |||
35 | #include <IDocumentRedlineAccess.hxx> | |||
36 | #include <IDocumentFieldsAccess.hxx> | |||
37 | #include <docary.hxx> | |||
38 | #include <frame.hxx> | |||
39 | #include <swtable.hxx> | |||
40 | #include <ndtxt.hxx> | |||
41 | #include <tabcol.hxx> | |||
42 | #include <tabfrm.hxx> | |||
43 | #include <cellfrm.hxx> | |||
44 | #include <rowfrm.hxx> | |||
45 | #include <swserv.hxx> | |||
46 | #include <expfld.hxx> | |||
47 | #include <mdiexp.hxx> | |||
48 | #include <cellatr.hxx> | |||
49 | #include <txatbase.hxx> | |||
50 | #include <htmltbl.hxx> | |||
51 | #include <swtblfmt.hxx> | |||
52 | #include <ndindex.hxx> | |||
53 | #include <tblrwcl.hxx> | |||
54 | #include <shellres.hxx> | |||
55 | #include <viewsh.hxx> | |||
56 | #include <redline.hxx> | |||
57 | #include <vector> | |||
58 | #include <calbck.hxx> | |||
59 | ||||
60 | #ifdef DBG_UTIL | |||
61 | #define CHECK_TABLE(t) (t).CheckConsistency(); | |||
62 | #else | |||
63 | #define CHECK_TABLE(t) | |||
64 | #endif | |||
65 | ||||
66 | using namespace com::sun::star; | |||
67 | ||||
68 | ||||
69 | #define COLFUZZY20 20 | |||
70 | ||||
71 | static void ChgTextToNum( SwTableBox& rBox, const OUString& rText, const Color* pCol, | |||
72 | bool bChgAlign, sal_uLong nNdPos ); | |||
73 | ||||
74 | inline const Color* SwTableBox::GetSaveUserColor() const | |||
75 | { | |||
76 | return mpUserColor.get(); | |||
77 | } | |||
78 | ||||
79 | inline const Color* SwTableBox::GetSaveNumFormatColor() const | |||
80 | { | |||
81 | return mpNumFormatColor.get(); | |||
82 | } | |||
83 | ||||
84 | inline void SwTableBox::SetSaveUserColor(const Color* p ) | |||
85 | { | |||
86 | if (p) | |||
87 | mpUserColor.reset(new Color(*p)); | |||
88 | else | |||
89 | mpUserColor.reset(); | |||
90 | } | |||
91 | ||||
92 | inline void SwTableBox::SetSaveNumFormatColor( const Color* p ) | |||
93 | { | |||
94 | if (p) | |||
95 | mpNumFormatColor.reset(new Color(*p)); | |||
96 | else | |||
97 | mpNumFormatColor.reset(); | |||
98 | } | |||
99 | ||||
100 | long SwTableBox::getRowSpan() const | |||
101 | { | |||
102 | return mnRowSpan; | |||
103 | } | |||
104 | ||||
105 | void SwTableBox::setRowSpan( long nNewRowSpan ) | |||
106 | { | |||
107 | mnRowSpan = nNewRowSpan; | |||
108 | } | |||
109 | ||||
110 | bool SwTableBox::getDummyFlag() const | |||
111 | { | |||
112 | return mbDummyFlag; | |||
113 | } | |||
114 | ||||
115 | void SwTableBox::setDummyFlag( bool bDummy ) | |||
116 | { | |||
117 | mbDummyFlag = bDummy; | |||
118 | } | |||
119 | ||||
120 | //JP 15.09.98: Bug 55741 - Keep tabs (front and rear) | |||
121 | static OUString& lcl_TabToBlankAtSttEnd( OUString& rText ) | |||
122 | { | |||
123 | sal_Unicode c; | |||
124 | sal_Int32 n; | |||
125 | ||||
126 | for( n = 0; n < rText.getLength() && ' ' >= ( c = rText[n] ); ++n ) | |||
127 | if( '\x9' == c ) | |||
128 | rText = rText.replaceAt( n, 1, " " ); | |||
129 | for( n = rText.getLength(); n && ' ' >= ( c = rText[--n] ); ) | |||
130 | if( '\x9' == c ) | |||
131 | rText = rText.replaceAt( n, 1, " " ); | |||
132 | return rText; | |||
133 | } | |||
134 | ||||
135 | static OUString& lcl_DelTabsAtSttEnd( OUString& rText ) | |||
136 | { | |||
137 | sal_Unicode c; | |||
138 | sal_Int32 n; | |||
139 | OUStringBuffer sBuff(rText); | |||
140 | ||||
141 | for( n = 0; n < sBuff.getLength() && ' ' >= ( c = sBuff[ n ]); ++n ) | |||
142 | { | |||
143 | if( '\x9' == c ) | |||
144 | sBuff.remove( n--, 1 ); | |||
145 | } | |||
146 | for( n = sBuff.getLength(); n && ' ' >= ( c = sBuff[ --n ]); ) | |||
147 | { | |||
148 | if( '\x9' == c ) | |||
149 | sBuff.remove( n, 1 ); | |||
150 | } | |||
151 | rText = sBuff.makeStringAndClear(); | |||
152 | return rText; | |||
153 | } | |||
154 | ||||
155 | void InsTableBox( SwDoc& rDoc, SwTableNode* pTableNd, | |||
156 | SwTableLine* pLine, SwTableBoxFormat* pBoxFrameFormat, | |||
157 | SwTableBox* pBox, | |||
158 | sal_uInt16 nInsPos, sal_uInt16 nCnt ) | |||
159 | { | |||
160 | OSL_ENSURE( pBox->GetSttNd(), "Box with no start node" )do { if (true && (!(pBox->GetSttNd()))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sw/source/core/table/swtable.cxx" ":" "160" ": "), "%s", "Box with no start node"); } } while ( false); | |||
161 | SwNodeIndex aIdx( *pBox->GetSttNd(), +1 ); | |||
162 | SwContentNode* pCNd = aIdx.GetNode().GetContentNode(); | |||
163 | if( !pCNd ) | |||
164 | pCNd = rDoc.GetNodes().GoNext( &aIdx ); | |||
165 | OSL_ENSURE( pCNd, "Box with no content node" )do { if (true && (!(pCNd))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN ), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sw/source/core/table/swtable.cxx" ":" "165" ": "), "%s", "Box with no content node"); } } while (false); | |||
166 | ||||
167 | if( pCNd->IsTextNode() ) | |||
168 | { | |||
169 | if( pBox->GetSaveNumFormatColor() && pCNd->GetpSwAttrSet() ) | |||
170 | { | |||
171 | SwAttrSet aAttrSet( *pCNd->GetpSwAttrSet() ); | |||
172 | if( pBox->GetSaveUserColor() ) | |||
173 | aAttrSet.Put( SvxColorItem( *pBox->GetSaveUserColor(), RES_CHRATR_COLOR )); | |||
174 | else | |||
175 | aAttrSet.ClearItem( RES_CHRATR_COLOR ); | |||
176 | rDoc.GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat, | |||
177 | static_cast<SwTextNode*>(pCNd)->GetTextColl(), | |||
178 | &aAttrSet, nInsPos, nCnt ); | |||
179 | } | |||
180 | else | |||
181 | rDoc.GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat, | |||
182 | static_cast<SwTextNode*>(pCNd)->GetTextColl(), | |||
183 | pCNd->GetpSwAttrSet(), | |||
184 | nInsPos, nCnt ); | |||
185 | } | |||
186 | else | |||
187 | rDoc.GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat, | |||
188 | rDoc.GetDfltTextFormatColl(), nullptr, | |||
189 | nInsPos, nCnt ); | |||
190 | ||||
191 | long nRowSpan = pBox->getRowSpan(); | |||
192 | if( nRowSpan != 1 ) | |||
193 | { | |||
194 | SwTableBoxes& rTableBoxes = pLine->GetTabBoxes(); | |||
195 | for( sal_uInt16 i = 0; i < nCnt; ++i ) | |||
196 | { | |||
197 | pBox = rTableBoxes[ i + nInsPos ]; | |||
198 | pBox->setRowSpan( nRowSpan ); | |||
199 | } | |||
200 | } | |||
201 | } | |||
202 | ||||
203 | SwTable::SwTable() | |||
204 | : SwClient( nullptr ), | |||
205 | m_pTableNode( nullptr ), | |||
206 | m_nGraphicsThatResize( 0 ), | |||
207 | m_nRowsToRepeat( 1 ), | |||
208 | m_bModifyLocked( false ), | |||
209 | m_bNewModel( true ) | |||
210 | { | |||
211 | // default value set in the options | |||
212 | m_eTableChgMode = GetTableChgDefaultMode(); | |||
213 | } | |||
214 | ||||
215 | SwTable::SwTable( const SwTable& rTable ) | |||
216 | : SwClient( rTable.GetFrameFormat() ), | |||
217 | m_pTableNode( nullptr ), | |||
218 | m_eTableChgMode( rTable.m_eTableChgMode ), | |||
219 | m_nGraphicsThatResize( 0 ), | |||
220 | m_nRowsToRepeat( rTable.GetRowsToRepeat() ), | |||
221 | maTableStyleName(rTable.maTableStyleName), | |||
222 | m_bModifyLocked( false ), | |||
223 | m_bNewModel( rTable.m_bNewModel ) | |||
224 | { | |||
225 | } | |||
226 | ||||
227 | void DelBoxNode( SwTableSortBoxes const & rSortCntBoxes ) | |||
228 | { | |||
229 | for (size_t n = 0; n < rSortCntBoxes.size(); ++n) | |||
230 | { | |||
231 | rSortCntBoxes[ n ]->m_pStartNode = nullptr; | |||
232 | } | |||
233 | } | |||
234 | ||||
235 | SwTable::~SwTable() | |||
236 | { | |||
237 | if( m_xRefObj.is() ) | |||
238 | { | |||
239 | SwDoc* pDoc = GetFrameFormat()->GetDoc(); | |||
240 | if( !pDoc->IsInDtor() ) // then remove from the list | |||
241 | pDoc->getIDocumentLinksAdministration().GetLinkManager().RemoveServer( m_xRefObj.get() ); | |||
242 | ||||
243 | m_xRefObj->Closed(); | |||
244 | } | |||
245 | ||||
246 | // the table can be deleted if it's the last client of the FrameFormat | |||
247 | SwTableFormat* pFormat = GetFrameFormat(); | |||
248 | pFormat->Remove( this ); // remove | |||
249 | ||||
250 | if( !pFormat->HasWriterListeners() ) | |||
251 | pFormat->GetDoc()->DelTableFrameFormat( pFormat ); // and delete | |||
252 | ||||
253 | // Delete the pointers from the SortArray of the boxes. The objects | |||
254 | // are preserved and are deleted by the lines/boxes arrays dtor. | |||
255 | // Note: unfortunately not enough, pointers to the StartNode of the | |||
256 | // section need deletion. | |||
257 | DelBoxNode(m_TabSortContentBoxes); | |||
258 | m_TabSortContentBoxes.clear(); | |||
259 | } | |||
260 | ||||
261 | namespace | |||
262 | { | |||
263 | ||||
264 | template<class T> | |||
265 | T lcl_MulDiv64(sal_uInt64 nA, sal_uInt64 nM, sal_uInt64 nD) | |||
266 | { | |||
267 | return static_cast<T>((nA*nM)/nD); | |||
268 | } | |||
269 | ||||
270 | } | |||
271 | ||||
272 | static void FormatInArr( std::vector<SwFormat*>& rFormatArr, SwFormat* pBoxFormat ) | |||
273 | { | |||
274 | std::vector<SwFormat*>::const_iterator it = std::find( rFormatArr.begin(), rFormatArr.end(), pBoxFormat ); | |||
275 | if ( it == rFormatArr.end() ) | |||
276 | rFormatArr.push_back( pBoxFormat ); | |||
277 | } | |||
278 | ||||
279 | static void lcl_ModifyBoxes( SwTableBoxes &rBoxes, const long nOld, | |||
280 | const long nNew, std::vector<SwFormat*>& rFormatArr ); | |||
281 | ||||
282 | static void lcl_ModifyLines( SwTableLines &rLines, const long nOld, | |||
283 | const long nNew, std::vector<SwFormat*>& rFormatArr, const bool bCheckSum ) | |||
284 | { | |||
285 | for ( size_t i = 0; i < rLines.size(); ++i ) | |||
286 | ::lcl_ModifyBoxes( rLines[i]->GetTabBoxes(), nOld, nNew, rFormatArr ); | |||
287 | if( bCheckSum ) | |||
288 | { | |||
289 | for(SwFormat* pFormat : rFormatArr) | |||
290 | { | |||
291 | const SwTwips nBox = lcl_MulDiv64<SwTwips>(pFormat->GetFrameSize().GetWidth(), nNew, nOld); | |||
292 | SwFormatFrameSize aNewBox( SwFrameSize::Variable, nBox, 0 ); | |||
293 | pFormat->LockModify(); | |||
294 | pFormat->SetFormatAttr( aNewBox ); | |||
295 | pFormat->UnlockModify(); | |||
296 | } | |||
297 | } | |||
298 | } | |||
299 | ||||
300 | static void lcl_ModifyBoxes( SwTableBoxes &rBoxes, const long nOld, | |||
301 | const long nNew, std::vector<SwFormat*>& rFormatArr ) | |||
302 | { | |||
303 | sal_uInt64 nSum = 0; // To avoid rounding errors we summarize all box widths | |||
304 | sal_uInt64 nOriginalSum = 0; // Sum of original widths | |||
305 | for ( size_t i = 0; i < rBoxes.size(); ++i ) | |||
306 | { | |||
307 | SwTableBox &rBox = *rBoxes[i]; | |||
308 | if ( !rBox.GetTabLines().empty() ) | |||
309 | { | |||
310 | // For SubTables the rounding problem will not be solved :-( | |||
311 | ::lcl_ModifyLines( rBox.GetTabLines(), nOld, nNew, rFormatArr, false ); | |||
312 | } | |||
313 | // Adjust the box | |||
314 | SwFrameFormat *pFormat = rBox.GetFrameFormat(); | |||
315 | sal_uInt64 nBox = pFormat->GetFrameSize().GetWidth(); | |||
316 | nOriginalSum += nBox; | |||
317 | nBox *= nNew; | |||
318 | nBox /= nOld; | |||
319 | const sal_uInt64 nWishedSum = lcl_MulDiv64<sal_uInt64>(nOriginalSum, nNew, nOld) - nSum; | |||
320 | if( nWishedSum > 0 ) | |||
321 | { | |||
322 | if( nBox == nWishedSum ) | |||
323 | FormatInArr( rFormatArr, pFormat ); | |||
324 | else | |||
325 | { | |||
326 | nBox = nWishedSum; | |||
327 | pFormat = rBox.ClaimFrameFormat(); | |||
328 | SwFormatFrameSize aNewBox( SwFrameSize::Variable, static_cast< SwTwips >(nBox), 0 ); | |||
329 | pFormat->LockModify(); | |||
330 | pFormat->SetFormatAttr( aNewBox ); | |||
331 | pFormat->UnlockModify(); | |||
332 | } | |||
333 | } | |||
334 | else { | |||
335 | OSL_FAIL( "Rounding error" )do { if (true && (((sal_Bool)1))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sw/source/core/table/swtable.cxx" ":" "335" ": "), "%s", "Rounding error"); } } while (false); | |||
336 | } | |||
337 | nSum += nBox; | |||
338 | } | |||
339 | } | |||
340 | ||||
341 | void SwTable::SwClientNotify(const SwModify&, const SfxHint& rHint) | |||
342 | { | |||
343 | auto pLegacy = dynamic_cast<const sw::LegacyModifyHint*>(&rHint); | |||
344 | if(!pLegacy) | |||
345 | return; | |||
346 | // catch SSize changes, to adjust the lines/boxes | |||
347 | const sal_uInt16 nWhich = pLegacy->GetWhich(); | |||
348 | const SwFormatFrameSize* pNewSize = nullptr, *pOldSize = nullptr; | |||
349 | switch(nWhich) | |||
350 | { | |||
351 | case RES_ATTRSET_CHG: | |||
352 | { | |||
353 | if (pLegacy->m_pOld && pLegacy->m_pNew | |||
354 | && SfxItemState::SET == static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet()->GetItemState( | |||
355 | RES_FRM_SIZE, | |||
356 | false, | |||
357 | reinterpret_cast<const SfxPoolItem**>(&pNewSize))) | |||
358 | { | |||
359 | pOldSize = &static_cast<const SwAttrSetChg*>(pLegacy->m_pOld)->GetChgSet()->GetFrameSize(); | |||
360 | } | |||
361 | } | |||
362 | break; | |||
363 | case RES_FRM_SIZE: | |||
364 | { | |||
365 | pOldSize = static_cast<const SwFormatFrameSize*>(pLegacy->m_pOld); | |||
366 | pNewSize = static_cast<const SwFormatFrameSize*>(pLegacy->m_pNew); | |||
367 | } | |||
368 | break; | |||
369 | default: | |||
370 | CheckRegistration(pLegacy->m_pOld); | |||
371 | } | |||
372 | if (pOldSize && pNewSize && !m_bModifyLocked) | |||
373 | AdjustWidths(pOldSize->GetWidth(), pNewSize->GetWidth()); | |||
374 | } | |||
375 | ||||
376 | void SwTable::AdjustWidths( const long nOld, const long nNew ) | |||
377 | { | |||
378 | std::vector<SwFormat*> aFormatArr; | |||
379 | aFormatArr.reserve( m_aLines[0]->GetTabBoxes().size() ); | |||
380 | ::lcl_ModifyLines( m_aLines, nOld, nNew, aFormatArr, true ); | |||
381 | } | |||
382 | ||||
383 | static void lcl_RefreshHidden( SwTabCols &rToFill, size_t nPos ) | |||
384 | { | |||
385 | for ( size_t i = 0; i < rToFill.Count(); ++i ) | |||
386 | { | |||
387 | if ( std::abs(static_cast<long>(nPos) - rToFill[i]) <= COLFUZZY20 ) | |||
388 | { | |||
389 | rToFill.SetHidden( i, false ); | |||
390 | break; | |||
391 | } | |||
392 | } | |||
393 | } | |||
394 | ||||
395 | static void lcl_SortedTabColInsert( SwTabCols &rToFill, const SwTableBox *pBox, | |||
396 | const SwFrameFormat *pTabFormat, const bool bHidden, | |||
397 | const bool bRefreshHidden ) | |||
398 | { | |||
399 | const long nWish = pTabFormat->GetFrameSize().GetWidth(); | |||
400 | OSL_ENSURE(nWish, "weird <= 0 width frmfrm")do { if (true && (!(nWish))) { sal_detail_logFormat(( SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sw/source/core/table/swtable.cxx" ":" "400" ": "), "%s", "weird <= 0 width frmfrm"); } } while (false); | |||
401 | ||||
402 | // The value for the left edge of the box is calculated from the | |||
403 | // widths of the previous boxes. | |||
404 | long nPos = 0; | |||
405 | long nLeftMin = 0; | |||
406 | long nRightMax = 0; | |||
407 | if (nWish != 0) //fdo#33012 0 width frmfmt | |||
408 | { | |||
409 | SwTwips nSum = 0; | |||
410 | const SwTableBox *pCur = pBox; | |||
411 | const SwTableLine *pLine = pBox->GetUpper(); | |||
412 | const long nAct = rToFill.GetRight() - rToFill.GetLeft(); // +1 why? | |||
413 | ||||
414 | while ( pLine ) | |||
415 | { | |||
416 | const SwTableBoxes &rBoxes = pLine->GetTabBoxes(); | |||
417 | for ( size_t i = 0; i < rBoxes.size(); ++i ) | |||
418 | { | |||
419 | const SwTwips nWidth = rBoxes[i]->GetFrameFormat()->GetFrameSize().GetWidth(); | |||
420 | nSum += nWidth; | |||
421 | const long nTmp = lcl_MulDiv64<long>(nSum, nAct, nWish); | |||
422 | ||||
423 | if (rBoxes[i] != pCur) | |||
424 | { | |||
425 | if ( pLine == pBox->GetUpper() || 0 == nLeftMin ) | |||
426 | nLeftMin = nTmp - nPos; | |||
427 | nPos = nTmp; | |||
428 | } | |||
429 | else | |||
430 | { | |||
431 | nSum -= nWidth; | |||
432 | if ( 0 == nRightMax ) | |||
433 | nRightMax = nTmp - nPos; | |||
434 | break; | |||
435 | } | |||
436 | } | |||
437 | pCur = pLine->GetUpper(); | |||
438 | pLine = pCur ? pCur->GetUpper() : nullptr; | |||
439 | } | |||
440 | } | |||
441 | ||||
442 | bool bInsert = !bRefreshHidden; | |||
443 | for ( size_t j = 0; bInsert && (j < rToFill.Count()); ++j ) | |||
444 | { | |||
445 | long nCmp = rToFill[j]; | |||
446 | if ( (nPos >= ((nCmp >= COLFUZZY20) ? nCmp - COLFUZZY20 : nCmp)) && | |||
447 | (nPos <= (nCmp + COLFUZZY20)) ) | |||
448 | { | |||
449 | bInsert = false; // Already has it. | |||
450 | } | |||
451 | else if ( nPos < nCmp ) | |||
452 | { | |||
453 | bInsert = false; | |||
454 | rToFill.Insert( nPos, bHidden, j ); | |||
455 | } | |||
456 | } | |||
457 | if ( bInsert ) | |||
458 | rToFill.Insert( nPos, bHidden, rToFill.Count() ); | |||
459 | else if ( bRefreshHidden ) | |||
460 | ::lcl_RefreshHidden( rToFill, nPos ); | |||
461 | ||||
462 | if ( !bHidden || bRefreshHidden ) | |||
463 | return; | |||
464 | ||||
465 | // calculate minimum/maximum values for the existing entries: | |||
466 | nLeftMin = nPos - nLeftMin; | |||
467 | nRightMax = nPos + nRightMax; | |||
468 | ||||
469 | // check if nPos is entry: | |||
470 | bool bFoundPos = false; | |||
471 | bool bFoundMax = false; | |||
472 | for ( size_t j = 0; !(bFoundPos && bFoundMax ) && j < rToFill.Count(); ++j ) | |||
473 | { | |||
474 | SwTabColsEntry& rEntry = rToFill.GetEntry( j ); | |||
475 | long nCmp = rToFill[j]; | |||
476 | ||||
477 | if ( (nPos >= ((nCmp >= COLFUZZY20) ? nCmp - COLFUZZY20 : nCmp)) && | |||
478 | (nPos <= (nCmp + COLFUZZY20)) ) | |||
479 | { | |||
480 | // check if nLeftMin is > old minimum for entry nPos: | |||
481 | const long nOldMin = rEntry.nMin; | |||
482 | if ( nLeftMin > nOldMin ) | |||
483 | rEntry.nMin = nLeftMin; | |||
484 | // check if nRightMin is < old maximum for entry nPos: | |||
485 | const long nOldMax = rEntry.nMax; | |||
486 | if ( nRightMax < nOldMax ) | |||
487 | rEntry.nMax = nRightMax; | |||
488 | ||||
489 | bFoundPos = true; | |||
490 | } | |||
491 | else if ( (nRightMax >= ((nCmp >= COLFUZZY20) ? nCmp - COLFUZZY20 : nCmp)) && | |||
492 | (nRightMax <= (nCmp + COLFUZZY20)) ) | |||
493 | { | |||
494 | // check if nPos is > old minimum for entry nRightMax: | |||
495 | const long nOldMin = rEntry.nMin; | |||
496 | if ( nPos > nOldMin ) | |||
497 | rEntry.nMin = nPos; | |||
498 | ||||
499 | bFoundMax = true; | |||
500 | } | |||
501 | } | |||
502 | } | |||
503 | ||||
504 | static void lcl_ProcessBoxGet( const SwTableBox *pBox, SwTabCols &rToFill, | |||
505 | const SwFrameFormat *pTabFormat, bool bRefreshHidden ) | |||
506 | { | |||
507 | if ( !pBox->GetTabLines().empty() ) | |||
508 | { | |||
509 | const SwTableLines &rLines = pBox->GetTabLines(); | |||
510 | for ( size_t i = 0; i < rLines.size(); ++i ) | |||
511 | { | |||
512 | const SwTableBoxes &rBoxes = rLines[i]->GetTabBoxes(); | |||
513 | for ( size_t j = 0; j < rBoxes.size(); ++j ) | |||
514 | ::lcl_ProcessBoxGet( rBoxes[j], rToFill, pTabFormat, bRefreshHidden); | |||
515 | } | |||
516 | } | |||
517 | else | |||
518 | ::lcl_SortedTabColInsert( rToFill, pBox, pTabFormat, false, bRefreshHidden ); | |||
519 | } | |||
520 | ||||
521 | static void lcl_ProcessLineGet( const SwTableLine *pLine, SwTabCols &rToFill, | |||
522 | const SwFrameFormat *pTabFormat ) | |||
523 | { | |||
524 | for ( size_t i = 0; i < pLine->GetTabBoxes().size(); ++i ) | |||
525 | { | |||
526 | const SwTableBox *pBox = pLine->GetTabBoxes()[i]; | |||
527 | if ( pBox->GetSttNd() ) | |||
528 | ::lcl_SortedTabColInsert( rToFill, pBox, pTabFormat, true, false ); | |||
529 | else | |||
530 | for ( size_t j = 0; j < pBox->GetTabLines().size(); ++j ) | |||
531 | ::lcl_ProcessLineGet( pBox->GetTabLines()[j], rToFill, pTabFormat ); | |||
532 | } | |||
533 | } | |||
534 | ||||
535 | void SwTable::GetTabCols( SwTabCols &rToFill, const SwTableBox *pStart, | |||
536 | bool bRefreshHidden, bool bCurRowOnly ) const | |||
537 | { | |||
538 | // Optimization: if bHidden is set, we only update the Hidden Array. | |||
539 | if ( bRefreshHidden ) | |||
540 | { | |||
541 | // remove corrections | |||
542 | for ( size_t i = 0; i < rToFill.Count(); ++i ) | |||
543 | { | |||
544 | SwTabColsEntry& rEntry = rToFill.GetEntry( i ); | |||
545 | rEntry.nPos -= rToFill.GetLeft(); | |||
546 | rEntry.nMin -= rToFill.GetLeft(); | |||
547 | rEntry.nMax -= rToFill.GetLeft(); | |||
548 | } | |||
549 | ||||
550 | // All are hidden, so add the visible ones. | |||
551 | for ( size_t i = 0; i < rToFill.Count(); ++i ) | |||
552 | rToFill.SetHidden( i, true ); | |||
553 | } | |||
554 | else | |||
555 | { | |||
556 | rToFill.Remove( 0, rToFill.Count() ); | |||
557 | } | |||
558 | ||||
559 | // Insertion cases: | |||
560 | // 1. All boxes which are inferior to Line which is superior to the Start, | |||
561 | // as well as their inferior boxes if present. | |||
562 | // 2. Starting from the Line, the superior box plus its neighbours; but no inferiors. | |||
563 | // 3. Apply 2. to the Line superior to the chain of boxes, | |||
564 | // until the Line's superior is not a box but the table. | |||
565 | // Only those boxes are inserted that don't contain further rows. The insertion | |||
566 | // function takes care to avoid duplicates. In order to achieve this, we work | |||
567 | // with some degree of fuzzyness (to avoid rounding errors). | |||
568 | // Only the left edge of the boxes are inserted. | |||
569 | // Finally, the first entry is removed again, because it's already | |||
570 | // covered by the border. | |||
571 | // 4. Scan the table again and insert _all_ boxes, this time as hidden. | |||
572 | ||||
573 | const SwFrameFormat *pTabFormat = GetFrameFormat(); | |||
574 | ||||
575 | // 1. | |||
576 | const SwTableBoxes &rBoxes = pStart->GetUpper()->GetTabBoxes(); | |||
577 | ||||
578 | for ( size_t i = 0; i < rBoxes.size(); ++i ) | |||
579 | ::lcl_ProcessBoxGet( rBoxes[i], rToFill, pTabFormat, bRefreshHidden ); | |||
580 | ||||
581 | // 2. and 3. | |||
582 | const SwTableLine *pLine = pStart->GetUpper()->GetUpper() ? | |||
583 | pStart->GetUpper()->GetUpper()->GetUpper() : nullptr; | |||
584 | while ( pLine ) | |||
585 | { | |||
586 | const SwTableBoxes &rBoxes2 = pLine->GetTabBoxes(); | |||
587 | for ( size_t k = 0; k < rBoxes2.size(); ++k ) | |||
588 | ::lcl_SortedTabColInsert( rToFill, rBoxes2[k], | |||
589 | pTabFormat, false, bRefreshHidden ); | |||
590 | pLine = pLine->GetUpper() ? pLine->GetUpper()->GetUpper() : nullptr; | |||
591 | } | |||
592 | ||||
593 | if ( !bRefreshHidden ) | |||
594 | { | |||
595 | // 4. | |||
596 | if ( !bCurRowOnly ) | |||
597 | { | |||
598 | for ( size_t i = 0; i < m_aLines.size(); ++i ) | |||
599 | ::lcl_ProcessLineGet( m_aLines[i], rToFill, pTabFormat ); | |||
600 | } | |||
601 | ||||
602 | rToFill.Remove( 0 ); | |||
603 | } | |||
604 | ||||
605 | // Now the coordinates are relative to the left table border - i.e. | |||
606 | // relative to SwTabCols.nLeft. However, they are expected | |||
607 | // relative to the left document border, i.e. SwTabCols.nLeftMin. | |||
608 | // So all values need to be extended by nLeft. | |||
609 | for ( size_t i = 0; i < rToFill.Count(); ++i ) | |||
610 | { | |||
611 | SwTabColsEntry& rEntry = rToFill.GetEntry( i ); | |||
612 | rEntry.nPos += rToFill.GetLeft(); | |||
613 | rEntry.nMin += rToFill.GetLeft(); | |||
614 | rEntry.nMax += rToFill.GetLeft(); | |||
615 | } | |||
616 | } | |||
617 | ||||
618 | // Structure for parameter passing | |||
619 | struct Parm | |||
620 | { | |||
621 | const SwTabCols &rNew; | |||
622 | const SwTabCols &rOld; | |||
623 | long nNewWish, | |||
624 | nOldWish; | |||
625 | std::deque<SwTableBox*> aBoxArr; | |||
626 | SwShareBoxFormats aShareFormats; | |||
627 | ||||
628 | Parm( const SwTabCols &rN, const SwTabCols &rO ) | |||
629 | : rNew( rN ), rOld( rO ), nNewWish(0), nOldWish(0) | |||
630 | {} | |||
631 | }; | |||
632 | ||||
633 | static void lcl_ProcessBoxSet( SwTableBox *pBox, Parm &rParm ); | |||
634 | ||||
635 | static void lcl_ProcessLine( SwTableLine *pLine, Parm &rParm ) | |||
636 | { | |||
637 | SwTableBoxes &rBoxes = pLine->GetTabBoxes(); | |||
638 | for ( size_t i = rBoxes.size(); i > 0; ) | |||
639 | { | |||
640 | --i; | |||
641 | ::lcl_ProcessBoxSet( rBoxes[i], rParm ); | |||
642 | } | |||
643 | } | |||
644 | ||||
645 | static void lcl_ProcessBoxSet( SwTableBox *pBox, Parm &rParm ) | |||
646 | { | |||
647 | if ( !pBox->GetTabLines().empty() ) | |||
648 | { | |||
649 | SwTableLines &rLines = pBox->GetTabLines(); | |||
650 | for ( size_t i = rLines.size(); i > 0; ) | |||
651 | { | |||
652 | --i; | |||
653 | lcl_ProcessLine( rLines[i], rParm ); | |||
654 | } | |||
655 | } | |||
656 | else | |||
657 | { | |||
658 | // Search the old TabCols for the current position (calculate from | |||
659 | // left and right edge). Adjust the box if the values differ from | |||
660 | // the new TabCols. If the adjusted edge has no neighbour we also | |||
661 | // adjust all superior boxes. | |||
662 | ||||
663 | const long nOldAct = rParm.rOld.GetRight() - | |||
664 | rParm.rOld.GetLeft(); // +1 why? | |||
665 | ||||
666 | // The value for the left edge of the box is calculated from the | |||
667 | // widths of the previous boxes plus the left edge. | |||
668 | long nLeft = rParm.rOld.GetLeft(); | |||
669 | const SwTableBox *pCur = pBox; | |||
670 | const SwTableLine *pLine = pBox->GetUpper(); | |||
671 | ||||
672 | while ( pLine ) | |||
673 | { | |||
674 | const SwTableBoxes &rBoxes = pLine->GetTabBoxes(); | |||
675 | for ( size_t i = 0; (i < rBoxes.size()) && (rBoxes[i] != pCur); ++i) | |||
676 | { | |||
677 | nLeft += lcl_MulDiv64<long>( | |||
678 | rBoxes[i]->GetFrameFormat()->GetFrameSize().GetWidth(), | |||
679 | nOldAct, rParm.nOldWish); | |||
680 | } | |||
681 | pCur = pLine->GetUpper(); | |||
682 | pLine = pCur ? pCur->GetUpper() : nullptr; | |||
683 | } | |||
684 | long nLeftDiff = 0; | |||
685 | long nRightDiff = 0; | |||
686 | if ( nLeft != rParm.rOld.GetLeft() ) // There are still boxes before this. | |||
687 | { | |||
688 | // Right edge is left edge plus width. | |||
689 | const long nWidth = lcl_MulDiv64<long>( | |||
690 | pBox->GetFrameFormat()->GetFrameSize().GetWidth(), | |||
691 | nOldAct, rParm.nOldWish); | |||
692 | const long nRight = nLeft + nWidth; | |||
693 | size_t nLeftPos = 0; | |||
694 | size_t nRightPos = 0; | |||
695 | bool bFoundLeftPos = false; | |||
696 | bool bFoundRightPos = false; | |||
697 | for ( size_t i = 0; i < rParm.rOld.Count(); ++i ) | |||
698 | { | |||
699 | if ( nLeft >= (rParm.rOld[i] - COLFUZZY20) && | |||
700 | nLeft <= (rParm.rOld[i] + COLFUZZY20) ) | |||
701 | { | |||
702 | nLeftPos = i; | |||
703 | bFoundLeftPos = true; | |||
704 | } | |||
705 | else if ( nRight >= (rParm.rOld[i] - COLFUZZY20) && | |||
706 | nRight <= (rParm.rOld[i] + COLFUZZY20) ) | |||
707 | { | |||
708 | nRightPos = i; | |||
709 | bFoundRightPos = true; | |||
710 | } | |||
711 | } | |||
712 | nLeftDiff = bFoundLeftPos ? | |||
713 | rParm.rOld[nLeftPos] - rParm.rNew[nLeftPos] : 0; | |||
714 | nRightDiff= bFoundRightPos ? | |||
715 | rParm.rNew[nRightPos] - rParm.rOld[nRightPos] : 0; | |||
716 | } | |||
717 | else // The first box. | |||
718 | { | |||
719 | nLeftDiff = rParm.rOld.GetLeft() - rParm.rNew.GetLeft(); | |||
720 | if ( rParm.rOld.Count() ) | |||
721 | { | |||
722 | // Calculate the difference to the edge touching the first box. | |||
723 | const long nWidth = lcl_MulDiv64<long>( | |||
724 | pBox->GetFrameFormat()->GetFrameSize().GetWidth(), | |||
725 | nOldAct, rParm.nOldWish); | |||
726 | const long nTmp = nWidth + rParm.rOld.GetLeft(); | |||
727 | for ( size_t i = 0; i < rParm.rOld.Count(); ++i ) | |||
728 | { | |||
729 | if ( nTmp >= (rParm.rOld[i] - COLFUZZY20) && | |||
730 | nTmp <= (rParm.rOld[i] + COLFUZZY20) ) | |||
731 | { | |||
732 | nRightDiff = rParm.rNew[i] - rParm.rOld[i]; | |||
733 | break; | |||
734 | } | |||
735 | } | |||
736 | } | |||
737 | } | |||
738 | ||||
739 | if( pBox->getRowSpan() == 1 ) | |||
740 | { | |||
741 | const sal_uInt16 nPos = pBox->GetUpper()->GetBoxPos( pBox ); | |||
742 | SwTableBoxes& rTableBoxes = pBox->GetUpper()->GetTabBoxes(); | |||
743 | if( nPos && rTableBoxes[ nPos - 1 ]->getRowSpan() != 1 ) | |||
744 | nLeftDiff = 0; | |||
745 | if( nPos + 1 < static_cast<sal_uInt16>(rTableBoxes.size()) && | |||
746 | rTableBoxes[ nPos + 1 ]->getRowSpan() != 1 ) | |||
747 | nRightDiff = 0; | |||
748 | } | |||
749 | else | |||
750 | nLeftDiff = nRightDiff = 0; | |||
751 | ||||
752 | if ( nLeftDiff || nRightDiff ) | |||
753 | { | |||
754 | // The difference is the actual difference amount. For stretched | |||
755 | // tables, it does not make sense to adjust the attributes of the | |||
756 | // boxes by this amount. The difference amount needs to be converted | |||
757 | // accordingly. | |||
758 | long nTmp = rParm.rNew.GetRight() - rParm.rNew.GetLeft(); // +1 why? | |||
759 | nLeftDiff *= rParm.nNewWish; | |||
760 | nLeftDiff /= nTmp; | |||
761 | nRightDiff *= rParm.nNewWish; | |||
762 | nRightDiff /= nTmp; | |||
763 | long nDiff = nLeftDiff + nRightDiff; | |||
764 | ||||
765 | // Adjust the box and all superiors by the difference amount. | |||
766 | while ( pBox ) | |||
767 | { | |||
768 | SwFormatFrameSize aFormatFrameSize( pBox->GetFrameFormat()->GetFrameSize() ); | |||
769 | aFormatFrameSize.SetWidth( aFormatFrameSize.GetWidth() + nDiff ); | |||
770 | if ( aFormatFrameSize.GetWidth() < 0 ) | |||
771 | aFormatFrameSize.SetWidth( -aFormatFrameSize.GetWidth() ); | |||
772 | rParm.aShareFormats.SetSize( *pBox, aFormatFrameSize ); | |||
773 | ||||
774 | // The outer cells of the last row are responsible to adjust a surrounding cell. | |||
775 | // Last line check: | |||
776 | if ( pBox->GetUpper()->GetUpper() && | |||
777 | pBox->GetUpper() != pBox->GetUpper()->GetUpper()->GetTabLines().back()) | |||
778 | { | |||
779 | pBox = nullptr; | |||
780 | } | |||
781 | else | |||
782 | { | |||
783 | // Middle cell check: | |||
784 | if ( pBox != pBox->GetUpper()->GetTabBoxes().front() ) | |||
785 | nDiff = nRightDiff; | |||
786 | ||||
787 | if ( pBox != pBox->GetUpper()->GetTabBoxes().back() ) | |||
788 | nDiff -= nRightDiff; | |||
789 | ||||
790 | pBox = nDiff ? pBox->GetUpper()->GetUpper() : nullptr; | |||
791 | } | |||
792 | } | |||
793 | } | |||
794 | } | |||
795 | } | |||
796 | ||||
797 | static void lcl_ProcessBoxPtr( SwTableBox *pBox, std::deque<SwTableBox*> &rBoxArr, | |||
798 | bool bBefore ) | |||
799 | { | |||
800 | if ( !pBox->GetTabLines().empty() ) | |||
801 | { | |||
802 | const SwTableLines &rLines = pBox->GetTabLines(); | |||
803 | for ( size_t i = 0; i < rLines.size(); ++i ) | |||
804 | { | |||
805 | const SwTableBoxes &rBoxes = rLines[i]->GetTabBoxes(); | |||
806 | for ( size_t j = 0; j < rBoxes.size(); ++j ) | |||
807 | ::lcl_ProcessBoxPtr( rBoxes[j], rBoxArr, bBefore ); | |||
808 | } | |||
809 | } | |||
810 | else if ( bBefore ) | |||
811 | rBoxArr.push_front( pBox ); | |||
812 | else | |||
813 | rBoxArr.push_back( pBox ); | |||
814 | } | |||
815 | ||||
816 | static void lcl_AdjustBox( SwTableBox *pBox, const long nDiff, Parm &rParm ); | |||
817 | ||||
818 | static void lcl_AdjustLines( SwTableLines &rLines, const long nDiff, Parm &rParm ) | |||
819 | { | |||
820 | for ( size_t i = 0; i < rLines.size(); ++i ) | |||
821 | { | |||
822 | SwTableBox *pBox = rLines[i]->GetTabBoxes() | |||
823 | [rLines[i]->GetTabBoxes().size()-1]; | |||
824 | lcl_AdjustBox( pBox, nDiff, rParm ); | |||
825 | } | |||
826 | } | |||
827 | ||||
828 | static void lcl_AdjustBox( SwTableBox *pBox, const long nDiff, Parm &rParm ) | |||
829 | { | |||
830 | if ( !pBox->GetTabLines().empty() ) | |||
831 | ::lcl_AdjustLines( pBox->GetTabLines(), nDiff, rParm ); | |||
832 | ||||
833 | // Adjust the size of the box. | |||
834 | SwFormatFrameSize aFormatFrameSize( pBox->GetFrameFormat()->GetFrameSize() ); | |||
835 | aFormatFrameSize.SetWidth( aFormatFrameSize.GetWidth() + nDiff ); | |||
836 | ||||
837 | rParm.aShareFormats.SetSize( *pBox, aFormatFrameSize ); | |||
838 | } | |||
839 | ||||
840 | void SwTable::SetTabCols( const SwTabCols &rNew, const SwTabCols &rOld, | |||
841 | const SwTableBox *pStart, bool bCurRowOnly ) | |||
842 | { | |||
843 | CHECK_TABLE( *this ) | |||
844 | ||||
845 | SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // delete HTML-Layout | |||
846 | ||||
847 | // FME: Made rOld const. The caller is responsible for passing correct | |||
848 | // values of rOld. Therefore we do not have to call GetTabCols anymore: | |||
849 | //GetTabCols( rOld, pStart ); | |||
850 | ||||
851 | Parm aParm( rNew, rOld ); | |||
852 | ||||
853 | OSL_ENSURE( rOld.Count() == rNew.Count(), "Number of columns changed.")do { if (true && (!(rOld.Count() == rNew.Count()))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl" ), ("/home/maarten/src/libreoffice/core/sw/source/core/table/swtable.cxx" ":" "853" ": "), "%s", "Number of columns changed."); } } while (false); | |||
854 | ||||
855 | // Convert the edges. We need to adjust the size of the table and some boxes. | |||
856 | // For the size adjustment, we must not make use of the Modify, since that'd | |||
857 | // adjust all boxes, which we really don't want. | |||
858 | SwFrameFormat *pFormat = GetFrameFormat(); | |||
859 | aParm.nOldWish = aParm.nNewWish = pFormat->GetFrameSize().GetWidth(); | |||
860 | if ( (rOld.GetLeft() != rNew.GetLeft()) || | |||
861 | (rOld.GetRight()!= rNew.GetRight()) ) | |||
862 | { | |||
863 | LockModify(); | |||
864 | { | |||
865 | SvxLRSpaceItem aLR( pFormat->GetLRSpace() ); | |||
866 | SvxShadowItem aSh( pFormat->GetShadow() ); | |||
867 | ||||
868 | SwTwips nShRight = aSh.CalcShadowSpace( SvxShadowItemSide::RIGHT ); | |||
869 | SwTwips nShLeft = aSh.CalcShadowSpace( SvxShadowItemSide::LEFT ); | |||
870 | ||||
871 | aLR.SetLeft ( rNew.GetLeft() - nShLeft ); | |||
872 | aLR.SetRight( rNew.GetRightMax() - rNew.GetRight() - nShRight ); | |||
873 | pFormat->SetFormatAttr( aLR ); | |||
874 | ||||
875 | // The alignment of the table needs to be adjusted accordingly. | |||
876 | // This is done by preserving the exact positions that have been | |||
877 | // set by the user. | |||
878 | SwFormatHoriOrient aOri( pFormat->GetHoriOrient() ); | |||
879 | if( text::HoriOrientation::NONE != aOri.GetHoriOrient() && | |||
880 | text::HoriOrientation::CENTER != aOri.GetHoriOrient() ) | |||
881 | { | |||
882 | const bool bLeftDist = rNew.GetLeft() != nShLeft; | |||
883 | const bool bRightDist = rNew.GetRight() + nShRight != rNew.GetRightMax(); | |||
884 | if(!bLeftDist && !bRightDist) | |||
885 | aOri.SetHoriOrient( text::HoriOrientation::FULL ); | |||
886 | else if(!bRightDist && rNew.GetLeft() > nShLeft ) | |||
887 | aOri.SetHoriOrient( text::HoriOrientation::RIGHT ); | |||
888 | else if(!bLeftDist && rNew.GetRight() + nShRight < rNew.GetRightMax()) | |||
889 | aOri.SetHoriOrient( text::HoriOrientation::LEFT ); | |||
890 | else | |||
891 | aOri.SetHoriOrient( text::HoriOrientation::LEFT_AND_WIDTH ); | |||
892 | } | |||
893 | pFormat->SetFormatAttr( aOri ); | |||
894 | } | |||
895 | const long nAct = rOld.GetRight() - rOld.GetLeft(); // +1 why? | |||
896 | long nTabDiff = 0; | |||
897 | ||||
898 | if ( rOld.GetLeft() != rNew.GetLeft() ) | |||
899 | { | |||
900 | nTabDiff = rOld.GetLeft() - rNew.GetLeft(); | |||
901 | nTabDiff *= aParm.nOldWish; | |||
902 | nTabDiff /= nAct; | |||
903 | } | |||
904 | if ( rOld.GetRight() != rNew.GetRight() ) | |||
905 | { | |||
906 | long nDiff = rNew.GetRight() - rOld.GetRight(); | |||
907 | nDiff *= aParm.nOldWish; | |||
908 | nDiff /= nAct; | |||
909 | nTabDiff += nDiff; | |||
910 | if( !IsNewModel() ) | |||
911 | ::lcl_AdjustLines( GetTabLines(), nDiff, aParm ); | |||
912 | } | |||
913 | ||||
914 | // Adjust the size of the table, watch out for stretched tables. | |||
915 | if ( nTabDiff ) | |||
916 | { | |||
917 | aParm.nNewWish += nTabDiff; | |||
918 | if ( aParm.nNewWish < 0 ) | |||
919 | aParm.nNewWish = USHRT_MAX(32767 *2 +1); // Oops! Have to roll back. | |||
920 | SwFormatFrameSize aSz( pFormat->GetFrameSize() ); | |||
921 | if ( aSz.GetWidth() != aParm.nNewWish ) | |||
922 | { | |||
923 | aSz.SetWidth( aParm.nNewWish ); | |||
924 | aSz.SetWidthPercent( 0 ); | |||
925 | pFormat->SetFormatAttr( aSz ); | |||
926 | } | |||
927 | } | |||
928 | UnlockModify(); | |||
929 | } | |||
930 | ||||
931 | if( IsNewModel() ) | |||
932 | NewSetTabCols( aParm, rNew, rOld, pStart, bCurRowOnly ); | |||
933 | else | |||
934 | { | |||
935 | if ( bCurRowOnly ) | |||
936 | { | |||
937 | // To adjust the current row, we need to process all its boxes, | |||
938 | // similar to the filling of the TabCols (see GetTabCols()). | |||
939 | // Unfortunately we again have to take care to adjust the boxes | |||
940 | // from back to front, respectively from outer to inner. | |||
941 | // The best way to achieve this is probably to track the boxes | |||
942 | // in a PtrArray. | |||
943 | const SwTableBoxes &rBoxes = pStart->GetUpper()->GetTabBoxes(); | |||
944 | for ( size_t i = 0; i < rBoxes.size(); ++i ) | |||
945 | ::lcl_ProcessBoxPtr( rBoxes[i], aParm.aBoxArr, false ); | |||
946 | ||||
947 | const SwTableLine *pLine = pStart->GetUpper()->GetUpper() ? | |||
948 | pStart->GetUpper()->GetUpper()->GetUpper() : nullptr; | |||
949 | const SwTableBox *pExcl = pStart->GetUpper()->GetUpper(); | |||
950 | while ( pLine ) | |||
951 | { | |||
952 | const SwTableBoxes &rBoxes2 = pLine->GetTabBoxes(); | |||
953 | bool bBefore = true; | |||
954 | for ( size_t i = 0; i < rBoxes2.size(); ++i ) | |||
955 | { | |||
956 | if ( rBoxes2[i] != pExcl ) | |||
957 | ::lcl_ProcessBoxPtr( rBoxes2[i], aParm.aBoxArr, bBefore ); | |||
958 | else | |||
959 | bBefore = false; | |||
960 | } | |||
961 | pExcl = pLine->GetUpper(); | |||
962 | pLine = pLine->GetUpper() ? pLine->GetUpper()->GetUpper() : nullptr; | |||
963 | } | |||
964 | // After we've inserted a bunch of boxes (hopefully all and in | |||
965 | // correct order), we just need to process them in reverse order. | |||
966 | for ( int j = aParm.aBoxArr.size()-1; j >= 0; --j ) | |||
967 | { | |||
968 | SwTableBox *pBox = aParm.aBoxArr[j]; | |||
969 | ::lcl_ProcessBoxSet( pBox, aParm ); | |||
970 | } | |||
971 | } | |||
972 | else | |||
973 | { | |||
974 | // Adjusting the entire table is 'easy'. All boxes without lines are | |||
975 | // adjusted, as are their superiors. Of course we need to process | |||
976 | // in reverse order to prevent fooling ourselves! | |||
977 | SwTableLines &rLines = GetTabLines(); | |||
978 | for ( size_t i = rLines.size(); i > 0; ) | |||
979 | { | |||
980 | --i; | |||
981 | ::lcl_ProcessLine( rLines[i], aParm ); | |||
982 | } | |||
983 | } | |||
984 | } | |||
985 | ||||
986 | #ifdef DBG_UTIL | |||
987 | { | |||
988 | // do some checking for correct table widths | |||
989 | SwTwips nSize = GetFrameFormat()->GetFrameSize().GetWidth(); | |||
990 | for (size_t n = 0; n < m_aLines.size(); ++n) | |||
991 | { | |||
992 | CheckBoxWidth( *m_aLines[ n ], nSize ); | |||
993 | } | |||
994 | } | |||
995 | #endif | |||
996 | } | |||
997 | ||||
998 | typedef std::pair<sal_uInt16, sal_uInt16> ColChange; | |||
999 | typedef std::list< ColChange > ChangeList; | |||
1000 | ||||
1001 | static void lcl_AdjustWidthsInLine( SwTableLine* pLine, ChangeList& rOldNew, | |||
1002 | Parm& rParm, sal_uInt16 nColFuzzy ) | |||
1003 | { | |||
1004 | ChangeList::iterator pCurr = rOldNew.begin(); | |||
1005 | if( pCurr == rOldNew.end() ) | |||
1006 | return; | |||
1007 | const size_t nCount = pLine->GetTabBoxes().size(); | |||
1008 | SwTwips nBorder = 0; | |||
1009 | SwTwips nRest = 0; | |||
1010 | for( size_t i = 0; i < nCount; ++i ) | |||
1011 | { | |||
1012 | SwTableBox* pBox = pLine->GetTabBoxes()[i]; | |||
1013 | SwTwips nWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth(); | |||
1014 | SwTwips nNewWidth = nWidth - nRest; | |||
1015 | nRest = 0; | |||
1016 | nBorder += nWidth; | |||
1017 | if( pCurr != rOldNew.end() && nBorder + nColFuzzy >= pCurr->first ) | |||
1018 | { | |||
1019 | nBorder -= nColFuzzy; | |||
1020 | while( pCurr != rOldNew.end() && nBorder > pCurr->first ) | |||
1021 | ++pCurr; | |||
1022 | if( pCurr != rOldNew.end() ) | |||
1023 | { | |||
1024 | nBorder += nColFuzzy; | |||
1025 | if( nBorder + nColFuzzy >= pCurr->first ) | |||
1026 | { | |||
1027 | if( pCurr->second == pCurr->first ) | |||
1028 | nRest = 0; | |||
1029 | else | |||
1030 | nRest = pCurr->second - nBorder; | |||
1031 | nNewWidth += nRest; | |||
1032 | ++pCurr; | |||
1033 | } | |||
1034 | } | |||
1035 | } | |||
1036 | if( nNewWidth != nWidth ) | |||
1037 | { | |||
1038 | if( nNewWidth < 0 ) | |||
1039 | { | |||
1040 | nRest += 1 - nNewWidth; | |||
1041 | nNewWidth = 1; | |||
1042 | } | |||
1043 | SwFormatFrameSize aFormatFrameSize( pBox->GetFrameFormat()->GetFrameSize() ); | |||
1044 | aFormatFrameSize.SetWidth( nNewWidth ); | |||
1045 | rParm.aShareFormats.SetSize( *pBox, aFormatFrameSize ); | |||
1046 | } | |||
1047 | } | |||
1048 | } | |||
1049 | ||||
1050 | static void lcl_CalcNewWidths( std::vector<sal_uInt16> &rSpanPos, ChangeList& rChanges, | |||
1051 | SwTableLine* pLine, long nWish, long nWidth, bool bTop ) | |||
1052 | { | |||
1053 | if( rChanges.empty() ) | |||
1054 | { | |||
1055 | rSpanPos.clear(); | |||
1056 | return; | |||
1057 | } | |||
1058 | if( rSpanPos.empty() ) | |||
1059 | { | |||
1060 | rChanges.clear(); | |||
1061 | return; | |||
1062 | } | |||
1063 | std::vector<sal_uInt16> aNewSpanPos; | |||
1064 | ChangeList aNewChanges; | |||
1065 | ChangeList::iterator pCurr = rChanges.begin(); | |||
1066 | aNewChanges.push_back( *pCurr ); // Nullposition | |||
1067 | std::vector<sal_uInt16>::iterator pSpan = rSpanPos.begin(); | |||
1068 | sal_uInt16 nCurr = 0; | |||
1069 | SwTwips nOrgSum = 0; | |||
1070 | bool bRowSpan = false; | |||
1071 | sal_uInt16 nRowSpanCount = 0; | |||
1072 | const size_t nCount = pLine->GetTabBoxes().size(); | |||
1073 | for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox ) | |||
1074 | { | |||
1075 | SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox]; | |||
1076 | SwTwips nCurrWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth(); | |||
1077 | const long nRowSpan = pBox->getRowSpan(); | |||
1078 | const bool bCurrRowSpan = bTop ? nRowSpan < 0 : | |||
1079 | ( nRowSpan > 1 || nRowSpan < -1 ); | |||
1080 | if( bRowSpan || bCurrRowSpan ) | |||
1081 | aNewSpanPos.push_back( nRowSpanCount ); | |||
1082 | bRowSpan = bCurrRowSpan; | |||
1083 | nOrgSum += nCurrWidth; | |||
1084 | const sal_uInt16 nPos = lcl_MulDiv64<sal_uInt16>( | |||
1085 | lcl_MulDiv64<sal_uInt64>(nOrgSum, nWidth, nWish), | |||
1086 | nWish, nWidth); | |||
1087 | while( pCurr != rChanges.end() && pCurr->first < nPos ) | |||
1088 | { | |||
1089 | ++nCurr; | |||
1090 | ++pCurr; | |||
1091 | } | |||
1092 | bool bNew = true; | |||
1093 | if( pCurr != rChanges.end() && pCurr->first <= nPos && | |||
1094 | pCurr->first != pCurr->second ) | |||
1095 | { | |||
1096 | pSpan = std::find_if(pSpan, rSpanPos.end(), | |||
1097 | [nCurr](const sal_uInt16 nSpan) { return nSpan >= nCurr; }); | |||
1098 | if( pSpan != rSpanPos.end() && *pSpan == nCurr ) | |||
1099 | { | |||
1100 | aNewChanges.push_back( *pCurr ); | |||
1101 | ++nRowSpanCount; | |||
1102 | bNew = false; | |||
1103 | } | |||
1104 | } | |||
1105 | if( bNew ) | |||
1106 | { | |||
1107 | ColChange aTmp( nPos, nPos ); | |||
1108 | aNewChanges.push_back( aTmp ); | |||
1109 | ++nRowSpanCount; | |||
1110 | } | |||
1111 | } | |||
1112 | ||||
1113 | pCurr = aNewChanges.begin(); | |||
1114 | ChangeList::iterator pLast = pCurr; | |||
1115 | ChangeList::iterator pLeftMove = pCurr; | |||
1116 | while( pCurr != aNewChanges.end() ) | |||
1117 | { | |||
1118 | if( pLeftMove == pCurr ) | |||
1119 | { | |||
1120 | while( ++pLeftMove != aNewChanges.end() && pLeftMove->first <= pLeftMove->second ) | |||
1121 | ; | |||
1122 | } | |||
1123 | if( pCurr->second == pCurr->first ) | |||
1124 | { | |||
1125 | if( pLeftMove != aNewChanges.end() && pCurr->second > pLeftMove->second ) | |||
1126 | { | |||
1127 | if( pLeftMove->first == pLast->first ) | |||
1128 | pCurr->second = pLeftMove->second; | |||
1129 | else | |||
1130 | { | |||
1131 | pCurr->second = lcl_MulDiv64<sal_uInt16>( | |||
1132 | pCurr->first - pLast->first, | |||
1133 | pLeftMove->second - pLast->second, | |||
1134 | pLeftMove->first - pLast->first) + pLast->second; | |||
1135 | } | |||
1136 | } | |||
1137 | pLast = pCurr; | |||
1138 | ++pCurr; | |||
1139 | } | |||
1140 | else if( pCurr->second > pCurr->first ) | |||
1141 | { | |||
1142 | pLast = pCurr; | |||
1143 | ++pCurr; | |||
1144 | ChangeList::iterator pNext = pCurr; | |||
1145 | while( pNext != pLeftMove && pNext->second == pNext->first && | |||
1146 | pNext->second < pLast->second ) | |||
1147 | ++pNext; | |||
1148 | while( pCurr != pNext ) | |||
1149 | { | |||
1150 | if( pNext == aNewChanges.end() || pNext->first == pLast->first ) | |||
1151 | pCurr->second = pLast->second; | |||
1152 | else | |||
1153 | { | |||
1154 | pCurr->second = lcl_MulDiv64<sal_uInt16>( | |||
1155 | pCurr->first - pLast->first, | |||
1156 | pNext->second - pLast->second, | |||
1157 | pNext->first - pLast->first) + pLast->second; | |||
1158 | } | |||
1159 | ++pCurr; | |||
1160 | } | |||
1161 | pLast = pCurr; | |||
1162 | } | |||
1163 | else | |||
1164 | { | |||
1165 | pLast = pCurr; | |||
1166 | ++pCurr; | |||
1167 | } | |||
1168 | } | |||
1169 | ||||
1170 | rChanges.swap(aNewChanges); | |||
1171 | rSpanPos.swap(aNewSpanPos); | |||
1172 | } | |||
1173 | ||||
1174 | void SwTable::NewSetTabCols( Parm &rParm, const SwTabCols &rNew, | |||
1175 | const SwTabCols &rOld, const SwTableBox *pStart, bool bCurRowOnly ) | |||
1176 | { | |||
1177 | #if OSL_DEBUG_LEVEL1 > 1 | |||
1178 | static int nCallCount = 0; | |||
1179 | ++nCallCount; | |||
1180 | #endif | |||
1181 | // First step: evaluate which lines have been moved/which widths changed | |||
1182 | ChangeList aOldNew; | |||
1183 | const long nNewWidth = rParm.rNew.GetRight() - rParm.rNew.GetLeft(); | |||
1184 | const long nOldWidth = rParm.rOld.GetRight() - rParm.rOld.GetLeft(); | |||
1185 | if( nNewWidth < 1 || nOldWidth < 1 ) | |||
1186 | return; | |||
1187 | for( size_t i = 0; i <= rOld.Count(); ++i ) | |||
1188 | { | |||
1189 | long nNewPos; | |||
1190 | long nOldPos; | |||
1191 | if( i == rOld.Count() ) | |||
1192 | { | |||
1193 | nOldPos = rParm.rOld.GetRight() - rParm.rOld.GetLeft(); | |||
1194 | nNewPos = rParm.rNew.GetRight() - rParm.rNew.GetLeft(); | |||
1195 | } | |||
1196 | else | |||
1197 | { | |||
1198 | nOldPos = rOld[i] - rParm.rOld.GetLeft(); | |||
1199 | nNewPos = rNew[i] - rParm.rNew.GetLeft(); | |||
1200 | } | |||
1201 | nNewPos = lcl_MulDiv64<long>(nNewPos, rParm.nNewWish, nNewWidth); | |||
1202 | nOldPos = lcl_MulDiv64<long>(nOldPos, rParm.nOldWish, nOldWidth); | |||
1203 | if( nOldPos != nNewPos && nNewPos > 0 && nOldPos > 0 ) | |||
1204 | { | |||
1205 | ColChange aChg( static_cast<sal_uInt16>(nOldPos), static_cast<sal_uInt16>(nNewPos) ); | |||
1206 | aOldNew.push_back( aChg ); | |||
1207 | } | |||
1208 | } | |||
1209 | // Finished first step | |||
1210 | int nCount = aOldNew.size(); | |||
1211 | if( !nCount ) | |||
1212 | return; // no change, nothing to do | |||
1213 | SwTableLines &rLines = GetTabLines(); | |||
1214 | if( bCurRowOnly ) | |||
1215 | { | |||
1216 | const SwTableLine* pCurrLine = pStart->GetUpper(); | |||
1217 | sal_uInt16 nCurr = rLines.GetPos( pCurrLine ); | |||
1218 | if( nCurr >= USHRT_MAX(32767 *2 +1) ) | |||
1219 | return; | |||
1220 | ||||
1221 | ColChange aChg( 0, 0 ); | |||
1222 | aOldNew.push_front( aChg ); | |||
1223 | std::vector<sal_uInt16> aRowSpanPos; | |||
1224 | if( nCurr ) | |||
1225 | { | |||
1226 | ChangeList aCopy; | |||
1227 | sal_uInt16 nPos = 0; | |||
1228 | for( const auto& rCop : aOldNew ) | |||
1229 | { | |||
1230 | aCopy.push_back( rCop ); | |||
1231 | aRowSpanPos.push_back( nPos++ ); | |||
1232 | } | |||
1233 | lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[nCurr], | |||
1234 | rParm.nOldWish, nOldWidth, true ); | |||
1235 | bool bGoOn = !aRowSpanPos.empty(); | |||
1236 | sal_uInt16 j = nCurr; | |||
1237 | while( bGoOn ) | |||
1238 | { | |||
1239 | lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[--j], | |||
1240 | rParm.nOldWish, nOldWidth, true ); | |||
1241 | lcl_AdjustWidthsInLine( rLines[j], aCopy, rParm, 0 ); | |||
1242 | bGoOn = !aRowSpanPos.empty() && j > 0; | |||
1243 | } | |||
1244 | aRowSpanPos.clear(); | |||
1245 | } | |||
1246 | if( nCurr+1 < static_cast<sal_uInt16>(rLines.size()) ) | |||
1247 | { | |||
1248 | ChangeList aCopy; | |||
1249 | sal_uInt16 nPos = 0; | |||
1250 | for( const auto& rCop : aOldNew ) | |||
1251 | { | |||
1252 | aCopy.push_back( rCop ); | |||
1253 | aRowSpanPos.push_back( nPos++ ); | |||
1254 | } | |||
1255 | lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[nCurr], | |||
1256 | rParm.nOldWish, nOldWidth, false ); | |||
1257 | bool bGoOn = !aRowSpanPos.empty(); | |||
1258 | sal_uInt16 j = nCurr; | |||
1259 | while( bGoOn ) | |||
1260 | { | |||
1261 | lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[++j], | |||
1262 | rParm.nOldWish, nOldWidth, false ); | |||
1263 | lcl_AdjustWidthsInLine( rLines[j], aCopy, rParm, 0 ); | |||
1264 | bGoOn = !aRowSpanPos.empty() && j+1 < static_cast<sal_uInt16>(rLines.size()); | |||
1265 | } | |||
1266 | } | |||
1267 | ::lcl_AdjustWidthsInLine( rLines[nCurr], aOldNew, rParm, COLFUZZY20 ); | |||
1268 | } | |||
1269 | else | |||
1270 | { | |||
1271 | for( size_t i = 0; i < rLines.size(); ++i ) | |||
1272 | ::lcl_AdjustWidthsInLine( rLines[i], aOldNew, rParm, COLFUZZY20 ); | |||
1273 | } | |||
1274 | CHECK_TABLE( *this ) | |||
1275 | } | |||
1276 | ||||
1277 | // return the pointer of the box specified. | |||
1278 | static bool lcl_IsValidRowName( const OUString& rStr ) | |||
1279 | { | |||
1280 | bool bIsValid = true; | |||
1281 | sal_Int32 nLen = rStr.getLength(); | |||
1282 | for( sal_Int32 i = 0; i < nLen && bIsValid; ++i ) | |||
1283 | { | |||
1284 | const sal_Unicode cChar = rStr[i]; | |||
1285 | if (cChar < '0' || cChar > '9') | |||
1286 | bIsValid = false; | |||
1287 | } | |||
1288 | return bIsValid; | |||
1289 | } | |||
1290 | ||||
1291 | // #i80314# | |||
1292 | // add 3rd parameter and its handling | |||
1293 | sal_uInt16 SwTable::GetBoxNum( OUString& rStr, bool bFirstPart, | |||
1294 | const bool bPerformValidCheck ) | |||
1295 | { | |||
1296 | sal_uInt16 nRet = 0; | |||
1297 | if( bFirstPart ) // true == column; false == row | |||
1298 | { | |||
1299 | sal_Int32 nPos = 0; | |||
1300 | // the first one uses letters for addressing! | |||
1301 | bool bFirst = true; | |||
1302 | sal_uInt32 num = 0; | |||
1303 | bool overflow = false; | |||
1304 | while (nPos<rStr.getLength()) | |||
1305 | { | |||
1306 | sal_Unicode cChar = rStr[nPos]; | |||
1307 | if ((cChar<'A' || cChar>'Z') && (cChar<'a' || cChar>'z')) | |||
1308 | break; | |||
1309 | cChar -= 'A'; | |||
1310 | if( cChar >= 26 ) | |||
1311 | cChar -= 'a' - '['; | |||
1312 | if( bFirst ) | |||
1313 | bFirst = false; | |||
1314 | else | |||
1315 | ++num; | |||
1316 | num = num * 52 + cChar; | |||
1317 | if (num > SAL_MAX_UINT16((sal_uInt16) 0xFFFF)) { | |||
1318 | overflow = true; | |||
1319 | } | |||
1320 | ++nPos; | |||
1321 | } | |||
1322 | nRet = overflow ? SAL_MAX_UINT16((sal_uInt16) 0xFFFF) : num; | |||
1323 | rStr = rStr.copy( nPos ); // Remove char from String | |||
1324 | } | |||
1325 | else | |||
1326 | { | |||
1327 | const sal_Int32 nPos = rStr.indexOf( "." ); | |||
1328 | if ( nPos<0 ) | |||
1329 | { | |||
1330 | nRet = 0; | |||
1331 | if ( !bPerformValidCheck || lcl_IsValidRowName( rStr ) ) | |||
1332 | { | |||
1333 | nRet = static_cast<sal_uInt16>(rStr.toInt32()); | |||
1334 | } | |||
1335 | rStr.clear(); | |||
1336 | } | |||
1337 | else | |||
1338 | { | |||
1339 | nRet = 0; | |||
1340 | const OUString aText( rStr.copy( 0, nPos ) ); | |||
1341 | if ( !bPerformValidCheck || lcl_IsValidRowName( aText ) ) | |||
1342 | { | |||
1343 | nRet = static_cast<sal_uInt16>(aText.toInt32()); | |||
1344 | } | |||
1345 | rStr = rStr.copy( nPos+1 ); | |||
1346 | } | |||
1347 | } | |||
1348 | return nRet; | |||
1349 | } | |||
1350 | ||||
1351 | // #i80314# | |||
1352 | // add 2nd parameter and its handling | |||
1353 | const SwTableBox* SwTable::GetTableBox( const OUString& rName, | |||
1354 | const bool bPerformValidCheck ) const | |||
1355 | { | |||
1356 | const SwTableBox* pBox = nullptr; | |||
1357 | const SwTableLine* pLine; | |||
1358 | const SwTableLines* pLines; | |||
1359 | ||||
1360 | sal_uInt16 nLine, nBox; | |||
1361 | OUString aNm( rName ); | |||
1362 | while( !aNm.isEmpty() ) | |||
1363 | { | |||
1364 | nBox = SwTable::GetBoxNum( aNm, nullptr == pBox, bPerformValidCheck ); | |||
1365 | // first box ? | |||
1366 | if( !pBox ) | |||
1367 | pLines = &GetTabLines(); | |||
1368 | else | |||
1369 | { | |||
1370 | pLines = &pBox->GetTabLines(); | |||
1371 | if( nBox ) | |||
1372 | --nBox; | |||
1373 | } | |||
1374 | ||||
1375 | nLine = SwTable::GetBoxNum( aNm, false, bPerformValidCheck ); | |||
1376 | ||||
1377 | // determine line | |||
1378 | if( !nLine || nLine > pLines->size() ) | |||
1379 | return nullptr; | |||
1380 | pLine = (*pLines)[ nLine-1 ]; | |||
1381 | ||||
1382 | // determine box | |||
1383 | const SwTableBoxes* pBoxes = &pLine->GetTabBoxes(); | |||
1384 | if( nBox >= pBoxes->size() ) | |||
1385 | return nullptr; | |||
1386 | pBox = (*pBoxes)[ nBox ]; | |||
1387 | } | |||
1388 | ||||
1389 | // check if the box found has any contents | |||
1390 | if( pBox && !pBox->GetSttNd() ) | |||
1391 | { | |||
1392 | OSL_FAIL( "Box without content, looking for the next one!" )do { if (true && (((sal_Bool)1))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sw/source/core/table/swtable.cxx" ":" "1392" ": "), "%s", "Box without content, looking for the next one!" ); } } while (false); | |||
1393 | // "drop this" until the first box | |||
1394 | while( !pBox->GetTabLines().empty() ) | |||
1395 | pBox = pBox->GetTabLines().front()->GetTabBoxes().front(); | |||
1396 | } | |||
1397 | return pBox; | |||
1398 | } | |||
1399 | ||||
1400 | SwTableBox* SwTable::GetTableBox( sal_uLong nSttIdx ) | |||
1401 | { | |||
1402 | // For optimizations, don't always process the entire SortArray. | |||
1403 | // Converting text to table, tries certain conditions | |||
1404 | // to ask for a table box of a table that is not yet having a format | |||
1405 | if(!GetFrameFormat()) | |||
1406 | return nullptr; | |||
1407 | SwTableBox* pRet = nullptr; | |||
1408 | SwNodes& rNds = GetFrameFormat()->GetDoc()->GetNodes(); | |||
1409 | sal_uLong nIndex = nSttIdx + 1; | |||
1410 | SwContentNode* pCNd = nullptr; | |||
1411 | SwTableNode* pTableNd = nullptr; | |||
1412 | ||||
1413 | while ( nIndex < rNds.Count() ) | |||
1414 | { | |||
1415 | pTableNd = rNds[ nIndex ]->GetTableNode(); | |||
1416 | if ( pTableNd ) | |||
1417 | break; | |||
1418 | ||||
1419 | pCNd = rNds[ nIndex ]->GetContentNode(); | |||
1420 | if ( pCNd ) | |||
1421 | break; | |||
1422 | ||||
1423 | ++nIndex; | |||
1424 | } | |||
1425 | ||||
1426 | if ( pCNd || pTableNd ) | |||
1427 | { | |||
1428 | SwModify* pModify = pCNd; | |||
1429 | // #144862# Better handling of table in table | |||
1430 | if ( pTableNd && pTableNd->GetTable().GetFrameFormat() ) | |||
1431 | pModify = pTableNd->GetTable().GetFrameFormat(); | |||
1432 | ||||
1433 | SwFrame* pFrame = pModify ? SwIterator<SwFrame,SwModify>(*pModify).First() : nullptr; | |||
1434 | while ( pFrame && !pFrame->IsCellFrame() ) | |||
1435 | pFrame = pFrame->GetUpper(); | |||
1436 | if ( pFrame ) | |||
1437 | pRet = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox()); | |||
1438 | } | |||
1439 | ||||
1440 | // In case the layout doesn't exist yet or anything else goes wrong. | |||
1441 | if ( !pRet ) | |||
1442 | { | |||
1443 | for (size_t n = m_TabSortContentBoxes.size(); n; ) | |||
1444 | { | |||
1445 | if (m_TabSortContentBoxes[ --n ]->GetSttIdx() == nSttIdx) | |||
1446 | { | |||
1447 | return m_TabSortContentBoxes[ n ]; | |||
1448 | } | |||
1449 | } | |||
1450 | } | |||
1451 | return pRet; | |||
1452 | } | |||
1453 | ||||
1454 | bool SwTable::IsTableComplex() const | |||
1455 | { | |||
1456 | // Returns true for complex tables, i.e. tables that contain nestings, | |||
1457 | // like containing boxes not part of the first line, e.g. results of | |||
1458 | // splits/merges which lead to more complex structures. | |||
1459 | for (size_t n = 0; n < m_TabSortContentBoxes.size(); ++n) | |||
1460 | { | |||
1461 | if (m_TabSortContentBoxes[ n ]->GetUpper()->GetUpper()) | |||
1462 | { | |||
1463 | return true; | |||
1464 | } | |||
1465 | } | |||
1466 | return false; | |||
1467 | } | |||
1468 | ||||
1469 | SwTableLine::SwTableLine( SwTableLineFormat *pFormat, sal_uInt16 nBoxes, | |||
1470 | SwTableBox *pUp ) | |||
1471 | : SwClient( pFormat ), | |||
1472 | m_aBoxes(), | |||
1473 | m_pUpper( pUp ) | |||
1474 | { | |||
1475 | m_aBoxes.reserve( nBoxes ); | |||
1476 | } | |||
1477 | ||||
1478 | SwTableLine::~SwTableLine() | |||
1479 | { | |||
1480 | for (size_t i = 0; i < m_aBoxes.size(); ++i) | |||
1481 | { | |||
1482 | delete m_aBoxes[i]; | |||
1483 | } | |||
1484 | // the TabelleLine can be deleted if it's the last client of the FrameFormat | |||
1485 | SwModify* pMod = GetFrameFormat(); | |||
1486 | pMod->Remove( this ); // remove, | |||
1487 | if( !pMod->HasWriterListeners() ) | |||
1488 | delete pMod; // and delete | |||
1489 | } | |||
1490 | ||||
1491 | SwFrameFormat* SwTableLine::ClaimFrameFormat() | |||
1492 | { | |||
1493 | // This method makes sure that this object is an exclusive SwTableLine client | |||
1494 | // of an SwTableLineFormat object | |||
1495 | // If other SwTableLine objects currently listen to the same SwTableLineFormat as | |||
1496 | // this one, something needs to be done | |||
1497 | SwTableLineFormat *pRet = static_cast<SwTableLineFormat*>(GetFrameFormat()); | |||
1498 | SwIterator<SwTableLine,SwFormat> aIter( *pRet ); | |||
1499 | for( SwTableLine* pLast = aIter.First(); pLast; pLast = aIter.Next() ) | |||
1500 | { | |||
1501 | if ( pLast != this ) | |||
1502 | { | |||
1503 | // found another SwTableLine that is a client of the current Format | |||
1504 | // create a new Format as a copy and use it for this object | |||
1505 | SwTableLineFormat *pNewFormat = pRet->GetDoc()->MakeTableLineFormat(); | |||
1506 | *pNewFormat = *pRet; | |||
1507 | ||||
1508 | // register SwRowFrames that know me as clients at the new Format | |||
1509 | SwIterator<SwRowFrame,SwFormat> aFrameIter( *pRet ); | |||
1510 | for( SwRowFrame* pFrame = aFrameIter.First(); pFrame; pFrame = aFrameIter.Next() ) | |||
1511 | if( pFrame->GetTabLine() == this ) | |||
1512 | pFrame->RegisterToFormat( *pNewFormat ); | |||
1513 | ||||
1514 | // register myself | |||
1515 | pNewFormat->Add( this ); | |||
1516 | pRet = pNewFormat; | |||
1517 | break; | |||
1518 | } | |||
1519 | } | |||
1520 | ||||
1521 | return pRet; | |||
1522 | } | |||
1523 | ||||
1524 | void SwTableLine::ChgFrameFormat( SwTableLineFormat *pNewFormat ) | |||
1525 | { | |||
1526 | SwFrameFormat *pOld = GetFrameFormat(); | |||
1527 | SwIterator<SwRowFrame,SwFormat> aIter( *pOld ); | |||
1528 | ||||
1529 | // First, re-register the Frames. | |||
1530 | for( SwRowFrame* pRow = aIter.First(); pRow; pRow = aIter.Next() ) | |||
1531 | { | |||
1532 | if( pRow->GetTabLine() == this ) | |||
1533 | { | |||
1534 | pRow->RegisterToFormat( *pNewFormat ); | |||
1535 | ||||
1536 | pRow->InvalidateSize(); | |||
1537 | pRow->InvalidatePrt_(); | |||
1538 | pRow->SetCompletePaint(); | |||
1539 | pRow->ReinitializeFrameSizeAttrFlags(); | |||
1540 | ||||
1541 | // #i35063# | |||
1542 | // consider 'split row allowed' attribute | |||
1543 | SwTabFrame* pTab = pRow->FindTabFrame(); | |||
1544 | bool bInFollowFlowRow = false; | |||
1545 | const bool bInFirstNonHeadlineRow = pTab->IsFollow() && | |||
1546 | pRow == pTab->GetFirstNonHeadlineRow(); | |||
1547 | if ( bInFirstNonHeadlineRow || | |||
1548 | !pRow->GetNext() || | |||
1549 | ( bInFollowFlowRow = pRow->IsInFollowFlowRow() ) || | |||
1550 | nullptr != pRow->IsInSplitTableRow() ) | |||
1551 | { | |||
1552 | if ( bInFirstNonHeadlineRow || bInFollowFlowRow ) | |||
1553 | pTab = pTab->FindMaster(); | |||
1554 | ||||
1555 | pTab->SetRemoveFollowFlowLinePending( true ); | |||
1556 | pTab->InvalidatePos(); | |||
1557 | } | |||
1558 | } | |||
1559 | } | |||
1560 | ||||
1561 | // Now, re-register self. | |||
1562 | pNewFormat->Add( this ); | |||
1563 | ||||
1564 | if ( !pOld->HasWriterListeners() ) | |||
1565 | delete pOld; | |||
1566 | } | |||
1567 | ||||
1568 | SwTwips SwTableLine::GetTableLineHeight( bool& bLayoutAvailable ) const | |||
1569 | { | |||
1570 | SwTwips nRet = 0; | |||
1571 | bLayoutAvailable = false; | |||
1572 | SwIterator<SwRowFrame,SwFormat> aIter( *GetFrameFormat() ); | |||
| ||||
1573 | // A row could appear several times in headers/footers so only one chain of master/follow tables | |||
1574 | // will be accepted... | |||
1575 | const SwTabFrame* pChain = nullptr; // My chain | |||
1576 | for( SwRowFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() ) | |||
1577 | { | |||
1578 | if( pLast->GetTabLine() == this ) | |||
1579 | { | |||
1580 | const SwTabFrame* pTab = pLast->FindTabFrame(); | |||
1581 | bLayoutAvailable = ( pTab && pTab->IsVertical() ) ? | |||
1582 | ( 0 < pTab->getFrameArea().Height() ) : | |||
1583 | ( 0 < pTab->getFrameArea().Width() ); | |||
| ||||
1584 | ||||
1585 | // The first one defines the chain, if a chain is defined, only members of the chain | |||
1586 | // will be added. | |||
1587 | if (pTab && (!pChain || pChain->IsAnFollow( pTab ) || pTab->IsAnFollow(pChain))) | |||
1588 | { | |||
1589 | pChain = pTab; // defines my chain (even it is already) | |||
1590 | if( pTab->IsVertical() ) | |||
1591 | nRet += pLast->getFrameArea().Width(); | |||
1592 | else | |||
1593 | nRet += pLast->getFrameArea().Height(); | |||
1594 | // Optimization, if there are no master/follows in my chain, nothing more to add | |||
1595 | if( !pTab->HasFollow() && !pTab->IsFollow() ) | |||
1596 | break; | |||
1597 | // This is not an optimization, this is necessary to avoid double additions of | |||
1598 | // repeating rows | |||
1599 | if( pTab->IsInHeadline(*pLast) ) | |||
1600 | break; | |||
1601 | } | |||
1602 | } | |||
1603 | } | |||
1604 | return nRet; | |||
1605 | } | |||
1606 | ||||
1607 | SwTableBox::SwTableBox( SwTableBoxFormat* pFormat, sal_uInt16 nLines, SwTableLine *pUp ) | |||
1608 | : SwClient(nullptr) | |||
1609 | , m_aLines() | |||
1610 | , m_pStartNode(nullptr) | |||
1611 | , m_pUpper(pUp) | |||
1612 | , mnRowSpan(1) | |||
1613 | , mbDummyFlag(false) | |||
1614 | , mbDirectFormatting(false) | |||
1615 | { | |||
1616 | m_aLines.reserve( nLines ); | |||
1617 | CheckBoxFormat( pFormat )->Add( this ); | |||
1618 | } | |||
1619 | ||||
1620 | SwTableBox::SwTableBox( SwTableBoxFormat* pFormat, const SwNodeIndex &rIdx, | |||
1621 | SwTableLine *pUp ) | |||
1622 | : SwClient(nullptr) | |||
1623 | , m_aLines() | |||
1624 | , m_pUpper(pUp) | |||
1625 | , mnRowSpan(1) | |||
1626 | , mbDummyFlag(false) | |||
1627 | , mbDirectFormatting(false) | |||
1628 | { | |||
1629 | CheckBoxFormat( pFormat )->Add( this ); | |||
1630 | ||||
1631 | m_pStartNode = rIdx.GetNode().GetStartNode(); | |||
1632 | ||||
1633 | // insert into the table | |||
1634 | const SwTableNode* pTableNd = m_pStartNode->FindTableNode(); | |||
1635 | assert(pTableNd && "In which table is that box?")(static_cast <bool> (pTableNd && "In which table is that box?" ) ? void (0) : __assert_fail ("pTableNd && \"In which table is that box?\"" , "/home/maarten/src/libreoffice/core/sw/source/core/table/swtable.cxx" , 1635, __extension__ __PRETTY_FUNCTION__)); | |||
1636 | SwTableSortBoxes& rSrtArr = const_cast<SwTableSortBoxes&>(pTableNd->GetTable(). | |||
1637 | GetTabSortBoxes()); | |||
1638 | SwTableBox* p = this; // error: &this | |||
1639 | rSrtArr.insert( p ); // insert | |||
1640 | } | |||
1641 | ||||
1642 | SwTableBox::SwTableBox( SwTableBoxFormat* pFormat, const SwStartNode& rSttNd, SwTableLine *pUp ) | |||
1643 | : SwClient(nullptr) | |||
1644 | , m_aLines() | |||
1645 | , m_pStartNode(&rSttNd) | |||
1646 | , m_pUpper(pUp) | |||
1647 | , mnRowSpan(1) | |||
1648 | , mbDummyFlag(false) | |||
1649 | , mbDirectFormatting(false) | |||
1650 | { | |||
1651 | CheckBoxFormat( pFormat )->Add( this ); | |||
1652 | ||||
1653 | // insert into the table | |||
1654 | const SwTableNode* pTableNd = m_pStartNode->FindTableNode(); | |||
1655 | OSL_ENSURE( pTableNd, "In which table is the box?" )do { if (true && (!(pTableNd))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sw/source/core/table/swtable.cxx" ":" "1655" ": "), "%s", "In which table is the box?"); } } while (false); | |||
1656 | SwTableSortBoxes& rSrtArr = const_cast<SwTableSortBoxes&>(pTableNd->GetTable(). | |||
1657 | GetTabSortBoxes()); | |||
1658 | SwTableBox* p = this; // error: &this | |||
1659 | rSrtArr.insert( p ); // insert | |||
1660 | } | |||
1661 | ||||
1662 | void SwTableBox::RemoveFromTable() | |||
1663 | { | |||
1664 | if (m_pStartNode) // box containing contents? | |||
1665 | { | |||
1666 | // remove from table | |||
1667 | const SwTableNode* pTableNd = m_pStartNode->FindTableNode(); | |||
1668 | assert(pTableNd && "In which table is that box?")(static_cast <bool> (pTableNd && "In which table is that box?" ) ? void (0) : __assert_fail ("pTableNd && \"In which table is that box?\"" , "/home/maarten/src/libreoffice/core/sw/source/core/table/swtable.cxx" , 1668, __extension__ __PRETTY_FUNCTION__)); | |||
1669 | SwTableSortBoxes& rSrtArr = const_cast<SwTableSortBoxes&>(pTableNd->GetTable(). | |||
1670 | GetTabSortBoxes()); | |||
1671 | SwTableBox *p = this; // error: &this | |||
1672 | rSrtArr.erase( p ); // remove | |||
1673 | m_pStartNode = nullptr; // clear it so this is only run once | |||
1674 | } | |||
1675 | } | |||
1676 | ||||
1677 | SwTableBox::~SwTableBox() | |||
1678 | { | |||
1679 | if (!GetFrameFormat()->GetDoc()->IsInDtor()) | |||
1680 | { | |||
1681 | RemoveFromTable(); | |||
1682 | } | |||
1683 | ||||
1684 | // the TabelleBox can be deleted if it's the last client of the FrameFormat | |||
1685 | SwModify* pMod = GetFrameFormat(); | |||
1686 | pMod->Remove( this ); // remove, | |||
1687 | if( !pMod->HasWriterListeners() ) | |||
1688 | delete pMod; // and delete | |||
1689 | } | |||
1690 | ||||
1691 | SwTableBoxFormat* SwTableBox::CheckBoxFormat( SwTableBoxFormat* pFormat ) | |||
1692 | { | |||
1693 | // We might need to create a new format here, because the box must be | |||
1694 | // added to the format solely if pFormat has a value or form. | |||
1695 | if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_VALUE, false ) || | |||
1696 | SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_FORMULA, false ) ) | |||
1697 | { | |||
1698 | SwTableBox* pOther = SwIterator<SwTableBox,SwFormat>( *pFormat ).First(); | |||
1699 | if( pOther ) | |||
1700 | { | |||
1701 | SwTableBoxFormat* pNewFormat = pFormat->GetDoc()->MakeTableBoxFormat(); | |||
1702 | pNewFormat->LockModify(); | |||
1703 | *pNewFormat = *pFormat; | |||
1704 | ||||
1705 | // Remove values and formulas | |||
1706 | pNewFormat->ResetFormatAttr( RES_BOXATR_FORMULA, RES_BOXATR_VALUE ); | |||
1707 | pNewFormat->UnlockModify(); | |||
1708 | ||||
1709 | pFormat = pNewFormat; | |||
1710 | } | |||
1711 | } | |||
1712 | return pFormat; | |||
1713 | } | |||
1714 | ||||
1715 | SwFrameFormat* SwTableBox::ClaimFrameFormat() | |||
1716 | { | |||
1717 | // This method makes sure that this object is an exclusive SwTableBox client | |||
1718 | // of an SwTableBoxFormat object | |||
1719 | // If other SwTableBox objects currently listen to the same SwTableBoxFormat as | |||
1720 | // this one, something needs to be done | |||
1721 | SwTableBoxFormat *pRet = static_cast<SwTableBoxFormat*>(GetFrameFormat()); | |||
1722 | SwIterator<SwTableBox,SwFormat> aIter( *pRet ); | |||
1723 | for( SwTableBox* pLast = aIter.First(); pLast; pLast = aIter.Next() ) | |||
1724 | { | |||
1725 | if ( pLast != this ) | |||
1726 | { | |||
1727 | // Found another SwTableBox object | |||
1728 | // create a new Format as a copy and assign me to it | |||
1729 | // don't copy values and formulas | |||
1730 | SwTableBoxFormat* pNewFormat = pRet->GetDoc()->MakeTableBoxFormat(); | |||
1731 | pNewFormat->LockModify(); | |||
1732 | *pNewFormat = *pRet; | |||
1733 | pNewFormat->ResetFormatAttr( RES_BOXATR_FORMULA, RES_BOXATR_VALUE ); | |||
1734 | pNewFormat->UnlockModify(); | |||
1735 | ||||
1736 | // re-register SwCellFrame objects that know me | |||
1737 | SwIterator<SwCellFrame,SwFormat> aFrameIter( *pRet ); | |||
1738 | for( SwCellFrame* pCell = aFrameIter.First(); pCell; pCell = aFrameIter.Next() ) | |||
1739 | if( pCell->GetTabBox() == this ) | |||
1740 | pCell->RegisterToFormat( *pNewFormat ); | |||
1741 | ||||
1742 | // re-register myself | |||
1743 | pNewFormat->Add( this ); | |||
1744 | pRet = pNewFormat; | |||
1745 | break; | |||
1746 | } | |||
1747 | } | |||
1748 | return pRet; | |||
1749 | } | |||
1750 | ||||
1751 | void SwTableBox::ChgFrameFormat( SwTableBoxFormat* pNewFormat, bool bNeedToReregister ) | |||
1752 | { | |||
1753 | SwFrameFormat *pOld = GetFrameFormat(); | |||
1754 | SwIterator<SwCellFrame,SwFormat> aIter( *pOld ); | |||
1755 | ||||
1756 | // tdf#84635 We set bNeedToReregister=false to avoid a quadratic slowdown on loading large tables, | |||
1757 | // and since we are creating the table for the first time, no re-registration is necessary. | |||
1758 | ||||
1759 | // First, re-register the Frames. | |||
1760 | if (bNeedToReregister) | |||
1761 | for( SwCellFrame* pCell = aIter.First(); pCell; pCell = aIter.Next() ) | |||
1762 | { | |||
1763 | if( pCell->GetTabBox() == this ) | |||
1764 | { | |||
1765 | pCell->RegisterToFormat( *pNewFormat ); | |||
1766 | pCell->InvalidateSize(); | |||
1767 | pCell->InvalidatePrt_(); | |||
1768 | pCell->SetCompletePaint(); | |||
1769 | pCell->SetDerivedVert( false ); | |||
1770 | pCell->CheckDirChange(); | |||
1771 | ||||
1772 | // #i47489# | |||
1773 | // make sure that the row will be formatted, in order | |||
1774 | // to have the correct Get(Top|Bottom)MarginForLowers values | |||
1775 | // set at the row. | |||
1776 | const SwTabFrame* pTab = pCell->FindTabFrame(); | |||
1777 | if ( pTab && pTab->IsCollapsingBorders() ) | |||
1778 | { | |||
1779 | SwFrame* pRow = pCell->GetUpper(); | |||
1780 | pRow->InvalidateSize_(); | |||
1781 | pRow->InvalidatePrt_(); | |||
1782 | } | |||
1783 | } | |||
1784 | } | |||
1785 | ||||
1786 | // Now, re-register self. | |||
1787 | pNewFormat->Add( this ); | |||
1788 | ||||
1789 | if( !pOld->HasWriterListeners() ) | |||
1790 | delete pOld; | |||
1791 | } | |||
1792 | ||||
1793 | // Return the name of this box. This is determined dynamically | |||
1794 | // resulting from the position in the lines/boxes/tables. | |||
1795 | void sw_GetTableBoxColStr( sal_uInt16 nCol, OUString& rNm ) | |||
1796 | { | |||
1797 | const sal_uInt16 coDiff = 52; // 'A'-'Z' 'a' - 'z' | |||
1798 | ||||
1799 | do { | |||
1800 | const sal_uInt16 nCalc = nCol % coDiff; | |||
1801 | if( nCalc >= 26 ) | |||
1802 | rNm = OUStringChar( sal_Unicode('a' - 26 + nCalc) ) + rNm; | |||
1803 | else | |||
1804 | rNm = OUStringChar( sal_Unicode('A' + nCalc) ) + rNm; | |||
1805 | ||||
1806 | nCol = nCol - nCalc; | |||
1807 | if( 0 == nCol ) | |||
1808 | break; | |||
1809 | nCol /= coDiff; | |||
1810 | --nCol; | |||
1811 | } while( true ); | |||
1812 | } | |||
1813 | ||||
1814 | Point SwTableBox::GetCoordinates() const | |||
1815 | { | |||
1816 | if( !m_pStartNode ) // box without content? | |||
1817 | { | |||
1818 | // search for the next first box? | |||
1819 | return Point( 0, 0 ); | |||
1820 | } | |||
1821 | ||||
1822 | const SwTable& rTable = m_pStartNode->FindTableNode()->GetTable(); | |||
1823 | sal_uInt16 nX, nY; | |||
1824 | const SwTableBox* pBox = this; | |||
1825 | do { | |||
1826 | const SwTableLine* pLine = pBox->GetUpper(); | |||
1827 | // at the first level? | |||
1828 | const SwTableLines* pLines = pLine->GetUpper() | |||
1829 | ? &pLine->GetUpper()->GetTabLines() : &rTable.GetTabLines(); | |||
1830 | ||||
1831 | nY = pLines->GetPos( pLine ) + 1 ; | |||
1832 | nX = pBox->GetUpper()->GetBoxPos( pBox ) + 1; | |||
1833 | pBox = pLine->GetUpper(); | |||
1834 | } while( pBox ); | |||
1835 | return Point( nX, nY ); | |||
1836 | } | |||
1837 | ||||
1838 | OUString SwTableBox::GetName() const | |||
1839 | { | |||
1840 | if( !m_pStartNode ) // box without content? | |||
1841 | { | |||
1842 | // search for the next first box? | |||
1843 | return OUString(); | |||
1844 | } | |||
1845 | ||||
1846 | const SwTable& rTable = m_pStartNode->FindTableNode()->GetTable(); | |||
1847 | sal_uInt16 nPos; | |||
1848 | OUString sNm, sTmp; | |||
1849 | const SwTableBox* pBox = this; | |||
1850 | do { | |||
1851 | const SwTableLine* pLine = pBox->GetUpper(); | |||
1852 | // at the first level? | |||
1853 | const SwTableLines* pLines = pLine->GetUpper() | |||
1854 | ? &pLine->GetUpper()->GetTabLines() : &rTable.GetTabLines(); | |||
1855 | ||||
1856 | nPos = pLines->GetPos( pLine ) + 1; | |||
1857 | sTmp = OUString::number( nPos ); | |||
1858 | if( !sNm.isEmpty() ) | |||
1859 | sNm = sTmp + "." + sNm; | |||
1860 | else | |||
1861 | sNm = sTmp; | |||
1862 | ||||
1863 | nPos = pBox->GetUpper()->GetBoxPos( pBox ); | |||
1864 | sTmp = OUString::number(nPos + 1); | |||
1865 | pBox = pLine->GetUpper(); | |||
1866 | if( nullptr != pBox ) | |||
1867 | sNm = sTmp + "." + sNm; | |||
1868 | else | |||
1869 | sw_GetTableBoxColStr( nPos, sNm ); | |||
1870 | ||||
1871 | } while( pBox ); | |||
1872 | return sNm; | |||
1873 | } | |||
1874 | ||||
1875 | bool SwTableBox::IsInHeadline( const SwTable* pTable ) const | |||
1876 | { | |||
1877 | if( !GetUpper() ) // should only happen upon merge. | |||
1878 | return false; | |||
1879 | ||||
1880 | if( !pTable ) | |||
1881 | pTable = &m_pStartNode->FindTableNode()->GetTable(); | |||
1882 | ||||
1883 | const SwTableLine* pLine = GetUpper(); | |||
1884 | while( pLine->GetUpper() ) | |||
1885 | pLine = pLine->GetUpper()->GetUpper(); | |||
1886 | ||||
1887 | // Headerline? | |||
1888 | return pTable->GetTabLines()[ 0 ] == pLine; | |||
1889 | } | |||
1890 | ||||
1891 | sal_uLong SwTableBox::GetSttIdx() const | |||
1892 | { | |||
1893 | return m_pStartNode ? m_pStartNode->GetIndex() : 0; | |||
1894 | } | |||
1895 | ||||
1896 | // retrieve information from the client | |||
1897 | bool SwTable::GetInfo( SfxPoolItem& rInfo ) const | |||
1898 | { | |||
1899 | switch( rInfo.Which() ) | |||
1900 | { | |||
1901 | case RES_AUTOFMT_DOCNODE: | |||
1902 | { | |||
1903 | const SwTableNode* pNode = GetTableNode(); | |||
1904 | if (pNode && &pNode->GetNodes() == static_cast<SwAutoFormatGetDocNode&>(rInfo).pNodes) | |||
1905 | { | |||
1906 | if (!m_TabSortContentBoxes.empty()) | |||
1907 | { | |||
1908 | SwNodeIndex aIdx( *m_TabSortContentBoxes[0]->GetSttNd() ); | |||
1909 | GetFrameFormat()->GetDoc()->GetNodes().GoNext( &aIdx ); | |||
1910 | } | |||
1911 | return false; | |||
1912 | } | |||
1913 | break; | |||
1914 | } | |||
1915 | case RES_FINDNEARESTNODE: | |||
1916 | if( GetFrameFormat() && | |||
1917 | GetFrameFormat()->GetFormatAttr( RES_PAGEDESC ).GetPageDesc() && | |||
1918 | !m_TabSortContentBoxes.empty() && | |||
1919 | m_TabSortContentBoxes[0]->GetSttNd()->GetNodes().IsDocNodes() ) | |||
1920 | static_cast<SwFindNearestNode&>(rInfo).CheckNode( * | |||
1921 | m_TabSortContentBoxes[0]->GetSttNd()->FindTableNode() ); | |||
1922 | break; | |||
1923 | ||||
1924 | case RES_CONTENT_VISIBLE: | |||
1925 | static_cast<SwPtrMsgPoolItem&>(rInfo).pObject = SwIterator<SwFrame,SwFormat>( *GetFrameFormat() ).First(); | |||
1926 | return false; | |||
1927 | } | |||
1928 | return true; | |||
1929 | } | |||
1930 | ||||
1931 | SwTable * SwTable::FindTable( SwFrameFormat const*const pFormat ) | |||
1932 | { | |||
1933 | return pFormat | |||
1934 | ? SwIterator<SwTable,SwFormat>(*pFormat).First() | |||
1935 | : nullptr; | |||
1936 | } | |||
1937 | ||||
1938 | SwTableNode* SwTable::GetTableNode() const | |||
1939 | { | |||
1940 | return !GetTabSortBoxes().empty() ? | |||
1941 | const_cast<SwTableNode*>(GetTabSortBoxes()[ 0 ]->GetSttNd()->FindTableNode()) : | |||
1942 | m_pTableNode; | |||
1943 | } | |||
1944 | ||||
1945 | void SwTable::SetRefObject( SwServerObject* pObj ) | |||
1946 | { | |||
1947 | if( m_xRefObj.is() ) | |||
1948 | m_xRefObj->Closed(); | |||
1949 | ||||
1950 | m_xRefObj = pObj; | |||
1951 | } | |||
1952 | ||||
1953 | void SwTable::SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout> const& r) | |||
1954 | { | |||
1955 | m_xHTMLLayout = r; | |||
1956 | } | |||
1957 | ||||
1958 | static void ChgTextToNum( SwTableBox& rBox, const OUString& rText, const Color* pCol, | |||
1959 | bool bChgAlign ) | |||
1960 | { | |||
1961 | sal_uLong nNdPos = rBox.IsValidNumTextNd(); | |||
1962 | ChgTextToNum( rBox,rText,pCol,bChgAlign,nNdPos); | |||
1963 | } | |||
1964 | void ChgTextToNum( SwTableBox& rBox, const OUString& rText, const Color* pCol, | |||
1965 | bool bChgAlign,sal_uLong nNdPos ) | |||
1966 | { | |||
1967 | ||||
1968 | if( ULONG_MAX(9223372036854775807L *2UL+1UL) == nNdPos ) | |||
1969 | return; | |||
1970 | ||||
1971 | SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc(); | |||
1972 | SwTextNode* pTNd = pDoc->GetNodes()[ nNdPos ]->GetTextNode(); | |||
1973 | const SfxPoolItem* pItem; | |||
1974 | ||||
1975 | // assign adjustment | |||
1976 | if( bChgAlign ) | |||
1977 | { | |||
1978 | pItem = &pTNd->SwContentNode::GetAttr( RES_PARATR_ADJUST ); | |||
1979 | SvxAdjust eAdjust = static_cast<const SvxAdjustItem*>(pItem)->GetAdjust(); | |||
1980 | if( SvxAdjust::Left == eAdjust || SvxAdjust::Block == eAdjust ) | |||
1981 | { | |||
1982 | SvxAdjustItem aAdjust( *static_cast<const SvxAdjustItem*>(pItem) ); | |||
1983 | aAdjust.SetAdjust( SvxAdjust::Right ); | |||
1984 | pTNd->SetAttr( aAdjust ); | |||
1985 | } | |||
1986 | } | |||
1987 | ||||
1988 | // assign color or save "user color" | |||
1989 | if( !pTNd->GetpSwAttrSet() || SfxItemState::SET != pTNd->GetpSwAttrSet()-> | |||
1990 | GetItemState( RES_CHRATR_COLOR, false, &pItem )) | |||
1991 | pItem = nullptr; | |||
1992 | ||||
1993 | const Color* pOldNumFormatColor = rBox.GetSaveNumFormatColor(); | |||
1994 | const Color* pNewUserColor = pItem ? &static_cast<const SvxColorItem*>(pItem)->GetValue() : nullptr; | |||
1995 | ||||
1996 | if( ( pNewUserColor && pOldNumFormatColor && | |||
1997 | *pNewUserColor == *pOldNumFormatColor ) || | |||
1998 | ( !pNewUserColor && !pOldNumFormatColor )) | |||
1999 | { | |||
2000 | // Keep the user color, set updated values, delete old NumFormatColor if needed | |||
2001 | if( pCol ) | |||
2002 | // if needed, set the color | |||
2003 | pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR )); | |||
2004 | else if( pItem ) | |||
2005 | { | |||
2006 | pNewUserColor = rBox.GetSaveUserColor(); | |||
2007 | if( pNewUserColor ) | |||
2008 | pTNd->SetAttr( SvxColorItem( *pNewUserColor, RES_CHRATR_COLOR )); | |||
2009 | else | |||
2010 | pTNd->ResetAttr( RES_CHRATR_COLOR ); | |||
2011 | } | |||
2012 | } | |||
2013 | else | |||
2014 | { | |||
2015 | // Save user color, set NumFormat color if needed, but never reset the color | |||
2016 | rBox.SetSaveUserColor( pNewUserColor ); | |||
2017 | ||||
2018 | if( pCol ) | |||
2019 | // if needed, set the color | |||
2020 | pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR )); | |||
2021 | ||||
2022 | } | |||
2023 | rBox.SetSaveNumFormatColor( pCol ); | |||
2024 | ||||
2025 | if( pTNd->GetText() != rText ) | |||
2026 | { | |||
2027 | // Exchange text. Bugfix to keep Tabs (front and back!) and annotations (inword comment anchors) | |||
2028 | const OUString& rOrig = pTNd->GetText(); | |||
2029 | sal_Int32 n; | |||
2030 | ||||
2031 | for( n = 0; n < rOrig.getLength() && ('\x9' == rOrig[n] || CH_TXTATR_INWORDu'\xFFF9' == rOrig[n]); ++n ) | |||
2032 | ; | |||
2033 | for( ; n < rOrig.getLength() && '\x01' == rOrig[n]; ++n ) | |||
2034 | ; | |||
2035 | SwIndex aIdx( pTNd, n ); | |||
2036 | for( n = rOrig.getLength(); n && ('\x9' == rOrig[--n] || CH_TXTATR_INWORDu'\xFFF9' == rOrig[n]); ) | |||
2037 | ; | |||
2038 | sal_Int32 nEndPos = n; | |||
2039 | n -= aIdx.GetIndex() - 1; | |||
2040 | ||||
2041 | // Reset DontExpand-Flags before exchange, to retrigger expansion | |||
2042 | { | |||
2043 | SwIndex aResetIdx( aIdx, n ); | |||
2044 | pTNd->DontExpandFormat( aResetIdx, false, false ); | |||
2045 | } | |||
2046 | ||||
2047 | if( !pDoc->getIDocumentRedlineAccess().IsIgnoreRedline() && !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() ) | |||
2048 | { | |||
2049 | SwPaM aTemp(*pTNd, 0, *pTNd, rOrig.getLength()); | |||
2050 | pDoc->getIDocumentRedlineAccess().DeleteRedline(aTemp, true, RedlineType::Any); | |||
2051 | } | |||
2052 | ||||
2053 | // preserve comments inside of the number by deleting number portions starting from the back | |||
2054 | sal_Int32 nCommentPos = pTNd->GetText().lastIndexOf( CH_TXTATR_INWORDu'\xFFF9', nEndPos ); | |||
2055 | while( nCommentPos > aIdx.GetIndex() ) | |||
2056 | { | |||
2057 | pTNd->EraseText( SwIndex(pTNd, nCommentPos+1), nEndPos - nCommentPos, SwInsertFlags::EMPTYEXPAND ); | |||
2058 | // find the next non-sequential comment anchor | |||
2059 | do | |||
2060 | { | |||
2061 | nEndPos = nCommentPos; | |||
2062 | n = nEndPos - aIdx.GetIndex(); | |||
2063 | nCommentPos = pTNd->GetText().lastIndexOf( CH_TXTATR_INWORDu'\xFFF9', nEndPos ); | |||
2064 | --nEndPos; | |||
2065 | } | |||
2066 | while( nCommentPos > aIdx.GetIndex() && nCommentPos == nEndPos ); | |||
2067 | } | |||
2068 | ||||
2069 | pTNd->EraseText( aIdx, n, SwInsertFlags::EMPTYEXPAND ); | |||
2070 | pTNd->InsertText( rText, aIdx, SwInsertFlags::EMPTYEXPAND ); | |||
2071 | ||||
2072 | if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) | |||
2073 | { | |||
2074 | SwPaM aTemp(*pTNd, 0, *pTNd, rText.getLength()); | |||
2075 | pDoc->getIDocumentRedlineAccess().AppendRedline(new SwRangeRedline(RedlineType::Insert, aTemp), true); | |||
2076 | } | |||
2077 | } | |||
2078 | ||||
2079 | // assign vertical orientation | |||
2080 | if( bChgAlign && | |||
2081 | ( SfxItemState::SET != rBox.GetFrameFormat()->GetItemState( | |||
2082 | RES_VERT_ORIENT, true, &pItem ) || | |||
2083 | text::VertOrientation::TOP == static_cast<const SwFormatVertOrient*>(pItem)->GetVertOrient() )) | |||
2084 | { | |||
2085 | rBox.GetFrameFormat()->SetFormatAttr( SwFormatVertOrient( 0, text::VertOrientation::BOTTOM )); | |||
2086 | } | |||
2087 | ||||
2088 | } | |||
2089 | ||||
2090 | static void ChgNumToText( SwTableBox& rBox, sal_uLong nFormat ) | |||
2091 | { | |||
2092 | sal_uLong nNdPos = rBox.IsValidNumTextNd( false ); | |||
2093 | if( ULONG_MAX(9223372036854775807L *2UL+1UL) == nNdPos ) | |||
2094 | return; | |||
2095 | ||||
2096 | SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc(); | |||
2097 | SwTextNode* pTNd = pDoc->GetNodes()[ nNdPos ]->GetTextNode(); | |||
2098 | bool bChgAlign = pDoc->IsInsTableAlignNum(); | |||
2099 | const SfxPoolItem* pItem; | |||
2100 | ||||
2101 | const Color* pCol = nullptr; | |||
2102 | if( getSwDefaultTextFormat() != nFormat ) | |||
2103 | { | |||
2104 | // special text format: | |||
2105 | OUString sTmp; | |||
2106 | const OUString sText( pTNd->GetText() ); | |||
2107 | pDoc->GetNumberFormatter()->GetOutputString( sText, nFormat, sTmp, &pCol ); | |||
2108 | if( sText != sTmp ) | |||
2109 | { | |||
2110 | // exchange text | |||
2111 | SwIndex aIdx( pTNd, sText.getLength() ); | |||
2112 | // Reset DontExpand-Flags before exchange, to retrigger expansion | |||
2113 | pTNd->DontExpandFormat( aIdx, false, false ); | |||
2114 | aIdx = 0; | |||
2115 | pTNd->EraseText( aIdx, SAL_MAX_INT32((sal_Int32) 0x7FFFFFFF), SwInsertFlags::EMPTYEXPAND ); | |||
2116 | pTNd->InsertText( sTmp, aIdx, SwInsertFlags::EMPTYEXPAND ); | |||
2117 | } | |||
2118 | } | |||
2119 | ||||
2120 | const SfxItemSet* pAttrSet = pTNd->GetpSwAttrSet(); | |||
2121 | ||||
2122 | // assign adjustment | |||
2123 | if( bChgAlign && pAttrSet && SfxItemState::SET == pAttrSet->GetItemState( | |||
2124 | RES_PARATR_ADJUST, false, &pItem ) && | |||
2125 | SvxAdjust::Right == static_cast<const SvxAdjustItem*>(pItem)->GetAdjust() ) | |||
2126 | { | |||
2127 | pTNd->SetAttr( SvxAdjustItem( SvxAdjust::Left, RES_PARATR_ADJUST ) ); | |||
2128 | } | |||
2129 | ||||
2130 | // assign color or save "user color" | |||
2131 | if( !pAttrSet || SfxItemState::SET != pAttrSet-> | |||
2132 | GetItemState( RES_CHRATR_COLOR, false, &pItem )) | |||
2133 | pItem = nullptr; | |||
2134 | ||||
2135 | const Color* pOldNumFormatColor = rBox.GetSaveNumFormatColor(); | |||
2136 | const Color* pNewUserColor = pItem ? &static_cast<const SvxColorItem*>(pItem)->GetValue() : nullptr; | |||
2137 | ||||
2138 | if( ( pNewUserColor && pOldNumFormatColor && | |||
2139 | *pNewUserColor == *pOldNumFormatColor ) || | |||
2140 | ( !pNewUserColor && !pOldNumFormatColor )) | |||
2141 | { | |||
2142 | // Keep the user color, set updated values, delete old NumFormatColor if needed | |||
2143 | if( pCol ) | |||
2144 | // if needed, set the color | |||
2145 | pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR )); | |||
2146 | else if( pItem ) | |||
2147 | { | |||
2148 | pNewUserColor = rBox.GetSaveUserColor(); | |||
2149 | if( pNewUserColor ) | |||
2150 | pTNd->SetAttr( SvxColorItem( *pNewUserColor, RES_CHRATR_COLOR )); | |||
2151 | else | |||
2152 | pTNd->ResetAttr( RES_CHRATR_COLOR ); | |||
2153 | } | |||
2154 | } | |||
2155 | else | |||
2156 | { | |||
2157 | // Save user color, set NumFormat color if needed, but never reset the color | |||
2158 | rBox.SetSaveUserColor( pNewUserColor ); | |||
2159 | ||||
2160 | if( pCol ) | |||
2161 | // if needed, set the color | |||
2162 | pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR )); | |||
2163 | ||||
2164 | } | |||
2165 | rBox.SetSaveNumFormatColor( pCol ); | |||
2166 | ||||
2167 | // assign vertical orientation | |||
2168 | if( bChgAlign && | |||
2169 | SfxItemState::SET == rBox.GetFrameFormat()->GetItemState( | |||
2170 | RES_VERT_ORIENT, false, &pItem ) && | |||
2171 | text::VertOrientation::BOTTOM == static_cast<const SwFormatVertOrient*>(pItem)->GetVertOrient() ) | |||
2172 | { | |||
2173 | rBox.GetFrameFormat()->SetFormatAttr( SwFormatVertOrient( 0, text::VertOrientation::TOP )); | |||
2174 | } | |||
2175 | ||||
2176 | } | |||
2177 | ||||
2178 | // for detection of modifications (mainly TableBoxAttribute) | |||
2179 | void SwTableBoxFormat::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) | |||
2180 | { | |||
2181 | if( !IsModifyLocked() && GetDoc() && !GetDoc()->IsInDtor()) | |||
2182 | { | |||
2183 | const SwTableBoxNumFormat *pNewFormat = nullptr; | |||
2184 | const SwTableBoxFormula *pNewFormula = nullptr; | |||
2185 | const SwTableBoxValue *pNewVal = nullptr; | |||
2186 | sal_uLong nOldFormat = getSwDefaultTextFormat(); | |||
2187 | ||||
2188 | switch( pNew ? pNew->Which() : 0 ) | |||
2189 | { | |||
2190 | case RES_ATTRSET_CHG: | |||
2191 | { | |||
2192 | const SfxItemSet& rSet = *static_cast<const SwAttrSetChg*>(pNew)->GetChgSet(); | |||
2193 | if( SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMAT, | |||
2194 | false, reinterpret_cast<const SfxPoolItem**>(&pNewFormat) ) ) | |||
2195 | nOldFormat = static_cast<const SwAttrSetChg*>(pOld)-> | |||
2196 | GetChgSet()->Get( RES_BOXATR_FORMAT ).GetValue(); | |||
2197 | rSet.GetItemState( RES_BOXATR_FORMULA, false, | |||
2198 | reinterpret_cast<const SfxPoolItem**>(&pNewFormula) ); | |||
2199 | rSet.GetItemState( RES_BOXATR_VALUE, false, | |||
2200 | reinterpret_cast<const SfxPoolItem**>(&pNewVal) ); | |||
2201 | break; | |||
2202 | } | |||
2203 | case RES_BOXATR_FORMAT: | |||
2204 | pNewFormat = static_cast<const SwTableBoxNumFormat*>(pNew); | |||
2205 | nOldFormat = static_cast<const SwTableBoxNumFormat*>(pOld)->GetValue(); | |||
2206 | break; | |||
2207 | case RES_BOXATR_FORMULA: | |||
2208 | pNewFormula = static_cast<const SwTableBoxFormula*>(pNew); | |||
2209 | break; | |||
2210 | case RES_BOXATR_VALUE: | |||
2211 | pNewVal = static_cast<const SwTableBoxValue*>(pNew); | |||
2212 | break; | |||
2213 | } | |||
2214 | ||||
2215 | // something changed and some BoxAttribut remained in the set! | |||
2216 | if( pNewFormat || pNewFormula || pNewVal ) | |||
2217 | { | |||
2218 | GetDoc()->getIDocumentFieldsAccess().SetFieldsDirty(true, nullptr, 0); | |||
2219 | ||||
2220 | if( SfxItemState::SET == GetItemState( RES_BOXATR_FORMAT, false ) || | |||
2221 | SfxItemState::SET == GetItemState( RES_BOXATR_VALUE, false ) || | |||
2222 | SfxItemState::SET == GetItemState( RES_BOXATR_FORMULA, false ) ) | |||
2223 | { | |||
2224 | // fetch the box | |||
2225 | SwIterator<SwTableBox,SwFormat> aIter( *this ); | |||
2226 | SwTableBox* pBox = aIter.First(); | |||
2227 | if( pBox ) | |||
2228 | { | |||
2229 | OSL_ENSURE( !aIter.Next(), "zero or more than one box at format" )do { if (true && (!(!aIter.Next()))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sw/source/core/table/swtable.cxx" ":" "2229" ": "), "%s", "zero or more than one box at format" ); } } while (false); | |||
2230 | ||||
2231 | sal_uLong nNewFormat; | |||
2232 | if( pNewFormat ) | |||
2233 | { | |||
2234 | nNewFormat = pNewFormat->GetValue(); | |||
2235 | // new formatting | |||
2236 | // is it newer or has the current been removed? | |||
2237 | if( SfxItemState::SET != GetItemState( RES_BOXATR_VALUE, false )) | |||
2238 | pNewFormat = nullptr; | |||
2239 | } | |||
2240 | else | |||
2241 | { | |||
2242 | // fetch the current Item | |||
2243 | (void)GetItemState(RES_BOXATR_FORMAT, false, reinterpret_cast<const SfxPoolItem**>(&pNewFormat)); | |||
2244 | nOldFormat = GetTableBoxNumFormat().GetValue(); | |||
2245 | nNewFormat = pNewFormat ? pNewFormat->GetValue() : nOldFormat; | |||
2246 | } | |||
2247 | ||||
2248 | // is it newer or has the current been removed? | |||
2249 | if( pNewVal ) | |||
2250 | { | |||
2251 | if( GetDoc()->GetNumberFormatter()->IsTextFormat(nNewFormat) ) | |||
2252 | nOldFormat = 0; | |||
2253 | else | |||
2254 | { | |||
2255 | if( SfxItemState::SET == GetItemState( RES_BOXATR_VALUE, false )) | |||
2256 | nOldFormat = getSwDefaultTextFormat(); | |||
2257 | else | |||
2258 | nNewFormat = getSwDefaultTextFormat(); | |||
2259 | } | |||
2260 | } | |||
2261 | ||||
2262 | // Logic: | |||
2263 | // Value change: -> "simulate" a format change! | |||
2264 | // Format change: | |||
2265 | // Text -> !Text or format change: | |||
2266 | // - align right for horizontal alignment, if LEFT or JUSTIFIED | |||
2267 | // - align bottom for vertical alignment, if TOP is set, or default | |||
2268 | // - replace text (color? negative numbers RED?) | |||
2269 | // !Text -> Text: | |||
2270 | // - align left for horizontal alignment, if RIGHT | |||
2271 | // - align top for vertical alignment, if BOTTOM is set | |||
2272 | SvNumberFormatter* pNumFormatr = GetDoc()->GetNumberFormatter(); | |||
2273 | bool bNewIsTextFormat = pNumFormatr->IsTextFormat( nNewFormat ); | |||
2274 | ||||
2275 | if( (!bNewIsTextFormat && nOldFormat != nNewFormat) || pNewFormula ) | |||
2276 | { | |||
2277 | bool bIsNumFormat = false; | |||
2278 | OUString aOrigText; | |||
2279 | bool bChgText = true; | |||
2280 | double fVal = 0; | |||
2281 | if( !pNewVal && SfxItemState::SET != GetItemState( | |||
2282 | RES_BOXATR_VALUE, false, reinterpret_cast<const SfxPoolItem**>(&pNewVal) )) | |||
2283 | { | |||
2284 | // so far, no value has been set, so try to evaluate the content | |||
2285 | sal_uLong nNdPos = pBox->IsValidNumTextNd(); | |||
2286 | if( ULONG_MAX(9223372036854775807L *2UL+1UL) != nNdPos ) | |||
2287 | { | |||
2288 | sal_uInt32 nTmpFormatIdx = nNewFormat; | |||
2289 | OUString aText( GetDoc()->GetNodes()[ nNdPos ] | |||
2290 | ->GetTextNode()->GetRedlineText()); | |||
2291 | aOrigText = aText; | |||
2292 | if( aText.isEmpty() ) | |||
2293 | bChgText = false; | |||
2294 | else | |||
2295 | { | |||
2296 | // Keep Tabs | |||
2297 | lcl_TabToBlankAtSttEnd( aText ); | |||
2298 | ||||
2299 | // JP 22.04.98: Bug 49659 - | |||
2300 | // Special casing for percent | |||
2301 | if( SvNumFormatType::PERCENT == | |||
2302 | pNumFormatr->GetType( nNewFormat )) | |||
2303 | { | |||
2304 | sal_uInt32 nTmpFormat = 0; | |||
2305 | if( GetDoc()->IsNumberFormat( | |||
2306 | aText, nTmpFormat, fVal )) | |||
2307 | { | |||
2308 | if( SvNumFormatType::NUMBER == | |||
2309 | pNumFormatr->GetType( nTmpFormat )) | |||
2310 | aText += "%"; | |||
2311 | ||||
2312 | bIsNumFormat = GetDoc()->IsNumberFormat( | |||
2313 | aText, nTmpFormatIdx, fVal ); | |||
2314 | } | |||
2315 | } | |||
2316 | else | |||
2317 | bIsNumFormat = GetDoc()->IsNumberFormat( | |||
2318 | aText, nTmpFormatIdx, fVal ); | |||
2319 | ||||
2320 | if( bIsNumFormat ) | |||
2321 | { | |||
2322 | // directly assign value - without Modify | |||
2323 | bool bIsLockMod = IsModifyLocked(); | |||
2324 | LockModify(); | |||
2325 | SetFormatAttr( SwTableBoxValue( fVal )); | |||
2326 | if( !bIsLockMod ) | |||
2327 | UnlockModify(); | |||
2328 | } | |||
2329 | } | |||
2330 | } | |||
2331 | } | |||
2332 | else | |||
2333 | { | |||
2334 | fVal = pNewVal->GetValue(); | |||
2335 | bIsNumFormat = true; | |||
2336 | } | |||
2337 | ||||
2338 | // format contents with the new value assigned and write to paragraph | |||
2339 | const Color* pCol = nullptr; | |||
2340 | OUString sNewText; | |||
2341 | if( DBL_MAX1.7976931348623157e+308 == fVal ) | |||
2342 | { | |||
2343 | sNewText = SwViewShell::GetShellRes()->aCalc_Error; | |||
2344 | } | |||
2345 | else | |||
2346 | { | |||
2347 | if (bIsNumFormat) | |||
2348 | pNumFormatr->GetOutputString( fVal, nNewFormat, sNewText, &pCol ); | |||
2349 | else | |||
2350 | { | |||
2351 | // Original text could not be parsed as | |||
2352 | // number/date/time/..., so keep the text. | |||
2353 | #if 0 | |||
2354 | // Actually the text should be formatted | |||
2355 | // according to the format, which may include | |||
2356 | // additional text from the format, for example | |||
2357 | // in {0;-0;"BAD: "@}. But other places when | |||
2358 | // entering a new value or changing text or | |||
2359 | // changing to a different format of type Text | |||
2360 | // don't do this (yet?). | |||
2361 | pNumFormatr->GetOutputString( aOrigText, nNewFormat, sNewText, &pCol ); | |||
2362 | #else | |||
2363 | sNewText = aOrigText; | |||
2364 | #endif | |||
2365 | } | |||
2366 | ||||
2367 | if( !bChgText ) | |||
2368 | { | |||
2369 | sNewText.clear(); | |||
2370 | } | |||
2371 | } | |||
2372 | ||||
2373 | // across all boxes | |||
2374 | ChgTextToNum( *pBox, sNewText, pCol, | |||
2375 | GetDoc()->IsInsTableAlignNum() ); | |||
2376 | ||||
2377 | } | |||
2378 | else if( bNewIsTextFormat && nOldFormat != nNewFormat ) | |||
2379 | { | |||
2380 | ChgNumToText( *pBox, nNewFormat ); | |||
2381 | } | |||
2382 | } | |||
2383 | } | |||
2384 | } | |||
2385 | } | |||
2386 | // call base class | |||
2387 | SwFrameFormat::Modify( pOld, pNew ); | |||
2388 | } | |||
2389 | ||||
2390 | bool SwTableBoxFormat::supportsFullDrawingLayerFillAttributeSet() const | |||
2391 | { | |||
2392 | return false; | |||
2393 | } | |||
2394 | ||||
2395 | bool SwTableFormat::supportsFullDrawingLayerFillAttributeSet() const | |||
2396 | { | |||
2397 | return false; | |||
2398 | } | |||
2399 | ||||
2400 | bool SwTableLineFormat::supportsFullDrawingLayerFillAttributeSet() const | |||
2401 | { | |||
2402 | return false; | |||
2403 | } | |||
2404 | ||||
2405 | bool SwTableBox::HasNumContent( double& rNum, sal_uInt32& rFormatIndex, | |||
2406 | bool& rIsEmptyTextNd ) const | |||
2407 | { | |||
2408 | bool bRet = false; | |||
2409 | sal_uLong nNdPos = IsValidNumTextNd(); | |||
2410 | if( ULONG_MAX(9223372036854775807L *2UL+1UL) != nNdPos ) | |||
2411 | { | |||
2412 | OUString aText( m_pStartNode->GetNodes()[ nNdPos ]->GetTextNode()->GetRedlineText() ); | |||
2413 | // Keep Tabs | |||
2414 | lcl_TabToBlankAtSttEnd( aText ); | |||
2415 | rIsEmptyTextNd = aText.isEmpty(); | |||
2416 | SvNumberFormatter* pNumFormatr = GetFrameFormat()->GetDoc()->GetNumberFormatter(); | |||
2417 | ||||
2418 | const SfxPoolItem* pItem; | |||
2419 | if( SfxItemState::SET == GetFrameFormat()->GetItemState( RES_BOXATR_FORMAT, false, &pItem )) | |||
2420 | { | |||
2421 | rFormatIndex = static_cast<const SwTableBoxNumFormat*>(pItem)->GetValue(); | |||
2422 | // Special casing for percent | |||
2423 | if( !rIsEmptyTextNd && SvNumFormatType::PERCENT == pNumFormatr->GetType( rFormatIndex )) | |||
2424 | { | |||
2425 | sal_uInt32 nTmpFormat = 0; | |||
2426 | if( GetFrameFormat()->GetDoc()->IsNumberFormat( aText, nTmpFormat, rNum ) && | |||
2427 | SvNumFormatType::NUMBER == pNumFormatr->GetType( nTmpFormat )) | |||
2428 | aText += "%"; | |||
2429 | } | |||
2430 | } | |||
2431 | else | |||
2432 | rFormatIndex = 0; | |||
2433 | ||||
2434 | bRet = GetFrameFormat()->GetDoc()->IsNumberFormat( aText, rFormatIndex, rNum ); | |||
2435 | } | |||
2436 | else | |||
2437 | rIsEmptyTextNd = false; | |||
2438 | return bRet; | |||
2439 | } | |||
2440 | ||||
2441 | bool SwTableBox::IsNumberChanged() const | |||
2442 | { | |||
2443 | bool bRet = true; | |||
2444 | ||||
2445 | if( SfxItemState::SET == GetFrameFormat()->GetItemState( RES_BOXATR_FORMULA, false )) | |||
2446 | { | |||
2447 | const SwTableBoxNumFormat *pNumFormat; | |||
2448 | const SwTableBoxValue *pValue; | |||
2449 | ||||
2450 | if( SfxItemState::SET != GetFrameFormat()->GetItemState( RES_BOXATR_VALUE, false, | |||
2451 | reinterpret_cast<const SfxPoolItem**>(&pValue) )) | |||
2452 | pValue = nullptr; | |||
2453 | if( SfxItemState::SET != GetFrameFormat()->GetItemState( RES_BOXATR_FORMAT, false, | |||
2454 | reinterpret_cast<const SfxPoolItem**>(&pNumFormat) )) | |||
2455 | pNumFormat = nullptr; | |||
2456 | ||||
2457 | sal_uLong nNdPos; | |||
2458 | if( pNumFormat && pValue && ULONG_MAX(9223372036854775807L *2UL+1UL) != ( nNdPos = IsValidNumTextNd() ) ) | |||
2459 | { | |||
2460 | OUString sNewText, sOldText( m_pStartNode->GetNodes()[ nNdPos ]-> | |||
2461 | GetTextNode()->GetRedlineText() ); | |||
2462 | lcl_DelTabsAtSttEnd( sOldText ); | |||
2463 | ||||
2464 | const Color* pCol = nullptr; | |||
2465 | GetFrameFormat()->GetDoc()->GetNumberFormatter()->GetOutputString( | |||
2466 | pValue->GetValue(), pNumFormat->GetValue(), sNewText, &pCol ); | |||
2467 | ||||
2468 | bRet = sNewText != sOldText || | |||
2469 | !( ( !pCol && !GetSaveNumFormatColor() ) || | |||
2470 | ( pCol && GetSaveNumFormatColor() && | |||
2471 | *pCol == *GetSaveNumFormatColor() )); | |||
2472 | } | |||
2473 | } | |||
2474 | return bRet; | |||
2475 | } | |||
2476 | ||||
2477 | sal_uLong SwTableBox::IsValidNumTextNd( bool bCheckAttr ) const | |||
2478 | { | |||
2479 | sal_uLong nPos = ULONG_MAX(9223372036854775807L *2UL+1UL); | |||
2480 | if( m_pStartNode ) | |||
2481 | { | |||
2482 | SwNodeIndex aIdx( *m_pStartNode ); | |||
2483 | sal_uLong nIndex = aIdx.GetIndex(); | |||
2484 | const sal_uLong nIndexEnd = m_pStartNode->GetNodes()[ nIndex ]->EndOfSectionIndex(); | |||
2485 | const SwTextNode *pTextNode = nullptr; | |||
2486 | while( ++nIndex < nIndexEnd ) | |||
2487 | { | |||
2488 | const SwNode* pNode = m_pStartNode->GetNodes()[nIndex]; | |||
2489 | if( pNode->IsTableNode() ) | |||
2490 | { | |||
2491 | pTextNode = nullptr; | |||
2492 | break; | |||
2493 | } | |||
2494 | if( pNode->IsTextNode() ) | |||
2495 | { | |||
2496 | if( pTextNode ) | |||
2497 | { | |||
2498 | pTextNode = nullptr; | |||
2499 | break; | |||
2500 | } | |||
2501 | else | |||
2502 | { | |||
2503 | pTextNode = pNode->GetTextNode(); | |||
2504 | nPos = nIndex; | |||
2505 | } | |||
2506 | } | |||
2507 | } | |||
2508 | if( pTextNode ) | |||
2509 | { | |||
2510 | if( bCheckAttr ) | |||
2511 | { | |||
2512 | const SwpHints* pHts = pTextNode->GetpSwpHints(); | |||
2513 | // do some tests if there's only text in the node! | |||
2514 | // Flys/fields/... | |||
2515 | if( pHts ) | |||
2516 | { | |||
2517 | sal_Int32 nNextSetField = 0; | |||
2518 | for( size_t n = 0; n < pHts->Count(); ++n ) | |||
2519 | { | |||
2520 | const SwTextAttr* pAttr = pHts->Get(n); | |||
2521 | if( RES_TXTATR_NOEND_BEGIN <= pAttr->Which() ) | |||
2522 | { | |||
2523 | if ( (pAttr->GetStart() == nNextSetField) | |||
2524 | && (pAttr->Which() == RES_TXTATR_FIELD)) | |||
2525 | { | |||
2526 | // #i104949# hideous hack for report builder: | |||
2527 | // it inserts hidden variable-set fields at | |||
2528 | // the beginning of para in cell, but they | |||
2529 | // should not turn cell into text cell | |||
2530 | const SwField* pField = pAttr->GetFormatField().GetField(); | |||
2531 | if (pField && | |||
2532 | (pField->GetTypeId() == SwFieldTypesEnum::Set) && | |||
2533 | (0 != (static_cast<SwSetExpField const*> | |||
2534 | (pField)->GetSubType() & | |||
2535 | nsSwExtendedSubType::SUB_INVISIBLE))) | |||
2536 | { | |||
2537 | nNextSetField = pAttr->GetStart() + 1; | |||
2538 | continue; | |||
2539 | } | |||
2540 | } | |||
2541 | else if( RES_TXTATR_ANNOTATION == pAttr->Which() ) | |||
2542 | { | |||
2543 | continue; | |||
2544 | } | |||
2545 | nPos = ULONG_MAX(9223372036854775807L *2UL+1UL); | |||
2546 | break; | |||
2547 | } | |||
2548 | } | |||
2549 | } | |||
2550 | } | |||
2551 | } | |||
2552 | else | |||
2553 | nPos = ULONG_MAX(9223372036854775807L *2UL+1UL); | |||
2554 | } | |||
2555 | return nPos; | |||
2556 | } | |||
2557 | ||||
2558 | // is this a Formula box or one with numeric content (AutoSum) | |||
2559 | sal_uInt16 SwTableBox::IsFormulaOrValueBox() const | |||
2560 | { | |||
2561 | sal_uInt16 nWhich = 0; | |||
2562 | const SwTextNode* pTNd; | |||
2563 | SwFrameFormat* pFormat = GetFrameFormat(); | |||
2564 | if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_FORMULA, false )) | |||
2565 | nWhich = RES_BOXATR_FORMULA; | |||
2566 | else if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_VALUE, false ) && | |||
2567 | !pFormat->GetDoc()->GetNumberFormatter()->IsTextFormat( | |||
2568 | pFormat->GetTableBoxNumFormat().GetValue() )) | |||
2569 | nWhich = RES_BOXATR_VALUE; | |||
2570 | else if( m_pStartNode && m_pStartNode->GetIndex() + 2 == m_pStartNode->EndOfSectionIndex() | |||
2571 | && nullptr != ( pTNd = m_pStartNode->GetNodes()[ m_pStartNode->GetIndex() + 1 ] | |||
2572 | ->GetTextNode() ) && pTNd->GetText().isEmpty()) | |||
2573 | nWhich = USHRT_MAX(32767 *2 +1); | |||
2574 | ||||
2575 | return nWhich; | |||
2576 | } | |||
2577 | ||||
2578 | void SwTableBox::ActualiseValueBox() | |||
2579 | { | |||
2580 | const SfxPoolItem *pFormatItem, *pValItem; | |||
2581 | SwFrameFormat* pFormat = GetFrameFormat(); | |||
2582 | if( SfxItemState::SET != pFormat->GetItemState( RES_BOXATR_FORMAT, true, &pFormatItem ) | |||
2583 | || SfxItemState::SET != pFormat->GetItemState( RES_BOXATR_VALUE, true, &pValItem )) | |||
2584 | return; | |||
2585 | ||||
2586 | const sal_uLong nFormatId = static_cast<const SwTableBoxNumFormat*>(pFormatItem)->GetValue(); | |||
2587 | sal_uLong nNdPos = ULONG_MAX(9223372036854775807L *2UL+1UL); | |||
2588 | SvNumberFormatter* pNumFormatr = pFormat->GetDoc()->GetNumberFormatter(); | |||
2589 | ||||
2590 | if( !pNumFormatr->IsTextFormat( nFormatId ) && | |||
2591 | ULONG_MAX(9223372036854775807L *2UL+1UL) != (nNdPos = IsValidNumTextNd()) ) | |||
2592 | { | |||
2593 | double fVal = static_cast<const SwTableBoxValue*>(pValItem)->GetValue(); | |||
2594 | const Color* pCol = nullptr; | |||
2595 | OUString sNewText; | |||
2596 | pNumFormatr->GetOutputString( fVal, nFormatId, sNewText, &pCol ); | |||
2597 | ||||
2598 | const OUString& rText = m_pStartNode->GetNodes()[ nNdPos ]->GetTextNode()->GetText(); | |||
2599 | if( rText != sNewText ) | |||
2600 | ChgTextToNum( *this, sNewText, pCol, false ,nNdPos); | |||
2601 | } | |||
2602 | } | |||
2603 | ||||
2604 | struct SwTableCellInfo::Impl | |||
2605 | { | |||
2606 | const SwTable * m_pTable; | |||
2607 | const SwCellFrame * m_pCellFrame; | |||
2608 | const SwTabFrame * m_pTabFrame; | |||
2609 | typedef o3tl::sorted_vector<const SwTableBox *> TableBoxes_t; | |||
2610 | TableBoxes_t m_HandledTableBoxes; | |||
2611 | ||||
2612 | public: | |||
2613 | Impl() | |||
2614 | : m_pTable(nullptr), m_pCellFrame(nullptr), m_pTabFrame(nullptr) | |||
2615 | { | |||
2616 | } | |||
2617 | ||||
2618 | void setTable(const SwTable * pTable) | |||
2619 | { | |||
2620 | m_pTable = pTable; | |||
2621 | SwFrameFormat * pFrameFormat = m_pTable->GetFrameFormat(); | |||
2622 | m_pTabFrame = SwIterator<SwTabFrame,SwFormat>(*pFrameFormat).First(); | |||
2623 | if (m_pTabFrame && m_pTabFrame->IsFollow()) | |||
2624 | m_pTabFrame = m_pTabFrame->FindMaster(true); | |||
2625 | } | |||
2626 | ||||
2627 | const SwCellFrame * getCellFrame() const { return m_pCellFrame; } | |||
2628 | ||||
2629 | const SwFrame * getNextFrameInTable(const SwFrame * pFrame); | |||
2630 | const SwCellFrame * getNextCellFrame(const SwFrame * pFrame); | |||
2631 | const SwCellFrame * getNextTableBoxsCellFrame(const SwFrame * pFrame); | |||
2632 | bool getNext(); | |||
2633 | }; | |||
2634 | ||||
2635 | const SwFrame * SwTableCellInfo::Impl::getNextFrameInTable(const SwFrame * pFrame) | |||
2636 | { | |||
2637 | const SwFrame * pResult = nullptr; | |||
2638 | ||||
2639 | if (((! pFrame->IsTabFrame()) || pFrame == m_pTabFrame) && pFrame->GetLower()) | |||
2640 | pResult = pFrame->GetLower(); | |||
2641 | else if (pFrame->GetNext()) | |||
2642 | pResult = pFrame->GetNext(); | |||
2643 | else | |||
2644 | { | |||
2645 | while (pFrame->GetUpper() != nullptr) | |||
2646 | { | |||
2647 | pFrame = pFrame->GetUpper(); | |||
2648 | ||||
2649 | if (pFrame->IsTabFrame()) | |||
2650 | { | |||
2651 | m_pTabFrame = static_cast<const SwTabFrame *>(pFrame)->GetFollow(); | |||
2652 | pResult = m_pTabFrame; | |||
2653 | break; | |||
2654 | } | |||
2655 | else if (pFrame->GetNext()) | |||
2656 | { | |||
2657 | pResult = pFrame->GetNext(); | |||
2658 | break; | |||
2659 | } | |||
2660 | } | |||
2661 | } | |||
2662 | ||||
2663 | return pResult; | |||
2664 | } | |||
2665 | ||||
2666 | const SwCellFrame * SwTableCellInfo::Impl::getNextCellFrame(const SwFrame * pFrame) | |||
2667 | { | |||
2668 | const SwCellFrame * pResult = nullptr; | |||
2669 | ||||
2670 | while ((pFrame = getNextFrameInTable(pFrame)) != nullptr) | |||
2671 | { | |||
2672 | if (pFrame->IsCellFrame()) | |||
2673 | { | |||
2674 | pResult = static_cast<const SwCellFrame *>(pFrame); | |||
2675 | break; | |||
2676 | } | |||
2677 | } | |||
2678 | ||||
2679 | return pResult; | |||
2680 | } | |||
2681 | ||||
2682 | const SwCellFrame * SwTableCellInfo::Impl::getNextTableBoxsCellFrame(const SwFrame * pFrame) | |||
2683 | { | |||
2684 | const SwCellFrame * pResult = nullptr; | |||
2685 | ||||
2686 | while ((pFrame = getNextCellFrame(pFrame)) != nullptr) | |||
2687 | { | |||
2688 | const SwCellFrame * pCellFrame = static_cast<const SwCellFrame *>(pFrame); | |||
2689 | const SwTableBox * pTabBox = pCellFrame->GetTabBox(); | |||
2690 | auto aIt = m_HandledTableBoxes.insert(pTabBox); | |||
2691 | if (aIt.second) | |||
2692 | { | |||
2693 | pResult = pCellFrame; | |||
2694 | break; | |||
2695 | } | |||
2696 | } | |||
2697 | ||||
2698 | return pResult; | |||
2699 | } | |||
2700 | ||||
2701 | const SwCellFrame * SwTableCellInfo::getCellFrame() const | |||
2702 | { | |||
2703 | return m_pImpl->getCellFrame(); | |||
2704 | } | |||
2705 | ||||
2706 | bool SwTableCellInfo::Impl::getNext() | |||
2707 | { | |||
2708 | if (m_pCellFrame == nullptr) | |||
2709 | { | |||
2710 | if (m_pTabFrame != nullptr) | |||
2711 | m_pCellFrame = Impl::getNextTableBoxsCellFrame(m_pTabFrame); | |||
2712 | } | |||
2713 | else | |||
2714 | m_pCellFrame = Impl::getNextTableBoxsCellFrame(m_pCellFrame); | |||
2715 | ||||
2716 | return m_pCellFrame != nullptr; | |||
2717 | } | |||
2718 | ||||
2719 | SwTableCellInfo::SwTableCellInfo(const SwTable * pTable) | |||
2720 | : m_pImpl(std::make_unique<Impl>()) | |||
2721 | { | |||
2722 | m_pImpl->setTable(pTable); | |||
2723 | } | |||
2724 | ||||
2725 | SwTableCellInfo::~SwTableCellInfo() | |||
2726 | { | |||
2727 | } | |||
2728 | ||||
2729 | bool SwTableCellInfo::getNext() | |||
2730 | { | |||
2731 | return m_pImpl->getNext(); | |||
2732 | } | |||
2733 | ||||
2734 | SwRect SwTableCellInfo::getRect() const | |||
2735 | { | |||
2736 | SwRect aRet; | |||
2737 | ||||
2738 | if (getCellFrame() != nullptr) | |||
2739 | aRet = getCellFrame()->getFrameArea(); | |||
2740 | ||||
2741 | return aRet; | |||
2742 | } | |||
2743 | ||||
2744 | const SwTableBox * SwTableCellInfo::getTableBox() const | |||
2745 | { | |||
2746 | const SwTableBox * pRet = nullptr; | |||
2747 | ||||
2748 | if (getCellFrame() != nullptr) | |||
2749 | pRet = getCellFrame()->GetTabBox(); | |||
2750 | ||||
2751 | return pRet; | |||
2752 | } | |||
2753 | ||||
2754 | void SwTable::RegisterToFormat( SwFormat& rFormat ) | |||
2755 | { | |||
2756 | rFormat.Add( this ); | |||
2757 | } | |||
2758 | ||||
2759 | bool SwTable::HasLayout() const | |||
2760 | { | |||
2761 | const SwFrameFormat* pFrameFormat = GetFrameFormat(); | |||
2762 | //a table in a clipboard document doesn't have any layout information | |||
2763 | return pFrameFormat && SwIterator<SwTabFrame,SwFormat>(*pFrameFormat).First(); | |||
2764 | } | |||
2765 | ||||
2766 | void SwTableLine::RegisterToFormat( SwFormat& rFormat ) | |||
2767 | { | |||
2768 | rFormat.Add( this ); | |||
2769 | } | |||
2770 | ||||
2771 | void SwTableBox::RegisterToFormat( SwFormat& rFormat ) | |||
2772 | { | |||
2773 | rFormat.Add( this ); | |||
2774 | } | |||
2775 | ||||
2776 | // free's any remaining child objects | |||
2777 | SwTableLines::~SwTableLines() | |||
2778 | { | |||
2779 | for ( const_iterator it = begin(); it != end(); ++it ) | |||
2780 | delete *it; | |||
2781 | } | |||
2782 | ||||
2783 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |
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 | #ifndef INCLUDED_SW_INC_CALBCK_HXX | ||||||||||
21 | #define INCLUDED_SW_INC_CALBCK_HXX | ||||||||||
22 | |||||||||||
23 | #include <cassert> | ||||||||||
24 | |||||||||||
25 | #include <svl/hint.hxx> | ||||||||||
26 | #include <svl/broadcast.hxx> | ||||||||||
27 | #include <svl/poolitem.hxx> | ||||||||||
28 | #include "swdllapi.h" | ||||||||||
29 | #include "ring.hxx" | ||||||||||
30 | #include <type_traits> | ||||||||||
31 | #include <vector> | ||||||||||
32 | #include <memory> | ||||||||||
33 | |||||||||||
34 | class SwModify; | ||||||||||
35 | class SfxPoolItem; | ||||||||||
36 | |||||||||||
37 | /* | ||||||||||
38 | SwModify and SwClient cooperate in propagating attribute changes. | ||||||||||
39 | If an attribute changes, the change is notified to all dependent | ||||||||||
40 | formats and other interested objects, e.g. Nodes. The clients will detect | ||||||||||
41 | if the change affects them. It could be that the changed attribute is | ||||||||||
42 | overruled in the receiving object so that its change does not become | ||||||||||
43 | effective or that the receiver is not interested in the particular attribute | ||||||||||
44 | in general (though probably in other attributes of the SwModify object they | ||||||||||
45 | are registered in). | ||||||||||
46 | As SwModify objects are derived from SwClient, they can create a chain of SwClient | ||||||||||
47 | objects where changes can get propagated through. | ||||||||||
48 | Each SwClient can be registered at only one SwModify object, while each SwModify | ||||||||||
49 | object is connected to a list of SwClient objects. If an object derived from SwClient | ||||||||||
50 | wants to get notifications from more than one SwModify object, it must create additional | ||||||||||
51 | SwClient objects. The SwDepend class allows to handle their notifications in the same | ||||||||||
52 | notification callback as it forwards the Modify() calls it receives to a "master" | ||||||||||
53 | SwClient implementation. | ||||||||||
54 | The SwIterator class allows to iterate over the SwClient objects registered at an | ||||||||||
55 | SwModify. For historical reasons its ability to use TypeInfo to restrict this iteration | ||||||||||
56 | to objects of a particular type created a lot of code that misuses SwClient-SwModify | ||||||||||
57 | relationships that basically should be used only for Modify/Notify callbacks. | ||||||||||
58 | This is still subject to refactoring. | ||||||||||
59 | */ | ||||||||||
60 | |||||||||||
61 | namespace sw | ||||||||||
62 | { | ||||||||||
63 | class ClientIteratorBase; | ||||||||||
64 | struct LegacyModifyHint final: SfxHint | ||||||||||
65 | { | ||||||||||
66 | LegacyModifyHint(const SfxPoolItem* pOld, const SfxPoolItem* pNew) : m_pOld(pOld), m_pNew(pNew) {}; | ||||||||||
67 | sal_uInt16 GetWhich() const { return m_pOld ? m_pOld->Which() : m_pNew ? m_pNew->Which() : 0; }; | ||||||||||
68 | virtual ~LegacyModifyHint() override; | ||||||||||
69 | const SfxPoolItem* m_pOld; | ||||||||||
70 | const SfxPoolItem* m_pNew; | ||||||||||
71 | }; | ||||||||||
72 | struct ModifyChangedHint final: SfxHint | ||||||||||
73 | { | ||||||||||
74 | ModifyChangedHint(const SwModify* pNew) : m_pNew(pNew) {}; | ||||||||||
75 | virtual ~ModifyChangedHint() override; | ||||||||||
76 | const SwModify* m_pNew; | ||||||||||
77 | }; | ||||||||||
78 | // Observer pattern using svl implementation | ||||||||||
79 | // use this instead of SwClient/SwModify wherever possible | ||||||||||
80 | // In writer layout, this might not always be possible, | ||||||||||
81 | // but for listeners outside of it (e.g. unocore) this should be used. | ||||||||||
82 | // The only "magic" signal this class issues is a ModifyChangedHint | ||||||||||
83 | // proclaiming its death. It does NOT however provide a new SwModify for | ||||||||||
84 | // listeners to switch to like the old SwModify/SwClient did, as that leads | ||||||||||
85 | // to madness. | ||||||||||
86 | class SW_DLLPUBLIC__attribute__ ((visibility("default"))) BroadcasterMixin { | ||||||||||
87 | SvtBroadcaster m_aNotifier; | ||||||||||
88 | public: | ||||||||||
89 | BroadcasterMixin() = default; | ||||||||||
90 | BroadcasterMixin(BroadcasterMixin const &) = default; | ||||||||||
91 | BroadcasterMixin& operator=(const BroadcasterMixin&) | ||||||||||
92 | { | ||||||||||
93 | return *this; // Listeners are never copied or moved. | ||||||||||
94 | } | ||||||||||
95 | SvtBroadcaster& GetNotifier() { return m_aNotifier; } | ||||||||||
96 | }; | ||||||||||
97 | /// refactoring out the same of the more sane SwClient functionality | ||||||||||
98 | class SW_DLLPUBLIC__attribute__ ((visibility("default"))) WriterListener | ||||||||||
99 | { | ||||||||||
100 | friend class ::SwModify; | ||||||||||
101 | friend class ::sw::ClientIteratorBase; | ||||||||||
102 | private: | ||||||||||
103 | WriterListener* m_pLeft; | ||||||||||
104 | WriterListener* m_pRight; ///< double-linked list of other clients | ||||||||||
105 | |||||||||||
106 | WriterListener(WriterListener const&) = delete; | ||||||||||
107 | WriterListener& operator=(WriterListener const&) = delete; | ||||||||||
108 | |||||||||||
109 | protected: | ||||||||||
110 | WriterListener() | ||||||||||
111 | : m_pLeft(nullptr), m_pRight(nullptr) | ||||||||||
112 | {} | ||||||||||
113 | virtual ~WriterListener() COVERITY_NOEXCEPT_FALSE {} | ||||||||||
114 | virtual void SwClientNotify( const SwModify&, const SfxHint& rHint) =0; | ||||||||||
115 | public: | ||||||||||
116 | bool IsLast() const { return !m_pLeft && !m_pRight; } | ||||||||||
117 | }; | ||||||||||
118 | enum class IteratorMode { Exact, UnwrapMulti }; | ||||||||||
119 | } | ||||||||||
120 | |||||||||||
121 | // SwClient | ||||||||||
122 | class SW_DLLPUBLIC__attribute__ ((visibility("default"))) SwClient : public ::sw::WriterListener | ||||||||||
123 | { | ||||||||||
124 | // avoids making the details of the linked list and the callback method public | ||||||||||
125 | friend class SwModify; | ||||||||||
126 | friend class sw::ClientIteratorBase; | ||||||||||
127 | template<typename E, typename S, sw::IteratorMode> friend class SwIterator; | ||||||||||
128 | |||||||||||
129 | SwModify *m_pRegisteredIn; ///< event source | ||||||||||
130 | |||||||||||
131 | protected: | ||||||||||
132 | // single argument ctors shall be explicit. | ||||||||||
133 | inline explicit SwClient( SwModify* pToRegisterIn ); | ||||||||||
134 | |||||||||||
135 | // write access to pRegisteredIn shall be granted only to the object itself (protected access) | ||||||||||
136 | SwModify* GetRegisteredInNonConst() const { return m_pRegisteredIn; } | ||||||||||
137 | |||||||||||
138 | public: | ||||||||||
139 | SwClient() : m_pRegisteredIn(nullptr) {} | ||||||||||
140 | SwClient(SwClient&&) noexcept; | ||||||||||
141 | virtual ~SwClient() override; | ||||||||||
142 | // callbacks received from SwModify (friend class - so these methods can be private) | ||||||||||
143 | // should be called only from SwModify the client is registered in | ||||||||||
144 | // mba: IMHO this method should be pure virtual | ||||||||||
145 | // DO NOT USE IN NEW CODE! use SwClientNotify instead. | ||||||||||
146 | virtual void Modify(const SfxPoolItem* pOldValue, const SfxPoolItem* pNewValue); | ||||||||||
147 | // when overriding this, you MUST call SwClient::SwClientModify() in the override! | ||||||||||
148 | virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override; | ||||||||||
149 | |||||||||||
150 | // in case an SwModify object is destroyed that itself is registered in another SwModify, | ||||||||||
151 | // its SwClient objects can decide to get registered to the latter instead by calling this method | ||||||||||
152 | std::unique_ptr<sw::ModifyChangedHint> CheckRegistration( const SfxPoolItem* pOldValue ); | ||||||||||
153 | |||||||||||
154 | // controlled access to Modify method | ||||||||||
155 | // mba: this is still considered a hack and it should be fixed; the name makes grep-ing easier | ||||||||||
156 | virtual void ModifyNotification( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue ) { Modify ( pOldValue, pNewValue ); } | ||||||||||
157 | void SwClientNotifyCall( const SwModify& rModify, const SfxHint& rHint ) { SwClientNotify( rModify, rHint ); } | ||||||||||
158 | |||||||||||
159 | const SwModify* GetRegisteredIn() const { return m_pRegisteredIn; } | ||||||||||
160 | SwModify* GetRegisteredIn() { return m_pRegisteredIn; } | ||||||||||
161 | void EndListeningAll(); | ||||||||||
162 | void StartListeningToSameModifyAs(const SwClient&); | ||||||||||
163 | |||||||||||
164 | |||||||||||
165 | // get information about attribute | ||||||||||
166 | virtual bool GetInfo( SfxPoolItem& ) const { return true; } | ||||||||||
167 | }; | ||||||||||
168 | |||||||||||
169 | |||||||||||
170 | // SwModify | ||||||||||
171 | |||||||||||
172 | // class has a doubly linked list for dependencies | ||||||||||
173 | class SW_DLLPUBLIC__attribute__ ((visibility("default"))) SwModify: public SwClient | ||||||||||
174 | { | ||||||||||
175 | friend class sw::ClientIteratorBase; | ||||||||||
176 | template<typename E, typename S, sw::IteratorMode> friend class SwIterator; | ||||||||||
177 | sw::WriterListener* m_pWriterListeners; // the start of the linked list of clients | ||||||||||
178 | bool m_bModifyLocked : 1; // don't broadcast changes now | ||||||||||
179 | bool m_bLockClientList : 1; // may be set when this instance notifies its clients | ||||||||||
180 | bool m_bInCache : 1; | ||||||||||
181 | bool m_bInSwFntCache : 1; | ||||||||||
182 | |||||||||||
183 | // mba: IMHO this method should be pure virtual | ||||||||||
184 | // DO NOT USE IN NEW CODE! use CallSwClientNotify instead. | ||||||||||
185 | virtual void Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew) override | ||||||||||
186 | { NotifyClients( pOld, pNew ); }; | ||||||||||
187 | |||||||||||
188 | SwModify(SwModify const &) = delete; | ||||||||||
189 | SwModify &operator =(const SwModify&) = delete; | ||||||||||
190 | public: | ||||||||||
191 | SwModify() | ||||||||||
192 | : SwClient(), m_pWriterListeners(nullptr), m_bModifyLocked(false), m_bLockClientList(false), m_bInCache(false), m_bInSwFntCache(false) | ||||||||||
193 | {} | ||||||||||
194 | |||||||||||
195 | // broadcasting: send notifications to all clients | ||||||||||
196 | // DO NOT USE IN NEW CODE! use CallSwClientNotify instead. | ||||||||||
197 | void NotifyClients( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue ); | ||||||||||
198 | // the same, but without setting m_bModifyLocked or checking for any of the flags | ||||||||||
199 | // DO NOT USE IN NEW CODE! use CallSwClientNotify instead. | ||||||||||
200 | void ModifyBroadcast( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue) | ||||||||||
201 | { CallSwClientNotify( sw::LegacyModifyHint{ pOldValue, pNewValue } ); }; | ||||||||||
202 | |||||||||||
203 | // a more universal broadcasting mechanism | ||||||||||
204 | virtual void CallSwClientNotify( const SfxHint& rHint ) const; | ||||||||||
205 | |||||||||||
206 | virtual ~SwModify() override; | ||||||||||
207 | |||||||||||
208 | void Add(SwClient *pDepend); | ||||||||||
209 | SwClient* Remove(SwClient *pDepend); | ||||||||||
210 | bool HasWriterListeners() const { return m_pWriterListeners; } | ||||||||||
211 | |||||||||||
212 | // get information about attribute | ||||||||||
213 | virtual bool GetInfo( SfxPoolItem& ) const override; | ||||||||||
214 | |||||||||||
215 | void LockModify() { m_bModifyLocked = true; } | ||||||||||
216 | void UnlockModify() { m_bModifyLocked = false; } | ||||||||||
217 | void SetInCache( bool bNew ) { m_bInCache = bNew; } | ||||||||||
218 | void SetInSwFntCache( bool bNew ) { m_bInSwFntCache = bNew; } | ||||||||||
219 | void SetInDocDTOR(); | ||||||||||
220 | bool IsModifyLocked() const { return m_bModifyLocked; } | ||||||||||
221 | bool IsInCache() const { return m_bInCache; } | ||||||||||
222 | bool IsInSwFntCache() const { return m_bInSwFntCache; } | ||||||||||
223 | |||||||||||
224 | void CheckCaching( const sal_uInt16 nWhich ); | ||||||||||
225 | bool HasOnlyOneListener() const { return m_pWriterListeners && m_pWriterListeners->IsLast(); } | ||||||||||
226 | }; | ||||||||||
227 | |||||||||||
228 | template<typename TElementType, typename TSource, sw::IteratorMode eMode> class SwIterator; | ||||||||||
229 | |||||||||||
230 | namespace sw | ||||||||||
231 | { | ||||||||||
232 | // this class is part of the migration: it still forwards the "old" | ||||||||||
233 | // SwModify events and announces them both to the old SwClients still | ||||||||||
234 | // registered and also to the new SvtListeners. | ||||||||||
235 | // Still: in the long run the SwClient/SwModify interface should not be | ||||||||||
236 | // used anymore, in which case a BroadcasterMixin should be enough instead | ||||||||||
237 | // then. | ||||||||||
238 | class SW_DLLPUBLIC__attribute__ ((visibility("default"))) BroadcastingModify : public SwModify, public BroadcasterMixin { | ||||||||||
239 | public: | ||||||||||
240 | virtual void CallSwClientNotify(const SfxHint& rHint) const override; | ||||||||||
241 | }; | ||||||||||
242 | // this should be hidden but sadly SwIterator template needs it... | ||||||||||
243 | class ListenerEntry final : public SwClient | ||||||||||
244 | { | ||||||||||
245 | private: | ||||||||||
246 | template<typename E, typename S, sw::IteratorMode> friend class ::SwIterator; | ||||||||||
247 | SwClient *m_pToTell; | ||||||||||
248 | |||||||||||
249 | public: | ||||||||||
250 | ListenerEntry(SwClient *const pTellHim, SwModify *const pDepend) | ||||||||||
251 | : SwClient(pDepend), m_pToTell(pTellHim) | ||||||||||
252 | {} | ||||||||||
253 | ListenerEntry(ListenerEntry const &) = delete; | ||||||||||
254 | ListenerEntry& operator=(ListenerEntry const&) = delete; | ||||||||||
255 | ListenerEntry(ListenerEntry&& other) noexcept | ||||||||||
256 | : SwClient(std::move(other)) | ||||||||||
257 | , m_pToTell(other.m_pToTell) | ||||||||||
258 | { } | ||||||||||
259 | ListenerEntry& operator=(ListenerEntry&& other) noexcept | ||||||||||
260 | { | ||||||||||
261 | m_pToTell = other.m_pToTell; | ||||||||||
262 | other.GetRegisteredIn()->Add(this); | ||||||||||
263 | other.EndListeningAll(); | ||||||||||
264 | return *this; | ||||||||||
265 | } | ||||||||||
266 | |||||||||||
267 | /** get Client information */ | ||||||||||
268 | virtual bool GetInfo( SfxPoolItem& rInfo) const override; | ||||||||||
269 | private: | ||||||||||
270 | virtual void Modify(const SfxPoolItem* pOldValue, const SfxPoolItem *pNewValue) override; | ||||||||||
271 | virtual void SwClientNotify(const SwModify& rModify, const SfxHint& rHint) override; | ||||||||||
272 | }; | ||||||||||
273 | |||||||||||
274 | class SW_DLLPUBLIC__attribute__ ((visibility("default"))) WriterMultiListener final | ||||||||||
275 | { | ||||||||||
276 | SwClient& m_rToTell; | ||||||||||
277 | std::vector<ListenerEntry> m_vDepends; | ||||||||||
278 | public: | ||||||||||
279 | WriterMultiListener(SwClient& rToTell); | ||||||||||
280 | WriterMultiListener& operator=(WriterMultiListener const&) = delete; // MSVC2015 workaround | ||||||||||
281 | WriterMultiListener(WriterMultiListener const&) = delete; // MSVC2015 workaround | ||||||||||
282 | ~WriterMultiListener(); | ||||||||||
283 | void StartListening(SwModify* pDepend); | ||||||||||
284 | void EndListening(SwModify* pDepend); | ||||||||||
285 | bool IsListeningTo(const SwModify* const pDepend) const; | ||||||||||
286 | void EndListeningAll(); | ||||||||||
287 | }; | ||||||||||
288 | class ClientIteratorBase : public sw::Ring< ::sw::ClientIteratorBase > | ||||||||||
289 | { | ||||||||||
290 | friend SwClient* SwModify::Remove(SwClient*); | ||||||||||
291 | friend void SwModify::Add(SwClient*); | ||||||||||
292 | protected: | ||||||||||
293 | const SwModify& m_rRoot; | ||||||||||
294 | // the current object in an iteration | ||||||||||
295 | WriterListener* m_pCurrent; | ||||||||||
296 | // in case the current object is already removed, the next object in the list | ||||||||||
297 | // is marked down to become the current object in the next step | ||||||||||
298 | // this is necessary because iteration requires access to members of the current object | ||||||||||
299 | WriterListener* m_pPosition; | ||||||||||
300 | static SW_DLLPUBLIC__attribute__ ((visibility("default"))) ClientIteratorBase* s_pClientIters; | ||||||||||
301 | |||||||||||
302 | ClientIteratorBase( const SwModify& rModify ) | ||||||||||
303 | : m_rRoot(rModify) | ||||||||||
304 | { | ||||||||||
305 | MoveTo(s_pClientIters); | ||||||||||
306 | s_pClientIters = this; | ||||||||||
307 | m_pCurrent = m_pPosition = m_rRoot.m_pWriterListeners; | ||||||||||
308 | } | ||||||||||
309 | WriterListener* GetLeftOfPos() { return m_pPosition->m_pLeft; } | ||||||||||
310 | WriterListener* GetRightOfPos() { return m_pPosition->m_pRight; } | ||||||||||
311 | WriterListener* GoStart() | ||||||||||
312 | { | ||||||||||
313 | m_pPosition = m_rRoot.m_pWriterListeners; | ||||||||||
314 | if(m_pPosition) | ||||||||||
315 | while( m_pPosition->m_pLeft ) | ||||||||||
316 | m_pPosition = m_pPosition->m_pLeft; | ||||||||||
317 | m_pCurrent = m_pPosition; | ||||||||||
318 | return m_pCurrent; | ||||||||||
319 | } | ||||||||||
320 | ~ClientIteratorBase() override | ||||||||||
321 | { | ||||||||||
322 | assert(s_pClientIters)(static_cast <bool> (s_pClientIters) ? void (0) : __assert_fail ("s_pClientIters", "/home/maarten/src/libreoffice/core/sw/inc/calbck.hxx" , 322, __extension__ __PRETTY_FUNCTION__)); | ||||||||||
323 | if(s_pClientIters == this) | ||||||||||
324 | s_pClientIters = unique() ? nullptr : GetNextInRing(); | ||||||||||
325 | MoveTo(nullptr); | ||||||||||
326 | } | ||||||||||
327 | // return "true" if an object was removed from a client chain in iteration | ||||||||||
328 | // adding objects to a client chain in iteration is forbidden | ||||||||||
329 | // SwModify::Add() asserts this | ||||||||||
330 | bool IsChanged() const { return m_pPosition != m_pCurrent; } | ||||||||||
331 | // ensures the iterator to point at a current client | ||||||||||
332 | WriterListener* Sync() { m_pCurrent = m_pPosition; return m_pCurrent; } | ||||||||||
333 | }; | ||||||||||
334 | } | ||||||||||
335 | |||||||||||
336 | template<typename TElementType, typename TSource, | ||||||||||
337 | sw::IteratorMode eMode = sw::IteratorMode::Exact> class SwIterator final | ||||||||||
338 | : private sw::ClientIteratorBase | ||||||||||
339 | { | ||||||||||
340 | //static_assert(!std::is_base_of<SwPageDesc,TSource>::value, "SwPageDesc as TSource is deprecated."); | ||||||||||
341 | static_assert(std::is_base_of<SwClient,TElementType>::value, "TElementType needs to be derived from SwClient."); | ||||||||||
342 | static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify."); | ||||||||||
343 | public: | ||||||||||
344 | SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {} | ||||||||||
345 | TElementType* First() | ||||||||||
346 | { | ||||||||||
347 | GoStart(); | ||||||||||
348 | if(!m_pPosition) | ||||||||||
349 | return nullptr; | ||||||||||
350 | m_pCurrent = nullptr; | ||||||||||
351 | return Next(); | ||||||||||
352 | } | ||||||||||
353 | TElementType* Next() | ||||||||||
354 | { | ||||||||||
355 | if(!IsChanged()) | ||||||||||
356 | m_pPosition = GetRightOfPos(); | ||||||||||
357 | sw::WriterListener *pCurrent(m_pPosition); | ||||||||||
358 | while (m_pPosition) | ||||||||||
359 | { | ||||||||||
360 | if (eMode
| ||||||||||
361 | { | ||||||||||
362 | if (auto const pLE = dynamic_cast<sw::ListenerEntry const*>(m_pPosition)) | ||||||||||
363 | { | ||||||||||
364 | pCurrent = pLE->m_pToTell; | ||||||||||
365 | } | ||||||||||
366 | } | ||||||||||
367 | if (dynamic_cast<const TElementType *>(pCurrent) == nullptr) | ||||||||||
368 | { | ||||||||||
369 | m_pPosition = GetRightOfPos(); | ||||||||||
370 | pCurrent = m_pPosition; | ||||||||||
371 | } | ||||||||||
372 | else | ||||||||||
373 | break; | ||||||||||
374 | } | ||||||||||
375 | Sync(); | ||||||||||
376 | return static_cast<TElementType*>(pCurrent); | ||||||||||
377 | } | ||||||||||
378 | using sw::ClientIteratorBase::IsChanged; | ||||||||||
379 | }; | ||||||||||
380 | |||||||||||
381 | template< typename TSource > class SwIterator<SwClient, TSource> final : private sw::ClientIteratorBase | ||||||||||
382 | { | ||||||||||
383 | static_assert(std::is_base_of<SwModify,TSource>::value, "TSource needs to be derived from SwModify"); | ||||||||||
384 | public: | ||||||||||
385 | SwIterator( const TSource& rSrc ) : sw::ClientIteratorBase(rSrc) {} | ||||||||||
386 | SwClient* First() | ||||||||||
387 | { return static_cast<SwClient*>(GoStart()); } | ||||||||||
388 | SwClient* Next() | ||||||||||
389 | { | ||||||||||
390 | if(!IsChanged()) | ||||||||||
391 | m_pPosition = GetRightOfPos(); | ||||||||||
392 | return static_cast<SwClient*>(Sync()); | ||||||||||
393 | } | ||||||||||
394 | using sw::ClientIteratorBase::IsChanged; | ||||||||||
395 | }; | ||||||||||
396 | |||||||||||
397 | SwClient::SwClient( SwModify* pToRegisterIn ) | ||||||||||
398 | : m_pRegisteredIn( nullptr ) | ||||||||||
399 | { | ||||||||||
400 | if(pToRegisterIn) | ||||||||||
401 | pToRegisterIn->Add(this); | ||||||||||
402 | } | ||||||||||
403 | |||||||||||
404 | #endif | ||||||||||
405 | |||||||||||
406 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |
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 | #ifndef INCLUDED_SW_INC_RING_HXX |
20 | #define INCLUDED_SW_INC_RING_HXX |
21 | |
22 | #include <utility> |
23 | #include <sal/types.h> |
24 | #include <iterator> |
25 | #include <type_traits> |
26 | #include <boost/iterator/iterator_facade.hpp> |
27 | #include <boost/intrusive/circular_list_algorithms.hpp> |
28 | |
29 | namespace sw |
30 | { |
31 | template <typename value_type> class RingContainer; |
32 | template <typename value_type> class RingIterator; |
33 | /** |
34 | * An intrusive container class double linking the contained nodes |
35 | * @example sw/qa/core/uwriter.cxx |
36 | */ |
37 | template <typename value_type> |
38 | class SAL_WARN_UNUSED__attribute__((warn_unused)) Ring |
39 | { |
40 | public: |
41 | typedef typename std::add_const<value_type>::type const_value_type; |
42 | typedef RingContainer<value_type> ring_container; |
43 | typedef RingContainer<const_value_type> const_ring_container; |
44 | virtual ~Ring() COVERITY_NOEXCEPT_FALSE |
45 | { unlink(); }; |
46 | /** algo::unlink is buggy! don't call it directly! */ |
47 | void unlink() |
48 | { |
49 | algo::unlink(this); |
50 | m_pNext = this; // don't leave pointers to old list behind! |
51 | m_pPrev = this; |
52 | } |
53 | /** |
54 | * Removes this item from its current ring container and adds it to |
55 | * another ring container. If the item was not alone in the original |
56 | * ring container, the other items in the ring will stay in the old |
57 | * ring container. |
58 | * Note: the newly created item will be inserted just before item pDestRing. |
59 | * @param pDestRing the container to add this item to |
60 | */ |
61 | void MoveTo( value_type* pDestRing ); |
62 | /** @return a stl-like container with begin()/end() for iteration */ |
63 | ring_container GetRingContainer(); |
64 | /** @return a stl-like container with begin()/end() for const iteration */ |
65 | const_ring_container GetRingContainer() const; |
66 | |
67 | protected: |
68 | /** |
69 | * Creates a new item in a ring container all by itself. |
70 | * Note: Ring instances can newer be outside a container. At most, they |
71 | * are alone in one. |
72 | */ |
73 | Ring() |
74 | : m_pNext(this) |
75 | , m_pPrev(this) |
76 | { } |
77 | /** |
78 | * Creates a new item and add it to an existing ring container. |
79 | * Note: the newly created item will be inserted just before item pRing. |
80 | * @param pRing ring container to add the created item to |
81 | */ |
82 | Ring( value_type* pRing ); |
83 | /** @return the next item in the ring container */ |
84 | value_type* GetNextInRing() |
85 | { return static_cast<value_type *>(m_pNext); } |
86 | /** @return the previous item in the ring container */ |
87 | value_type* GetPrevInRing() |
88 | { return static_cast<value_type *>(m_pPrev); } |
89 | /** @return the next item in the ring container */ |
90 | const_value_type* GetNextInRing() const |
91 | { return static_cast<value_type *>(m_pNext); } |
92 | /** @return the previous item in the ring container */ |
93 | const_value_type* GetPrevInRing() const |
94 | { return static_cast<value_type *>(m_pPrev); } |
95 | /** @return true if and only if this item is alone in its ring */ |
96 | bool unique() const |
97 | { return algo::unique(static_cast< const_value_type* >(this)); } |
98 | |
99 | private: |
100 | /** internal implementation class -- not for external use */ |
101 | struct Ring_node_traits |
102 | { |
103 | typedef Ring node; |
104 | typedef Ring* node_ptr; |
105 | typedef const Ring* const_node_ptr; |
106 | static node_ptr get_next(const_node_ptr n) { return const_cast<node_ptr>(n)->m_pNext; }; |
107 | static void set_next(node_ptr n, node_ptr next) { n->m_pNext = next; }; |
108 | static node_ptr get_previous(const_node_ptr n) { return const_cast<node_ptr>(n)->m_pPrev; }; |
109 | static void set_previous(node_ptr n, node_ptr previous) { n->m_pPrev = previous; }; |
110 | }; |
111 | friend ring_container; |
112 | friend const_ring_container; |
113 | friend typename ring_container::iterator; |
114 | friend typename ring_container::const_iterator; |
115 | friend typename const_ring_container::iterator; |
116 | friend typename const_ring_container::const_iterator; |
117 | friend class boost::iterator_core_access; |
118 | typedef boost::intrusive::circular_list_algorithms<Ring_node_traits> algo; |
119 | Ring* m_pNext; |
120 | Ring* m_pPrev; |
121 | }; |
122 | |
123 | template <typename value_type> |
124 | inline Ring<value_type>::Ring( value_type* pObj ) |
125 | : m_pNext(this) |
126 | , m_pPrev(this) |
127 | { |
128 | if( pObj ) |
129 | { |
130 | algo::link_before(pObj, this); |
131 | } |
132 | } |
133 | |
134 | template <typename value_type> |
135 | inline void Ring<value_type>::MoveTo(value_type* pDestRing) |
136 | { |
137 | value_type* pThis = static_cast< value_type* >(this); |
138 | unlink(); |
139 | // insert into "new" |
140 | if (pDestRing) |
141 | { |
142 | algo::link_before(pDestRing, pThis); |
143 | } |
144 | } |
145 | |
146 | /** |
147 | * helper class that provides Svalue_typeL-style container iteration to the ring |
148 | */ |
149 | template <typename value_type> |
150 | class SAL_WARN_UNUSED__attribute__((warn_unused)) RingContainer final |
151 | { |
152 | private: |
153 | /** the item in the ring where iteration starts */ |
154 | value_type* m_pStart; |
155 | typedef typename std::remove_const<value_type>::type nonconst_value_type; |
156 | |
157 | public: |
158 | RingContainer( value_type* pRing ) : m_pStart(pRing) {}; |
159 | typedef RingIterator<value_type> iterator; |
160 | typedef RingIterator<const value_type> const_iterator; |
161 | /** |
162 | * iterator access |
163 | * @code |
164 | * for(SwPaM& rCurrentPaM : pPaM->GetRingContainer()) |
165 | * do_stuff(rCurrentPaM); // this gets called on every SwPaM in the same ring as pPaM |
166 | * @endcode |
167 | */ |
168 | iterator begin(); |
169 | iterator end(); |
170 | const_iterator begin() const; |
171 | const_iterator end() const; |
172 | /** @return the number of elements in the container */ |
173 | size_t size() const |
174 | { return std::distance(begin(), end()); } |
175 | /** |
176 | * Merges two ring containers. All item from both ring containers will |
177 | * be in the same ring container in the end. |
178 | * Note: The items of this ring container will be inserted just before |
179 | * item pDestRing |
180 | * @param pDestRing the container to merge this container with |
181 | */ |
182 | void merge( RingContainer< value_type > aDestRing ) |
183 | { |
184 | // first check that we aren't merged already, swapping would |
185 | // actually un-merge in this case! |
186 | assert(m_pStart->m_pPrev != aDestRing.m_pStart)(static_cast <bool> (m_pStart->m_pPrev != aDestRing. m_pStart) ? void (0) : __assert_fail ("m_pStart->m_pPrev != aDestRing.m_pStart" , "/home/maarten/src/libreoffice/core/sw/inc/ring.hxx", 186, __extension__ __PRETTY_FUNCTION__)); |
187 | assert(m_pStart != aDestRing.m_pStart->m_pPrev)(static_cast <bool> (m_pStart != aDestRing.m_pStart-> m_pPrev) ? void (0) : __assert_fail ("m_pStart != aDestRing.m_pStart->m_pPrev" , "/home/maarten/src/libreoffice/core/sw/inc/ring.hxx", 187, __extension__ __PRETTY_FUNCTION__)); |
188 | std::swap(m_pStart->m_pPrev->m_pNext, aDestRing.m_pStart->m_pPrev->m_pNext); |
189 | std::swap(m_pStart->m_pPrev, aDestRing.m_pStart->m_pPrev); |
190 | } |
191 | }; |
192 | |
193 | template <typename value_type> |
194 | class RingIterator final : public boost::iterator_facade< |
195 | RingIterator<value_type> |
196 | , value_type |
197 | , boost::forward_traversal_tag |
198 | > |
199 | { |
200 | private: |
201 | typedef typename std::remove_const<value_type>::type nonconst_value_type; |
202 | public: |
203 | RingIterator() |
204 | : m_pCurrent(nullptr) |
205 | , m_pStart(nullptr) |
206 | {} |
207 | explicit RingIterator(nonconst_value_type* pRing, bool bStart = true) |
208 | : m_pCurrent(nullptr) |
209 | , m_pStart(pRing) |
210 | { |
211 | if(!bStart) |
212 | m_pCurrent = m_pStart; |
213 | } |
214 | |
215 | private: |
216 | friend class boost::iterator_core_access; |
217 | void increment() |
218 | { m_pCurrent = m_pCurrent ? m_pCurrent->GetNextInRing() : m_pStart->GetNextInRing(); } |
219 | bool equal(RingIterator const& other) const |
220 | { |
221 | // we never want to compare iterators from |
222 | // different rings or starting points |
223 | assert(m_pStart == other.m_pStart)(static_cast <bool> (m_pStart == other.m_pStart) ? void (0) : __assert_fail ("m_pStart == other.m_pStart", "/home/maarten/src/libreoffice/core/sw/inc/ring.hxx" , 223, __extension__ __PRETTY_FUNCTION__)); |
224 | return m_pCurrent == other.m_pCurrent; |
225 | } |
226 | value_type& dereference() const |
227 | { return m_pCurrent ? *m_pCurrent : * m_pStart; } |
228 | /** |
229 | * value_type is: |
230 | * - pointing to the current item in the iteration in general |
231 | * - nullptr if on the first item (begin()) |
232 | * - m_pStart when beyond the last item (end()) |
233 | */ |
234 | nonconst_value_type* m_pCurrent; |
235 | /** the first item of the iteration */ |
236 | nonconst_value_type* m_pStart; |
237 | }; |
238 | |
239 | template <typename value_type> |
240 | inline typename Ring<value_type>::ring_container Ring<value_type>::GetRingContainer() |
241 | { return Ring<value_type>::ring_container(static_cast< value_type* >(this)); }; |
242 | |
243 | template <typename value_type> |
244 | inline typename Ring<value_type>::const_ring_container Ring<value_type>::GetRingContainer() const |
245 | { return Ring<value_type>::const_ring_container(static_cast< const_value_type* >(this)); }; |
246 | |
247 | template <typename value_type> |
248 | inline typename RingContainer<value_type>::iterator RingContainer<value_type>::begin() |
249 | { return RingContainer<value_type>::iterator(const_cast< nonconst_value_type* >(m_pStart)); }; |
250 | |
251 | template <typename value_type> |
252 | inline typename RingContainer<value_type>::iterator RingContainer<value_type>::end() |
253 | { return RingContainer<value_type>::iterator(const_cast< nonconst_value_type* >(m_pStart), false); }; |
254 | |
255 | template <typename value_type> |
256 | inline typename RingContainer<value_type>::const_iterator RingContainer<value_type>::begin() const |
257 | { return RingContainer<value_type>::const_iterator(const_cast< nonconst_value_type* >(m_pStart)); }; |
258 | |
259 | template <typename value_type> |
260 | inline typename RingContainer<value_type>::const_iterator RingContainer<value_type>::end() const |
261 | { return RingContainer<value_type>::const_iterator(const_cast< nonconst_value_type* >(m_pStart), false); }; |
262 | } |
263 | #endif |
264 | |
265 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |
1 | ///////////////////////////////////////////////////////////////////////////// |
2 | // |
3 | // (C) Copyright Olaf Krzikalla 2004-2006. |
4 | // (C) Copyright Ion Gaztanaga 2006-2014 |
5 | // |
6 | // Distributed under the Boost Software License, Version 1.0. |
7 | // (See accompanying file LICENSE_1_0.txt or copy at |
8 | // http://www.boost.org/LICENSE_1_0.txt) |
9 | // |
10 | // See http://www.boost.org/libs/intrusive for documentation. |
11 | // |
12 | ///////////////////////////////////////////////////////////////////////////// |
13 | |
14 | #ifndef BOOST_INTRUSIVE_CIRCULAR_LIST_ALGORITHMS_HPP |
15 | #define BOOST_INTRUSIVE_CIRCULAR_LIST_ALGORITHMS_HPP |
16 | |
17 | #include <boost/intrusive/detail/config_begin.hpp> |
18 | #include <boost/intrusive/intrusive_fwd.hpp> |
19 | #include <boost/intrusive/detail/algo_type.hpp> |
20 | #include <boost/core/no_exceptions_support.hpp> |
21 | #include <cstddef> |
22 | |
23 | #if defined(BOOST_HAS_PRAGMA_ONCE) |
24 | # pragma once |
25 | #endif |
26 | |
27 | namespace boost { |
28 | namespace intrusive { |
29 | |
30 | //! circular_list_algorithms provides basic algorithms to manipulate nodes |
31 | //! forming a circular doubly linked list. An empty circular list is formed by a node |
32 | //! whose pointers point to itself. |
33 | //! |
34 | //! circular_list_algorithms is configured with a NodeTraits class, which encapsulates the |
35 | //! information about the node to be manipulated. NodeTraits must support the |
36 | //! following interface: |
37 | //! |
38 | //! <b>Typedefs</b>: |
39 | //! |
40 | //! <tt>node</tt>: The type of the node that forms the circular list |
41 | //! |
42 | //! <tt>node_ptr</tt>: A pointer to a node |
43 | //! |
44 | //! <tt>const_node_ptr</tt>: A pointer to a const node |
45 | //! |
46 | //! <b>Static functions</b>: |
47 | //! |
48 | //! <tt>static node_ptr get_previous(const_node_ptr n);</tt> |
49 | //! |
50 | //! <tt>static void set_previous(node_ptr n, node_ptr prev);</tt> |
51 | //! |
52 | //! <tt>static node_ptr get_next(const_node_ptr n);</tt> |
53 | //! |
54 | //! <tt>static void set_next(node_ptr n, node_ptr next);</tt> |
55 | template<class NodeTraits> |
56 | class circular_list_algorithms |
57 | { |
58 | public: |
59 | typedef typename NodeTraits::node node; |
60 | typedef typename NodeTraits::node_ptr node_ptr; |
61 | typedef typename NodeTraits::const_node_ptr const_node_ptr; |
62 | typedef NodeTraits node_traits; |
63 | |
64 | //! <b>Effects</b>: Constructs an non-used list element, so that |
65 | //! inited(this_node) == true |
66 | //! |
67 | //! <b>Complexity</b>: Constant |
68 | //! |
69 | //! <b>Throws</b>: Nothing. |
70 | BOOST_INTRUSIVE_FORCEINLINEinline static void init(node_ptr this_node) |
71 | { |
72 | const node_ptr null_node = node_ptr(); |
73 | NodeTraits::set_next(this_node, null_node); |
74 | NodeTraits::set_previous(this_node, null_node); |
75 | } |
76 | |
77 | //! <b>Effects</b>: Returns true is "this_node" is in a non-used state |
78 | //! as if it was initialized by the "init" function. |
79 | //! |
80 | //! <b>Complexity</b>: Constant |
81 | //! |
82 | //! <b>Throws</b>: Nothing. |
83 | BOOST_INTRUSIVE_FORCEINLINEinline static bool inited(const const_node_ptr &this_node) |
84 | { return !NodeTraits::get_next(this_node); } |
85 | |
86 | //! <b>Effects</b>: Constructs an empty list, making this_node the only |
87 | //! node of the circular list: |
88 | //! <tt>NodeTraits::get_next(this_node) == NodeTraits::get_previous(this_node) |
89 | //! == this_node</tt>. |
90 | //! |
91 | //! <b>Complexity</b>: Constant |
92 | //! |
93 | //! <b>Throws</b>: Nothing. |
94 | BOOST_INTRUSIVE_FORCEINLINEinline static void init_header(node_ptr this_node) |
95 | { |
96 | NodeTraits::set_next(this_node, this_node); |
97 | NodeTraits::set_previous(this_node, this_node); |
98 | } |
99 | |
100 | |
101 | //! <b>Requires</b>: this_node must be in a circular list or be an empty circular list. |
102 | //! |
103 | //! <b>Effects</b>: Returns true is "this_node" is the only node of a circular list: |
104 | //! <tt>return NodeTraits::get_next(this_node) == this_node</tt> |
105 | //! |
106 | //! <b>Complexity</b>: Constant |
107 | //! |
108 | //! <b>Throws</b>: Nothing. |
109 | BOOST_INTRUSIVE_FORCEINLINEinline static bool unique(const const_node_ptr &this_node) |
110 | { |
111 | node_ptr next = NodeTraits::get_next(this_node); |
112 | return !next || next == this_node; |
113 | } |
114 | |
115 | //! <b>Requires</b>: this_node must be in a circular list or be an empty circular list. |
116 | //! |
117 | //! <b>Effects</b>: Returns the number of nodes in a circular list. If the circular list |
118 | //! is empty, returns 1. |
119 | //! |
120 | //! <b>Complexity</b>: Linear |
121 | //! |
122 | //! <b>Throws</b>: Nothing. |
123 | static std::size_t count(const const_node_ptr &this_node) |
124 | { |
125 | std::size_t result = 0; |
126 | const_node_ptr p = this_node; |
127 | do{ |
128 | p = NodeTraits::get_next(p); |
129 | ++result; |
130 | }while (p != this_node); |
131 | return result; |
132 | } |
133 | |
134 | //! <b>Requires</b>: this_node must be in a circular list or be an empty circular list. |
135 | //! |
136 | //! <b>Effects</b>: Unlinks the node from the circular list. |
137 | //! |
138 | //! <b>Complexity</b>: Constant |
139 | //! |
140 | //! <b>Throws</b>: Nothing. |
141 | BOOST_INTRUSIVE_FORCEINLINEinline static node_ptr unlink(node_ptr this_node) |
142 | { |
143 | node_ptr next(NodeTraits::get_next(this_node)); |
144 | node_ptr prev(NodeTraits::get_previous(this_node)); |
145 | NodeTraits::set_next(prev, next); |
146 | NodeTraits::set_previous(next, prev); |
147 | return next; |
148 | } |
149 | |
150 | //! <b>Requires</b>: b and e must be nodes of the same circular list or an empty range. |
151 | //! |
152 | //! <b>Effects</b>: Unlinks the node [b, e) from the circular list. |
153 | //! |
154 | //! <b>Complexity</b>: Constant |
155 | //! |
156 | //! <b>Throws</b>: Nothing. |
157 | BOOST_INTRUSIVE_FORCEINLINEinline static void unlink(node_ptr b, node_ptr e) |
158 | { |
159 | if (b != e) { |
160 | node_ptr prevb(NodeTraits::get_previous(b)); |
161 | NodeTraits::set_previous(e, prevb); |
162 | NodeTraits::set_next(prevb, e); |
163 | } |
164 | } |
165 | |
166 | //! <b>Requires</b>: nxt_node must be a node of a circular list. |
167 | //! |
168 | //! <b>Effects</b>: Links this_node before nxt_node in the circular list. |
169 | //! |
170 | //! <b>Complexity</b>: Constant |
171 | //! |
172 | //! <b>Throws</b>: Nothing. |
173 | BOOST_INTRUSIVE_FORCEINLINEinline static void link_before(node_ptr nxt_node, node_ptr this_node) |
174 | { |
175 | node_ptr prev(NodeTraits::get_previous(nxt_node)); |
176 | NodeTraits::set_previous(this_node, prev); |
177 | NodeTraits::set_next(this_node, nxt_node); |
178 | //nxt_node might be an alias for prev->next_ |
179 | //so use it before NodeTraits::set_next(prev, ...) |
180 | //is called and the reference changes its value |
181 | NodeTraits::set_previous(nxt_node, this_node); |
182 | NodeTraits::set_next(prev, this_node); |
183 | } |
184 | |
185 | //! <b>Requires</b>: prev_node must be a node of a circular list. |
186 | //! |
187 | //! <b>Effects</b>: Links this_node after prev_node in the circular list. |
188 | //! |
189 | //! <b>Complexity</b>: Constant |
190 | //! |
191 | //! <b>Throws</b>: Nothing. |
192 | BOOST_INTRUSIVE_FORCEINLINEinline static void link_after(node_ptr prev_node, node_ptr this_node) |
193 | { |
194 | node_ptr next(NodeTraits::get_next(prev_node)); |
195 | NodeTraits::set_previous(this_node, prev_node); |
196 | NodeTraits::set_next(this_node, next); |
197 | //prev_node might be an alias for next->next_ |
198 | //so use it before update it before NodeTraits::set_previous(next, ...) |
199 | //is called and the reference changes it's value |
200 | NodeTraits::set_next(prev_node, this_node); |
201 | NodeTraits::set_previous(next, this_node); |
202 | } |
203 | |
204 | //! <b>Requires</b>: this_node and other_node must be nodes inserted |
205 | //! in circular lists or be empty circular lists. |
206 | //! |
207 | //! <b>Effects</b>: Swaps the position of the nodes: this_node is inserted in |
208 | //! other_nodes position in the second circular list and the other_node is inserted |
209 | //! in this_node's position in the first circular list. |
210 | //! |
211 | //! <b>Complexity</b>: Constant |
212 | //! |
213 | //! <b>Throws</b>: Nothing. |
214 | static void swap_nodes(node_ptr this_node, node_ptr other_node) |
215 | { |
216 | if (other_node == this_node) |
217 | return; |
218 | bool this_inited = inited(this_node); |
219 | bool other_inited = inited(other_node); |
220 | if(this_inited){ |
221 | init_header(this_node); |
222 | } |
223 | if(other_inited){ |
224 | init_header(other_node); |
225 | } |
226 | |
227 | node_ptr next_this(NodeTraits::get_next(this_node)); |
228 | node_ptr prev_this(NodeTraits::get_previous(this_node)); |
229 | node_ptr next_other(NodeTraits::get_next(other_node)); |
230 | node_ptr prev_other(NodeTraits::get_previous(other_node)); |
231 | //these first two swaps must happen before the other two |
232 | swap_prev(next_this, next_other); |
233 | swap_next(prev_this, prev_other); |
234 | swap_next(this_node, other_node); |
235 | swap_prev(this_node, other_node); |
236 | |
237 | if(this_inited){ |
238 | init(other_node); |
239 | } |
240 | if(other_inited){ |
241 | init(this_node); |
242 | } |
243 | } |
244 | |
245 | //! <b>Requires</b>: b and e must be nodes of the same circular list or an empty range. |
246 | //! and p must be a node of a different circular list or may not be an iterator in |
247 | // [b, e). |
248 | //! |
249 | //! <b>Effects</b>: Removes the nodes from [b, e) range from their circular list and inserts |
250 | //! them before p in p's circular list. |
251 | //! |
252 | //! <b>Complexity</b>: Constant |
253 | //! |
254 | //! <b>Throws</b>: Nothing. |
255 | static void transfer(node_ptr p, node_ptr b, node_ptr e) |
256 | { |
257 | if (b != e) { |
258 | node_ptr prev_p(NodeTraits::get_previous(p)); |
259 | node_ptr prev_b(NodeTraits::get_previous(b)); |
260 | node_ptr prev_e(NodeTraits::get_previous(e)); |
261 | NodeTraits::set_next(prev_e, p); |
262 | NodeTraits::set_previous(p, prev_e); |
263 | NodeTraits::set_next(prev_b, e); |
264 | NodeTraits::set_previous(e, prev_b); |
265 | NodeTraits::set_next(prev_p, b); |
266 | NodeTraits::set_previous(b, prev_p); |
267 | } |
268 | } |
269 | |
270 | //! <b>Requires</b>: i must a node of a circular list |
271 | //! and p must be a node of a different circular list. |
272 | //! |
273 | //! <b>Effects</b>: Removes the node i from its circular list and inserts |
274 | //! it before p in p's circular list. |
275 | //! If p == i or p == NodeTraits::get_next(i), this function is a null operation. |
276 | //! |
277 | //! <b>Complexity</b>: Constant |
278 | //! |
279 | //! <b>Throws</b>: Nothing. |
280 | static void transfer(node_ptr p, node_ptr i) |
281 | { |
282 | node_ptr n(NodeTraits::get_next(i)); |
283 | if(n != p && i != p){ |
284 | node_ptr prev_p(NodeTraits::get_previous(p)); |
285 | node_ptr prev_i(NodeTraits::get_previous(i)); |
286 | NodeTraits::set_next(prev_p, i); |
287 | NodeTraits::set_previous(i, prev_p); |
288 | NodeTraits::set_next(i, p); |
289 | NodeTraits::set_previous(p, i); |
290 | NodeTraits::set_previous(n, prev_i); |
291 | NodeTraits::set_next(prev_i, n); |
292 | |
293 | } |
294 | } |
295 | |
296 | //! <b>Effects</b>: Reverses the order of elements in the list. |
297 | //! |
298 | //! <b>Throws</b>: Nothing. |
299 | //! |
300 | //! <b>Complexity</b>: This function is linear time. |
301 | static void reverse(node_ptr p) |
302 | { |
303 | node_ptr f(NodeTraits::get_next(p)); |
304 | node_ptr i(NodeTraits::get_next(f)), e(p); |
305 | |
306 | while(i != e) { |
307 | node_ptr n = i; |
308 | i = NodeTraits::get_next(i); |
309 | transfer(f, n, i); |
310 | f = n; |
311 | } |
312 | } |
313 | |
314 | //! <b>Effects</b>: Moves the node p n positions towards the end of the list. |
315 | //! |
316 | //! <b>Throws</b>: Nothing. |
317 | //! |
318 | //! <b>Complexity</b>: Linear to the number of moved positions. |
319 | static void move_backwards(node_ptr p, std::size_t n) |
320 | { |
321 | //Null shift, nothing to do |
322 | if(!n) return; |
323 | node_ptr first = NodeTraits::get_next(p); |
324 | //size() == 0 or 1, nothing to do |
325 | if(first == NodeTraits::get_previous(p)) return; |
326 | unlink(p); |
327 | //Now get the new first node |
328 | while(n--){ |
329 | first = NodeTraits::get_next(first); |
330 | } |
331 | link_before(first, p); |
332 | } |
333 | |
334 | //! <b>Effects</b>: Moves the node p n positions towards the beginning of the list. |
335 | //! |
336 | //! <b>Throws</b>: Nothing. |
337 | //! |
338 | //! <b>Complexity</b>: Linear to the number of moved positions. |
339 | static void move_forward(node_ptr p, std::size_t n) |
340 | { |
341 | //Null shift, nothing to do |
342 | if(!n) return; |
343 | node_ptr last = NodeTraits::get_previous(p); |
344 | //size() == 0 or 1, nothing to do |
345 | if(last == NodeTraits::get_next(p)) return; |
346 | |
347 | unlink(p); |
348 | //Now get the new last node |
349 | while(n--){ |
350 | last = NodeTraits::get_previous(last); |
351 | } |
352 | link_after(last, p); |
353 | } |
354 | |
355 | //! <b>Requires</b>: f and l must be in a circular list. |
356 | //! |
357 | //! <b>Effects</b>: Returns the number of nodes in the range [f, l). |
358 | //! |
359 | //! <b>Complexity</b>: Linear |
360 | //! |
361 | //! <b>Throws</b>: Nothing. |
362 | static std::size_t distance(const const_node_ptr &f, const const_node_ptr &l) |
363 | { |
364 | const_node_ptr i(f); |
365 | std::size_t result = 0; |
366 | while(i != l){ |
367 | i = NodeTraits::get_next(i); |
368 | ++result; |
369 | } |
370 | return result; |
371 | } |
372 | |
373 | struct stable_partition_info |
374 | { |
375 | std::size_t num_1st_partition; |
376 | std::size_t num_2nd_partition; |
377 | node_ptr beg_2st_partition; |
378 | }; |
379 | |
380 | template<class Pred> |
381 | static void stable_partition(node_ptr beg, node_ptr end, Pred pred, stable_partition_info &info) |
382 | { |
383 | node_ptr bcur = node_traits::get_previous(beg); |
384 | node_ptr cur = beg; |
385 | node_ptr new_f = end; |
386 | |
387 | std::size_t num1 = 0, num2 = 0; |
388 | while(cur != end){ |
389 | if(pred(cur)){ |
390 | ++num1; |
391 | bcur = cur; |
392 | cur = node_traits::get_next(cur); |
393 | } |
394 | else{ |
395 | ++num2; |
396 | node_ptr last_to_remove = bcur; |
397 | new_f = cur; |
398 | bcur = cur; |
399 | cur = node_traits::get_next(cur); |
400 | BOOST_TRY{ try{ |
401 | //Main loop |
402 | while(cur != end){ |
403 | if(pred(cur)){ //Might throw |
404 | ++num1; |
405 | //Process current node |
406 | node_traits::set_next (last_to_remove, cur); |
407 | node_traits::set_previous(cur, last_to_remove); |
408 | last_to_remove = cur; |
409 | node_ptr nxt = node_traits::get_next(cur); |
410 | node_traits::set_next (bcur, nxt); |
411 | node_traits::set_previous(nxt, bcur); |
412 | cur = nxt; |
413 | } |
414 | else{ |
415 | ++num2; |
416 | bcur = cur; |
417 | cur = node_traits::get_next(cur); |
418 | } |
419 | } |
420 | } |
421 | BOOST_CATCH(...)catch(...){ |
422 | node_traits::set_next (last_to_remove, new_f); |
423 | node_traits::set_previous(new_f, last_to_remove); |
424 | BOOST_RETHROWthrow;; |
425 | } |
426 | BOOST_CATCH_END} |
427 | node_traits::set_next(last_to_remove, new_f); |
428 | node_traits::set_previous(new_f, last_to_remove); |
429 | break; |
430 | } |
431 | } |
432 | info.num_1st_partition = num1; |
433 | info.num_2nd_partition = num2; |
434 | info.beg_2st_partition = new_f; |
435 | } |
436 | |
437 | private: |
438 | BOOST_INTRUSIVE_FORCEINLINEinline static void swap_prev(node_ptr this_node, node_ptr other_node) |
439 | { |
440 | node_ptr temp(NodeTraits::get_previous(this_node)); |
441 | NodeTraits::set_previous(this_node, NodeTraits::get_previous(other_node)); |
442 | NodeTraits::set_previous(other_node, temp); |
443 | } |
444 | |
445 | BOOST_INTRUSIVE_FORCEINLINEinline static void swap_next(node_ptr this_node, node_ptr other_node) |
446 | { |
447 | node_ptr temp(NodeTraits::get_next(this_node)); |
448 | NodeTraits::set_next(this_node, NodeTraits::get_next(other_node)); |
449 | NodeTraits::set_next(other_node, temp); |
450 | } |
451 | }; |
452 | |
453 | /// @cond |
454 | |
455 | template<class NodeTraits> |
456 | struct get_algo<CircularListAlgorithms, NodeTraits> |
457 | { |
458 | typedef circular_list_algorithms<NodeTraits> type; |
459 | }; |
460 | |
461 | /// @endcond |
462 | |
463 | } //namespace intrusive |
464 | } //namespace boost |
465 | |
466 | #include <boost/intrusive/detail/config_end.hpp> |
467 | |
468 | #endif //BOOST_INTRUSIVE_CIRCULAR_LIST_ALGORITHMS_HPP |
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 | #ifndef INCLUDED_SW_SOURCE_CORE_INC_FRAME_HXX |
21 | #define INCLUDED_SW_SOURCE_CORE_INC_FRAME_HXX |
22 | |
23 | #include <drawinglayer/primitive2d/baseprimitive2d.hxx> |
24 | #include <editeng/borderline.hxx> |
25 | #include <svl/poolitem.hxx> |
26 | #include <swtypes.hxx> |
27 | #include <swrect.hxx> |
28 | #include <calbck.hxx> |
29 | #include <svl/SfxBroadcaster.hxx> |
30 | #include <o3tl/typed_flags_set.hxx> |
31 | #include <com/sun/star/style/TabStop.hpp> |
32 | #include <basegfx/matrix/b2dhommatrix.hxx> |
33 | #include <vcl/outdev.hxx> |
34 | |
35 | #include <memory> |
36 | |
37 | namespace drawinglayer::processor2d { class BaseProcessor2D; } |
38 | |
39 | class SwLayoutFrame; |
40 | class SwRootFrame; |
41 | class SwPageFrame; |
42 | class SwBodyFrame; |
43 | class SwFlyFrame; |
44 | class SwSectionFrame; |
45 | class SwFootnoteFrame; |
46 | class SwFootnoteBossFrame; |
47 | class SwTabFrame; |
48 | class SwRowFrame; |
49 | class SwContentFrame; |
50 | class SwAttrSet; |
51 | class Color; |
52 | class SwBorderAttrs; |
53 | class SwCache; |
54 | class SvxBrushItem; |
55 | class SvxFormatBreakItem; |
56 | class SwFormatPageDesc; |
57 | class SwSelectionList; |
58 | struct SwPosition; |
59 | struct SwCursorMoveState; |
60 | class SwFormat; |
61 | class SwPrintData; |
62 | class SwSortedObjs; |
63 | class SwAnchoredObject; |
64 | enum class SvxFrameDirection; |
65 | class IDocumentDrawModelAccess; |
66 | |
67 | // Each FrameType is represented here as a bit. |
68 | // The bits must be set in a way that it can be determined with masking of |
69 | // which kind of FrameType an instance is _and_ from what classes it was derived. |
70 | // Each frame has in its base class a member that must be set by the |
71 | // constructors accordingly. |
72 | enum class SwFrameType |
73 | { |
74 | None = 0x0000, |
75 | Root = 0x0001, |
76 | Page = 0x0002, |
77 | Column = 0x0004, |
78 | Header = 0x0008, |
79 | Footer = 0x0010, |
80 | FtnCont = 0x0020, |
81 | Ftn = 0x0040, |
82 | Body = 0x0080, |
83 | Fly = 0x0100, |
84 | Section = 0x0200, |
85 | // UNUSED 0x0400 |
86 | Tab = 0x0800, |
87 | Row = 0x1000, |
88 | Cell = 0x2000, |
89 | Txt = 0x4000, |
90 | NoTxt = 0x8000, |
91 | }; |
92 | |
93 | namespace o3tl |
94 | { |
95 | template<> struct typed_flags<SwFrameType> : is_typed_flags<SwFrameType, 0xfbff> {}; |
96 | }; |
97 | |
98 | // for internal use some common combinations |
99 | #define FRM_LAYOUTSwFrameType(0x3bFF) SwFrameType(0x3bFF) |
100 | #define FRM_ALLSwFrameType(0xfbff) SwFrameType(0xfbff) |
101 | #define FRM_CNTNT(SwFrameType::Txt | SwFrameType::NoTxt) (SwFrameType::Txt | SwFrameType::NoTxt) |
102 | #define FRM_FTNBOSS(SwFrameType::Page | SwFrameType::Column) (SwFrameType::Page | SwFrameType::Column) |
103 | #define FRM_ACCESSIBLE(SwFrameType::Root | SwFrameType::Page | SwFrameType::Header | SwFrameType::Footer | SwFrameType::Ftn | SwFrameType::Fly | SwFrameType ::Tab | SwFrameType::Cell | SwFrameType::Txt) (SwFrameType::Root | SwFrameType::Page | SwFrameType::Header | SwFrameType::Footer | SwFrameType::Ftn | SwFrameType::Fly | SwFrameType::Tab | SwFrameType::Cell | SwFrameType::Txt) |
104 | #define FRM_NEIGHBOUR(SwFrameType::Column | SwFrameType::Cell) (SwFrameType::Column | SwFrameType::Cell) |
105 | #define FRM_NOTE_VERT(SwFrameType::FtnCont | SwFrameType::Ftn | SwFrameType::Section | SwFrameType::Tab | SwFrameType::Row | SwFrameType::Cell | SwFrameType ::Txt) (SwFrameType::FtnCont | SwFrameType::Ftn | SwFrameType::Section | SwFrameType::Tab | SwFrameType::Row | SwFrameType::Cell | SwFrameType::Txt) |
106 | #define FRM_HEADFOOT(SwFrameType::Header | SwFrameType::Footer) (SwFrameType::Header | SwFrameType::Footer) |
107 | #define FRM_BODYFTNC(SwFrameType::FtnCont | SwFrameType::Body) (SwFrameType::FtnCont | SwFrameType::Body) |
108 | |
109 | // for GetNextLeaf/GetPrevLeaf. |
110 | enum MakePageType |
111 | { |
112 | MAKEPAGE_NONE, // do not create page/footnote |
113 | MAKEPAGE_APPEND, // only append page if needed |
114 | MAKEPAGE_INSERT, // add or append page if needed |
115 | MAKEPAGE_FTN, // add footnote if needed |
116 | MAKEPAGE_NOSECTION // Don't create section frames |
117 | }; |
118 | |
119 | namespace drawinglayer::attribute { |
120 | class SdrAllFillAttributesHelper; |
121 | typedef std::shared_ptr< SdrAllFillAttributesHelper > SdrAllFillAttributesHelperPtr; |
122 | } |
123 | |
124 | /// Helper class to isolate geometry-defining members of SwFrame |
125 | /// and to control their accesses. Moved to own class to have no |
126 | /// hidden accesses to 'private' members anymore. |
127 | /// |
128 | /// Added most important flags about the state of this geometric |
129 | /// information and if it is valid or not |
130 | class SAL_DLLPUBLIC_RTTI__attribute__ ((type_visibility("default"))) SwFrameAreaDefinition |
131 | { |
132 | private: |
133 | friend void FriendHackInvalidateRowFrame(SwFrameAreaDefinition &); |
134 | |
135 | // The absolute position and size of the SwFrame in the document. |
136 | // This values are set by the layouter implementations |
137 | SwRect maFrameArea; |
138 | |
139 | // The 'inner' Frame Area defined by a SwRect relative to FrameArea: |
140 | // When identical to FrameArea, Pos() will be (0, 0) and Size identical. |
141 | SwRect maFramePrintArea; |
142 | |
143 | // bitfield |
144 | bool mbFrameAreaPositionValid : 1; |
145 | bool mbFrameAreaSizeValid : 1; |
146 | bool mbFramePrintAreaValid : 1; |
147 | |
148 | // #i65250# |
149 | // frame ID is now in general available - used for layout loop control |
150 | static sal_uInt32 mnLastFrameId; |
151 | const sal_uInt32 mnFrameId; |
152 | |
153 | protected: |
154 | // write access to mb*Valid flags |
155 | void setFrameAreaPositionValid(bool bNew); |
156 | void setFrameAreaSizeValid(bool bNew); |
157 | void setFramePrintAreaValid(bool bNew); |
158 | |
159 | public: |
160 | SwFrameAreaDefinition(); |
161 | virtual ~SwFrameAreaDefinition(); |
162 | |
163 | // read access to mb*Valid flags |
164 | bool isFrameAreaPositionValid() const { return mbFrameAreaPositionValid; } |
165 | bool isFrameAreaSizeValid() const { return mbFrameAreaSizeValid; } |
166 | bool isFramePrintAreaValid() const { return mbFramePrintAreaValid; } |
167 | |
168 | // syntactic sugar: test whole FrameAreaDefinition |
169 | bool isFrameAreaDefinitionValid() const { return isFrameAreaPositionValid() && isFrameAreaSizeValid() && isFramePrintAreaValid(); } |
170 | |
171 | // #i65250# |
172 | sal_uInt32 GetFrameId() const { return mnFrameId; } |
173 | |
174 | // read accesses to FrameArea definitions - only const access allowed. |
175 | // Do *not* const_cast results, it is necessary to track changes. use |
176 | // the below offered WriteAccess helper classes instead |
177 | const SwRect& getFrameArea() const { return maFrameArea; } |
178 | const SwRect& getFramePrintArea() const { return maFramePrintArea; } |
179 | |
180 | // helper class(es) for FrameArea manipulation. These |
181 | // have to be used to apply changes to FrameAreas. They hold a copy of the |
182 | // SwRect for manipulation. It gets written back at destruction. Thus, this |
183 | // mechanism depends on scope usage, take care. It prevents errors using |
184 | // different instances of SwFrame in get/set methods which is more safe |
185 | class FrameAreaWriteAccess : public SwRect |
186 | { |
187 | private: |
188 | SwFrameAreaDefinition& mrTarget; |
189 | |
190 | FrameAreaWriteAccess(const FrameAreaWriteAccess&) = delete; |
191 | FrameAreaWriteAccess& operator=(const FrameAreaWriteAccess&) = delete; |
192 | |
193 | public: |
194 | FrameAreaWriteAccess(SwFrameAreaDefinition& rTarget) : SwRect(rTarget.getFrameArea()), mrTarget(rTarget) {} |
195 | ~FrameAreaWriteAccess(); |
196 | void setSwRect(const SwRect& rNew) { *reinterpret_cast< SwRect* >(this) = rNew; } |
197 | }; |
198 | |
199 | // same helper for FramePrintArea |
200 | class FramePrintAreaWriteAccess : public SwRect |
201 | { |
202 | private: |
203 | SwFrameAreaDefinition& mrTarget; |
204 | |
205 | FramePrintAreaWriteAccess(const FramePrintAreaWriteAccess&) = delete; |
206 | FramePrintAreaWriteAccess& operator=(const FramePrintAreaWriteAccess&) = delete; |
207 | |
208 | public: |
209 | FramePrintAreaWriteAccess(SwFrameAreaDefinition& rTarget) : SwRect(rTarget.getFramePrintArea()), mrTarget(rTarget) {} |
210 | ~FramePrintAreaWriteAccess(); |
211 | void setSwRect(const SwRect& rNew) { *reinterpret_cast< SwRect* >(this) = rNew; } |
212 | }; |
213 | |
214 | // RotateFlyFrame3 - Support for Transformations |
215 | // Hand out the Transformations for the current FrameAreaDefinition |
216 | // for the FrameArea and FramePrintArea. |
217 | // FramePrintArea is not relative to FrameArea in this |
218 | // transformation representation (to make it easier to use and understand). |
219 | // There is no 'set' method since SwFrame is a layout object. For |
220 | // some cases rotation will be included (used for SwGrfNode in inner |
221 | // SwFrame of a SwFlyFrame) |
222 | virtual basegfx::B2DHomMatrix getFrameAreaTransformation() const; |
223 | virtual basegfx::B2DHomMatrix getFramePrintAreaTransformation() const; |
224 | |
225 | // RotateFlyFrame3 - Support for Transformations |
226 | // Modify current transformations by applying given translation |
227 | virtual void transform_translate(const Point& rOffset); |
228 | }; |
229 | |
230 | /// RotateFlyFrame3: Helper class when you want to make your SwFrame derivate |
231 | /// transformable. It provides some tooling to do so. To use, add as member |
232 | /// (see e.g. SwFlyFreeFrame which uses 'std::unique_ptr< TransformableSwFrame >') |
233 | class TransformableSwFrame |
234 | { |
235 | private: |
236 | // The SwFrameAreaDefinition to work on |
237 | SwFrameAreaDefinition& mrSwFrameAreaDefinition; |
238 | |
239 | // FrameAreaTransformation and FramePrintAreaTransformation |
240 | // !identity when needed (translate/scale is used (e.g. rotation)) |
241 | basegfx::B2DHomMatrix maFrameAreaTransformation; |
242 | basegfx::B2DHomMatrix maFramePrintAreaTransformation; |
243 | |
244 | public: |
245 | TransformableSwFrame(SwFrameAreaDefinition& rSwFrameAreaDefinition) |
246 | : mrSwFrameAreaDefinition(rSwFrameAreaDefinition), |
247 | maFrameAreaTransformation(), |
248 | maFramePrintAreaTransformation() |
249 | { |
250 | } |
251 | |
252 | // get SwFrameArea in transformation form |
253 | const basegfx::B2DHomMatrix& getLocalFrameAreaTransformation() const |
254 | { |
255 | return maFrameAreaTransformation; |
256 | } |
257 | |
258 | // get SwFramePrintArea in transformation form |
259 | const basegfx::B2DHomMatrix& getLocalFramePrintAreaTransformation() const |
260 | { |
261 | return maFramePrintAreaTransformation; |
262 | } |
263 | |
264 | // Helpers to re-create the untransformed SwRect(s) originally |
265 | // in the SwFrameAreaDefinition, based on the current Transformations. |
266 | SwRect getUntransformedFrameArea() const; |
267 | SwRect getUntransformedFramePrintArea() const; |
268 | |
269 | // Helper method to re-create FrameAreaTransformations based on the |
270 | // current FrameAreaDefinition transformed by given rotation and Center |
271 | void createFrameAreaTransformations( |
272 | double fRotation, |
273 | const basegfx::B2DPoint& rCenter); |
274 | |
275 | // Tooling method to reset the SwRect(s) in the current |
276 | // SwFrameAreaDefinition which are already adapted to |
277 | // Transformation back to the untransformed state, using |
278 | // the getUntransformedFrame*Area calls above when needed. |
279 | // Only the SwRect(s) are changed back, not the transformations. |
280 | void restoreFrameAreas(); |
281 | |
282 | // Re-Creates the SwRect(s) as BoundAreas based on the current |
283 | // set Transformations. |
284 | void adaptFrameAreasToTransformations(); |
285 | |
286 | // Modify current definitions by applying the given transformation |
287 | void transform(const basegfx::B2DHomMatrix& aTransform); |
288 | }; |
289 | |
290 | /** |
291 | * Base class of the Writer layout elements. |
292 | * |
293 | * This includes not only fly frames, but everything down to the paragraph |
294 | * level: pages, headers, footers, etc. (Inside a paragraph SwLinePortion |
295 | * instances are used.) |
296 | */ |
297 | class SW_DLLPUBLIC__attribute__ ((visibility("default"))) SwFrame : public SwFrameAreaDefinition, public SwClient, public SfxBroadcaster |
298 | { |
299 | // the hidden Frame |
300 | friend class SwFlowFrame; |
301 | friend class SwLayoutFrame; |
302 | friend class SwLooping; |
303 | friend class SwDeletionChecker; // for GetDep() |
304 | |
305 | // voids lower during creation of a column |
306 | friend SwFrame *SaveContent( SwLayoutFrame *, SwFrame* pStart ); |
307 | friend void RestoreContent( SwFrame *, SwLayoutFrame *, SwFrame *pSibling ); |
308 | |
309 | // for validating a mistakenly invalidated one in SwContentFrame::MakeAll |
310 | friend void ValidateSz( SwFrame *pFrame ); |
311 | // implemented in text/txtftn.cxx, prevents Footnote oscillation |
312 | friend void ValidateText( SwFrame *pFrame ); |
313 | |
314 | friend void MakeNxt( SwFrame *pFrame, SwFrame *pNxt ); |
315 | |
316 | // cache for (border) attributes |
317 | static SwCache *mpCache; |
318 | |
319 | SwRootFrame *mpRoot; |
320 | SwLayoutFrame *mpUpper; |
321 | SwFrame *mpNext; |
322 | SwFrame *mpPrev; |
323 | |
324 | // sw_redlinehide: hide these dangerous SwClient functions |
325 | using SwClient::GetRegisteredInNonConst; |
326 | using SwClient::GetRegisteredIn; |
327 | |
328 | SwFrame *FindNext_(); |
329 | SwFrame *FindPrev_(); |
330 | |
331 | /** method to determine next content frame in the same environment |
332 | for a flow frame (content frame, table frame, section frame) |
333 | |
334 | #i27138# - adding documentation: |
335 | Travelling downwards through the layout to determine the next content |
336 | frame in the same environment. There are several environments in a |
337 | document, which form a closed context regarding this function. These |
338 | environments are: |
339 | - Each page header |
340 | - Each page footer |
341 | - Each unlinked fly frame |
342 | - Each group of linked fly frames |
343 | - All footnotes |
344 | - All document body frames |
345 | #i27138# - adding parameter <_bInSameFootnote> |
346 | Its default value is <false>. If its value is <true>, the environment |
347 | 'All footnotes' is no longer treated. Instead each footnote is treated |
348 | as an own environment. |
349 | |
350 | @param _bInSameFootnote |
351 | input parameter - boolean indicating, that the found next content |
352 | frame has to be in the same footnote frame. This parameter is only |
353 | relevant for flow frames in footnotes. |
354 | |
355 | @return SwContentFrame* |
356 | pointer to the found next content frame. It's NULL, if none exists. |
357 | */ |
358 | SwContentFrame* FindNextCnt_( const bool _bInSameFootnote ); |
359 | |
360 | /** method to determine previous content frame in the same environment |
361 | for a flow frame (content frame, table frame, section frame) |
362 | |
363 | #i27138# |
364 | Travelling upwards through the layout to determine the previous content |
365 | frame in the same environment. There are several environments in a |
366 | document, which form a closed context regarding this function. These |
367 | environments are: |
368 | - Each page header |
369 | - Each page footer |
370 | - Each unlinked fly frame |
371 | - Each group of linked fly frames |
372 | - All footnotes |
373 | - All document body frames |
374 | #i27138# - adding parameter <_bInSameFootnote> |
375 | Its default value is <false>. If its value is <true>, the environment |
376 | 'All footnotes' is no longer treated. Instead each footnote is treated |
377 | as an own environment. |
378 | |
379 | The found previous content frame has to be in the same footnote frame. This is only |
380 | relevant for flow frames in footnotes. |
381 | |
382 | @return SwContentFrame* |
383 | pointer to the found previous content frame. It's NULL, if none exists. |
384 | */ |
385 | SwContentFrame* FindPrevCnt_(); |
386 | |
387 | void UpdateAttrFrame( const SfxPoolItem*, const SfxPoolItem*, sal_uInt8 & ); |
388 | SwFrame* GetIndNext_(); |
389 | void SetDirFlags( bool bVert ); |
390 | |
391 | const SwLayoutFrame* ImplGetNextLayoutLeaf( bool bFwd ) const; |
392 | |
393 | SwPageFrame* ImplFindPageFrame(); |
394 | |
395 | protected: |
396 | std::unique_ptr<SwSortedObjs> m_pDrawObjs; // draw objects, can be null |
397 | SwFrameType mnFrameType; //Who am I? |
398 | |
399 | bool mbInDtor : 1; |
400 | bool mbInvalidR2L : 1; |
401 | bool mbDerivedR2L : 1; |
402 | bool mbRightToLeft : 1; |
403 | bool mbInvalidVert : 1; |
404 | bool mbDerivedVert : 1; |
405 | bool mbVertical : 1; |
406 | |
407 | bool mbVertLR : 1; |
408 | bool mbVertLRBT : 1; |
409 | |
410 | bool mbValidLineNum : 1; |
411 | bool mbFixSize : 1; |
412 | |
413 | // if true, frame will be painted completely even content was changed |
414 | // only partially. For ContentFrames a border (from Action) will exclusively |
415 | // painted if <mbCompletePaint> is true. |
416 | bool mbCompletePaint : 1; |
417 | |
418 | bool mbRetouche : 1; // frame is responsible for retouching |
419 | |
420 | bool mbInfInvalid : 1; // InfoFlags are invalid |
421 | bool mbInfBody : 1; // Frame is in document body |
422 | bool mbInfTab : 1; // Frame is in a table |
423 | bool mbInfFly : 1; // Frame is in a Fly |
424 | bool mbInfFootnote : 1; // Frame is in a footnote |
425 | bool mbInfSct : 1; // Frame is in a section |
426 | bool mbColLocked : 1; // lock Grow/Shrink for column-wise section |
427 | // or fly frames, will be set in Format |
428 | bool m_isInDestroy : 1; |
429 | bool mbForbidDelete : 1; |
430 | |
431 | void ColLock() { mbColLocked = true; } |
432 | void ColUnlock() { mbColLocked = false; } |
433 | |
434 | virtual void DestroyImpl(); |
435 | virtual ~SwFrame() override; |
436 | |
437 | // Only used by SwRootFrame Ctor to get 'this' into mpRoot... |
438 | void setRootFrame( SwRootFrame* pRoot ) { mpRoot = pRoot; } |
439 | |
440 | SwPageFrame *InsertPage( SwPageFrame *pSibling, bool bFootnote ); |
441 | void PrepareMake(vcl::RenderContext* pRenderContext); |
442 | void OptPrepareMake(); |
443 | virtual void MakePos(); |
444 | // Format next frame of table frame to assure keeping attributes. |
445 | // In case of nested tables method <SwFrame::MakeAll()> is called to |
446 | // avoid formatting of superior table frame. |
447 | friend SwFrame* sw_FormatNextContentForKeep( SwTabFrame* pTabFrame ); |
448 | |
449 | virtual void MakeAll(vcl::RenderContext* pRenderContext) = 0; |
450 | // adjust frames of a page |
451 | SwTwips AdjustNeighbourhood( SwTwips nDiff, bool bTst = false ); |
452 | |
453 | // change only frame size not the size of PrtArea |
454 | virtual SwTwips ShrinkFrame( SwTwips, bool bTst = false, bool bInfo = false ) = 0; |
455 | virtual SwTwips GrowFrame ( SwTwips, bool bTst = false, bool bInfo = false ) = 0; |
456 | |
457 | /// use these so we can grep for SwFrame's GetRegisteredIn accesses |
458 | /// beware that SwTextFrame may return sw::WriterMultiListener |
459 | SwModify *GetDep() { return GetRegisteredInNonConst(); } |
460 | const SwModify *GetDep() const { return GetRegisteredIn(); } |
461 | |
462 | SwFrame( SwModify*, SwFrame* ); |
463 | |
464 | void CheckDir( SvxFrameDirection nDir, bool bVert, bool bOnlyBiDi, bool bBrowse ); |
465 | |
466 | /** enumeration for the different invalidations |
467 | #i28701# |
468 | */ |
469 | enum InvalidationType |
470 | { |
471 | INVALID_SIZE, INVALID_PRTAREA, INVALID_POS, INVALID_LINENUM, INVALID_ALL |
472 | }; |
473 | |
474 | /** method to determine, if an invalidation is allowed. |
475 | #i28701 |
476 | */ |
477 | virtual bool InvalidationAllowed( const InvalidationType _nInvalid ) const; |
478 | |
479 | /** method to perform additional actions on an invalidation |
480 | |
481 | #i28701# |
482 | Method has *only* to contain actions, which has to be performed on |
483 | *every* assignment of the corresponding flag to <false>. |
484 | */ |
485 | virtual void ActionOnInvalidation( const InvalidationType _nInvalid ); |
486 | |
487 | // draw shadow and borders |
488 | void PaintShadow( const SwRect&, SwRect&, const SwBorderAttrs& ) const; |
489 | virtual void Modify( const SfxPoolItem*, const SfxPoolItem* ) override; |
490 | |
491 | virtual const IDocumentDrawModelAccess& getIDocumentDrawModelAccess( ); |
492 | |
493 | public: |
494 | virtual css::uno::Sequence< css::style::TabStop > GetTabStopInfo( SwTwips ) |
495 | { |
496 | return css::uno::Sequence< css::style::TabStop >(); |
497 | } |
498 | |
499 | |
500 | SwFrameType GetType() const { return mnFrameType; } |
501 | |
502 | static SwCache &GetCache() { return *mpCache; } |
503 | static SwCache *GetCachePtr() { return mpCache; } |
504 | static void SetCache( SwCache *pNew ) { mpCache = pNew; } |
505 | |
506 | // change PrtArea size and FrameSize |
507 | SwTwips Shrink( SwTwips, bool bTst = false, bool bInfo = false ); |
508 | SwTwips Grow ( SwTwips, bool bTst = false, bool bInfo = false ); |
509 | |
510 | // different methods for inserting in layout tree (for performance reasons) |
511 | |
512 | // insert before pBehind or at the end of the chain below mpUpper |
513 | void InsertBefore( SwLayoutFrame* pParent, SwFrame* pBehind ); |
514 | // insert after pBefore or at the beginning of the chain below mpUpper |
515 | void InsertBehind( SwLayoutFrame *pParent, SwFrame *pBefore ); |
516 | // insert before pBehind or at the end of the chain while considering |
517 | // the siblings of pSct |
518 | bool InsertGroupBefore( SwFrame* pParent, SwFrame* pWhere, SwFrame* pSct ); |
519 | void RemoveFromLayout(); |
520 | |
521 | // For internal use only - who ignores this will be put in a sack and has |
522 | // to stay there for two days |
523 | // Does special treatment for Get_[Next|Prev]Leaf() (for tables). |
524 | SwLayoutFrame *GetLeaf( MakePageType eMakePage, bool bFwd ); |
525 | SwLayoutFrame *GetNextLeaf ( MakePageType eMakePage ); |
526 | SwLayoutFrame *GetNextFootnoteLeaf( MakePageType eMakePage ); |
527 | SwLayoutFrame *GetNextSctLeaf( MakePageType eMakePage ); |
528 | SwLayoutFrame *GetNextCellLeaf(); |
529 | SwLayoutFrame *GetPrevLeaf (); |
530 | SwLayoutFrame *GetPrevFootnoteLeaf( MakePageType eMakeFootnote ); |
531 | SwLayoutFrame *GetPrevSctLeaf(); |
532 | SwLayoutFrame *GetPrevCellLeaf(); |
533 | const SwLayoutFrame *GetLeaf ( MakePageType eMakePage, bool bFwd, |
534 | const SwFrame *pAnch ) const; |
535 | |
536 | bool WrongPageDesc( SwPageFrame* pNew ); |
537 | |
538 | //#i28701# - new methods to append/remove drawing objects |
539 | void AppendDrawObj( SwAnchoredObject& _rNewObj ); |
540 | void RemoveDrawObj( SwAnchoredObject& _rToRemoveObj ); |
541 | |
542 | // work with chain of FlyFrames |
543 | void AppendFly( SwFlyFrame *pNew ); |
544 | void RemoveFly( SwFlyFrame *pToRemove ); |
545 | const SwSortedObjs *GetDrawObjs() const { return m_pDrawObjs.get(); } |
546 | SwSortedObjs *GetDrawObjs() { return m_pDrawObjs.get(); } |
547 | // #i28701# - change purpose of method and adjust its name |
548 | void InvalidateObjs( const bool _bNoInvaOfAsCharAnchoredObjs = true ); |
549 | |
550 | virtual void PaintSwFrameShadowAndBorder( |
551 | const SwRect&, |
552 | const SwPageFrame* pPage, |
553 | const SwBorderAttrs&) const; |
554 | void PaintBaBo( const SwRect&, const SwPageFrame *pPage, |
555 | const bool bOnlyTextBackground = false) const; |
556 | void PaintSwFrameBackground( const SwRect&, const SwPageFrame *pPage, |
557 | const SwBorderAttrs &, |
558 | const bool bLowerMode = false, |
559 | const bool bLowerBorder = false, |
560 | const bool bOnlyTextBackground = false ) const; |
561 | void PaintBorderLine( const SwRect&, const SwRect&, const SwPageFrame*, |
562 | const Color *pColor, |
563 | const SvxBorderLineStyle = SvxBorderLineStyle::SOLID ) const; |
564 | |
565 | std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> CreateProcessor2D( ) const; |
566 | void ProcessPrimitives( const drawinglayer::primitive2d::Primitive2DContainer& rSequence ) const; |
567 | |
568 | // retouch, not in the area of the given Rect! |
569 | void Retouch( const SwPageFrame *pPage, const SwRect &rRect ) const; |
570 | |
571 | bool GetBackgroundBrush( |
572 | drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes, |
573 | const SvxBrushItem*& rpBrush, |
574 | const Color*& rpColor, |
575 | SwRect &rOrigRect, |
576 | bool bLowerMode, |
577 | bool bConsiderTextBox ) const; |
578 | |
579 | inline void SetCompletePaint() const; |
580 | inline void ResetCompletePaint() const; |
581 | bool IsCompletePaint() const { return mbCompletePaint; } |
582 | |
583 | inline void SetRetouche() const; |
584 | inline void ResetRetouche() const; |
585 | bool IsRetouche() const { return mbRetouche; } |
586 | |
587 | void SetInfFlags(); |
588 | void InvalidateInfFlags() { mbInfInvalid = true; } |
589 | inline bool IsInDocBody() const; // use InfoFlags, determine flags |
590 | inline bool IsInFootnote() const; // if necessary |
591 | inline bool IsInTab() const; |
592 | inline bool IsInFly() const; |
593 | inline bool IsInSct() const; |
594 | |
595 | // If frame is inside a split table row, this function returns |
596 | // the corresponding row frame in the follow table. |
597 | const SwRowFrame* IsInSplitTableRow() const; |
598 | |
599 | // If frame is inside a follow flow row, this function returns |
600 | // the corresponding row frame master table |
601 | const SwRowFrame* IsInFollowFlowRow() const; |
602 | |
603 | bool IsInBalancedSection() const; |
604 | |
605 | inline bool IsVertical() const; |
606 | inline bool IsVertLR() const; |
607 | inline bool IsVertLRBT() const; |
608 | |
609 | void SetDerivedVert( bool bNew ){ mbDerivedVert = bNew; } |
610 | void SetInvalidVert( bool bNew) { mbInvalidVert = bNew; } |
611 | inline bool IsRightToLeft() const; |
612 | void SetDerivedR2L( bool bNew ) { mbDerivedR2L = bNew; } |
613 | |
614 | void CheckDirChange(); |
615 | // returns upper left frame position for LTR and |
616 | // upper right frame position for Asian / RTL frames |
617 | Point GetFrameAnchorPos( bool bIgnoreFlysAnchoredAtThisFrame ) const; |
618 | |
619 | /** determine, if frame is moveable in given environment |
620 | |
621 | method replaced 'old' method <bool IsMoveable() const>. |
622 | Determines, if frame is moveable in given environment. if no environment |
623 | is given (parameter _pLayoutFrame == 0), the movability in the actual |
624 | environment (<GetUpper()) is checked. |
625 | |
626 | @param _pLayoutFrame |
627 | input parameter - given environment (layout frame), in which the movability |
628 | will be checked. If not set ( == 0 ), actual environment is taken. |
629 | |
630 | @return boolean, indicating, if frame is moveable in given environment |
631 | */ |
632 | bool IsMoveable( const SwLayoutFrame* _pLayoutFrame = nullptr ) const; |
633 | |
634 | // Is it permitted for the (Text)Frame to add a footnote in the current |
635 | // environment (not e.g. for repeating table headlines) |
636 | bool IsFootnoteAllowed() const; |
637 | |
638 | virtual void Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttrs = nullptr ); |
639 | |
640 | virtual void CheckDirection( bool bVert ); |
641 | |
642 | void ReinitializeFrameSizeAttrFlags(); |
643 | |
644 | /// WARNING: this may not return correct RES_PAGEDESC/RES_BREAK items for |
645 | /// SwTextFrame, use GetBreakItem()/GetPageDescItem() instead |
646 | const SwAttrSet *GetAttrSet() const; |
647 | virtual const SvxFormatBreakItem& GetBreakItem() const; |
648 | virtual const SwFormatPageDesc& GetPageDescItem() const; |
649 | |
650 | bool HasFixSize() const { return mbFixSize; } |
651 | |
652 | // check all pages (starting from the given) and correct them if needed |
653 | static void CheckPageDescs( SwPageFrame *pStart, bool bNotifyFields = true, SwPageFrame** ppPrev = nullptr); |
654 | |
655 | // might return 0, with and without const |
656 | SwFrame *GetNext() { return mpNext; } |
657 | SwFrame *GetPrev() { return mpPrev; } |
658 | SwLayoutFrame *GetUpper() { return mpUpper; } |
659 | SwRootFrame *getRootFrame(){ return mpRoot; } |
660 | SwPageFrame *FindPageFrame() { return IsPageFrame() ? reinterpret_cast<SwPageFrame*>(this) : ImplFindPageFrame(); } |
661 | SwFrame *FindColFrame(); |
662 | SwRowFrame *FindRowFrame(); |
663 | SwFootnoteBossFrame *FindFootnoteBossFrame( bool bFootnotes = false ); |
664 | SwTabFrame *ImplFindTabFrame(); |
665 | SwFootnoteFrame *ImplFindFootnoteFrame(); |
666 | SwFlyFrame *ImplFindFlyFrame(); |
667 | SwSectionFrame *ImplFindSctFrame(); |
668 | const SwBodyFrame *ImplFindBodyFrame() const; |
669 | SwFrame *FindFooterOrHeader(); |
670 | SwFrame *GetLower(); |
671 | const SwFrame *GetNext() const { return mpNext; } |
672 | const SwFrame *GetPrev() const { return mpPrev; } |
673 | const SwLayoutFrame *GetUpper() const { return mpUpper; } |
674 | const SwRootFrame *getRootFrame() const { return mpRoot; } |
675 | inline SwTabFrame *FindTabFrame(); |
676 | inline SwFootnoteFrame *FindFootnoteFrame(); |
677 | inline SwFlyFrame *FindFlyFrame(); |
678 | inline SwSectionFrame *FindSctFrame(); |
679 | inline SwFrame *FindNext(); |
680 | // #i27138# - add parameter <_bInSameFootnote> |
681 | SwContentFrame* FindNextCnt( const bool _bInSameFootnote = false ); |
682 | inline SwFrame *FindPrev(); |
683 | inline const SwPageFrame *FindPageFrame() const; |
684 | inline const SwFootnoteBossFrame *FindFootnoteBossFrame( bool bFootnote = false ) const; |
685 | inline const SwFrame *FindColFrame() const; |
686 | inline const SwFrame *FindFooterOrHeader() const; |
687 | inline const SwTabFrame *FindTabFrame() const; |
688 | inline const SwFootnoteFrame *FindFootnoteFrame() const; |
689 | inline const SwFlyFrame *FindFlyFrame() const; |
690 | inline const SwSectionFrame *FindSctFrame() const; |
691 | inline const SwBodyFrame *FindBodyFrame() const; |
692 | inline const SwFrame *FindNext() const; |
693 | // #i27138# - add parameter <_bInSameFootnote> |
694 | const SwContentFrame* FindNextCnt( const bool _bInSameFootnote = false ) const; |
695 | inline const SwFrame *FindPrev() const; |
696 | const SwFrame *GetLower() const; |
697 | |
698 | SwContentFrame* FindPrevCnt(); |
699 | |
700 | const SwContentFrame* FindPrevCnt() const; |
701 | |
702 | // #i79774# |
703 | SwFrame* GetIndPrev_() const; |
704 | SwFrame* GetIndPrev() const |
705 | { return ( mpPrev || !IsInSct() ) ? mpPrev : GetIndPrev_(); } |
706 | |
707 | SwFrame* GetIndNext() |
708 | { return ( mpNext || !IsInSct() ) ? mpNext : GetIndNext_(); } |
709 | const SwFrame* GetIndNext() const { return const_cast<SwFrame*>(this)->GetIndNext(); } |
710 | |
711 | sal_uInt16 GetPhyPageNum() const; // page number without offset |
712 | sal_uInt16 GetVirtPageNum() const; // page number with offset |
713 | bool OnRightPage() const { return 0 != GetPhyPageNum() % 2; }; |
714 | bool WannaRightPage() const; |
715 | bool OnFirstPage() const; |
716 | |
717 | inline const SwLayoutFrame *GetPrevLayoutLeaf() const; |
718 | inline const SwLayoutFrame *GetNextLayoutLeaf() const; |
719 | inline SwLayoutFrame *GetPrevLayoutLeaf(); |
720 | inline SwLayoutFrame *GetNextLayoutLeaf(); |
721 | |
722 | virtual void Calc(vcl::RenderContext* pRenderContext) const; // here might be "formatted" |
723 | inline void OptCalc() const; // here we assume (for optimization) that |
724 | // the predecessors are already formatted |
725 | Point GetRelPos() const; |
726 | |
727 | // PaintArea is the area where content might be displayed. |
728 | // The margin of a page or the space between columns belongs to it. |
729 | SwRect GetPaintArea() const; |
730 | |
731 | // UnionFrame is the union of Frame- and PrtArea, normally identical |
732 | // to the FrameArea except in case of negative Prt margins. |
733 | SwRect UnionFrame( bool bBorder = false ) const; |
734 | |
735 | virtual Size ChgSize( const Size& aNewSize ); |
736 | |
737 | virtual void Cut() = 0; |
738 | virtual void Paste( SwFrame* pParent, SwFrame* pSibling = nullptr ) = 0; |
739 | |
740 | void ValidateLineNum() { mbValidLineNum = true; } |
741 | |
742 | bool GetValidLineNumFlag()const { return mbValidLineNum; } |
743 | |
744 | // Only invalidate Frame |
745 | // #i28701# - add call to method <ActionOnInvalidation(..)> |
746 | // for all invalidation methods. |
747 | // #i28701# - use method <InvalidationAllowed(..)> to |
748 | // decide, if invalidation will to be performed or not. |
749 | // #i26945# - no additional invalidation, if it's already |
750 | // invalidate. |
751 | void InvalidateSize_() |
752 | { |
753 | if ( isFrameAreaSizeValid() && InvalidationAllowed( INVALID_SIZE ) ) |
754 | { |
755 | setFrameAreaSizeValid(false); |
756 | ActionOnInvalidation( INVALID_SIZE ); |
757 | } |
758 | } |
759 | void InvalidatePrt_() |
760 | { |
761 | if ( isFramePrintAreaValid() && InvalidationAllowed( INVALID_PRTAREA ) ) |
762 | { |
763 | setFramePrintAreaValid(false); |
764 | ActionOnInvalidation( INVALID_PRTAREA ); |
765 | } |
766 | } |
767 | void InvalidatePos_() |
768 | { |
769 | if ( isFrameAreaPositionValid() && InvalidationAllowed( INVALID_POS ) ) |
770 | { |
771 | setFrameAreaPositionValid(false); |
772 | ActionOnInvalidation( INVALID_POS ); |
773 | } |
774 | } |
775 | void InvalidateLineNum_() |
776 | { |
777 | if ( mbValidLineNum && InvalidationAllowed( INVALID_LINENUM ) ) |
778 | { |
779 | mbValidLineNum = false; |
780 | ActionOnInvalidation( INVALID_LINENUM ); |
781 | } |
782 | } |
783 | void InvalidateAll_() |
784 | { |
785 | if ( ( isFrameAreaSizeValid() || isFramePrintAreaValid() || isFrameAreaPositionValid() ) && InvalidationAllowed( INVALID_ALL ) ) |
786 | { |
787 | setFrameAreaSizeValid(false); |
788 | setFrameAreaPositionValid(false); |
789 | setFramePrintAreaValid(false); |
790 | ActionOnInvalidation( INVALID_ALL ); |
791 | } |
792 | } |
793 | // also notify page at the same time |
794 | inline void InvalidateSize(); |
795 | inline void InvalidatePrt(); |
796 | inline void InvalidatePos(); |
797 | inline void InvalidateLineNum(); |
798 | inline void InvalidateAll(); |
799 | void ImplInvalidateSize(); |
800 | void ImplInvalidatePrt(); |
801 | void ImplInvalidatePos(); |
802 | void ImplInvalidateLineNum(); |
803 | |
804 | inline void InvalidateNextPos( bool bNoFootnote = false ); |
805 | void ImplInvalidateNextPos( bool bNoFootnote ); |
806 | |
807 | /** method to invalidate printing area of next frame |
808 | #i11859# |
809 | */ |
810 | void InvalidateNextPrtArea(); |
811 | |
812 | void InvalidatePage( const SwPageFrame *pPage = nullptr ) const; |
813 | |
814 | virtual bool FillSelection( SwSelectionList& rList, const SwRect& rRect ) const; |
815 | |
816 | virtual bool GetModelPositionForViewPoint( SwPosition *, Point&, |
817 | SwCursorMoveState* = nullptr, bool bTestBackground = false ) const; |
818 | virtual bool GetCharRect( SwRect &, const SwPosition&, |
819 | SwCursorMoveState* = nullptr, bool bAllowFarAway = true ) const; |
820 | virtual void PaintSwFrame( vcl::RenderContext& rRenderContext, SwRect const&, |
821 | SwPrintData const*const pPrintData = nullptr ) const; |
822 | |
823 | // HACK: shortcut between frame and formatting |
824 | // It's your own fault if you cast void* incorrectly! In any case check |
825 | // the void* for 0. |
826 | virtual bool Prepare( const PrepareHint ePrep = PrepareHint::Clear, |
827 | const void *pVoid = nullptr, bool bNotify = true ); |
828 | |
829 | // true if it is the correct class, false otherwise |
830 | inline bool IsLayoutFrame() const; |
831 | inline bool IsRootFrame() const; |
832 | inline bool IsPageFrame() const; |
833 | inline bool IsColumnFrame() const; |
834 | inline bool IsFootnoteBossFrame() const; // footnote bosses might be PageFrames or ColumnFrames |
835 | inline bool IsHeaderFrame() const; |
836 | inline bool IsFooterFrame() const; |
837 | inline bool IsFootnoteContFrame() const; |
838 | inline bool IsFootnoteFrame() const; |
839 | inline bool IsBodyFrame() const; |
840 | inline bool IsColBodyFrame() const; // implemented in layfrm.hxx, BodyFrame above ColumnFrame |
841 | inline bool IsPageBodyFrame() const; // implemented in layfrm.hxx, BodyFrame above PageFrame |
842 | inline bool IsFlyFrame() const; |
843 | inline bool IsSctFrame() const; |
844 | inline bool IsTabFrame() const; |
845 | inline bool IsRowFrame() const; |
846 | inline bool IsCellFrame() const; |
847 | inline bool IsContentFrame() const; |
848 | inline bool IsTextFrame() const; |
849 | inline bool IsNoTextFrame() const; |
850 | // Frames where its PrtArea depends on their neighbors and that are |
851 | // positioned in the content flow |
852 | inline bool IsFlowFrame() const; |
853 | // Frames that are capable of retouching or that might need to retouch behind |
854 | // themselves |
855 | inline bool IsRetoucheFrame() const; |
856 | inline bool IsAccessibleFrame() const; |
857 | |
858 | void PrepareCursor(); // CursorShell is allowed to call this |
859 | |
860 | // Is the Frame (or the section containing it) protected? Same for Fly in |
861 | // Fly in ... and footnotes |
862 | bool IsProtected() const; |
863 | |
864 | bool IsColLocked() const { return mbColLocked; } |
865 | virtual bool IsDeleteForbidden() const { return mbForbidDelete; } |
866 | |
867 | /// this is the only way to delete a SwFrame instance |
868 | static void DestroyFrame(SwFrame *const pFrame); |
869 | |
870 | bool IsInDtor() const { return mbInDtor; } |
871 | |
872 | // No inline cause we need the function pointers |
873 | long GetTopMargin() const; |
874 | long GetBottomMargin() const; |
875 | long GetLeftMargin() const; |
876 | long GetRightMargin() const; |
877 | void SetTopBottomMargins( long, long ); |
878 | void SetLeftRightMargins( long, long ); |
879 | void SetRightLeftMargins( long, long ); |
880 | long GetPrtLeft() const; |
881 | long GetPrtBottom() const; |
882 | long GetPrtRight() const; |
883 | long GetPrtTop() const; |
884 | bool SetMinLeft( long ); |
885 | bool SetMaxBottom( long ); |
886 | bool SetMaxRight( long ); |
887 | void MakeBelowPos( const SwFrame*, const SwFrame*, bool ); |
888 | void MakeLeftPos( const SwFrame*, const SwFrame*, bool ); |
889 | void MakeRightPos( const SwFrame*, const SwFrame*, bool ); |
890 | bool IsNeighbourFrame() const |
891 | { return bool(GetType() & FRM_NEIGHBOUR(SwFrameType::Column | SwFrameType::Cell)); } |
892 | |
893 | // NEW TABLES |
894 | // Some functions for covered/covering table cells. This way unnecessary |
895 | // includes can be avoided |
896 | virtual bool IsLeaveUpperAllowed() const; |
897 | virtual bool IsCoveredCell() const; |
898 | bool IsInCoveredCell() const; |
899 | |
900 | // #i81146# new loop control |
901 | bool KnowsFormat( const SwFormat& rFormat ) const; |
902 | void RegisterToFormat( SwFormat& rFormat ); |
903 | void ValidateThisAndAllLowers( const sal_uInt16 nStage ); |
904 | |
905 | void ForbidDelete() { mbForbidDelete = true; } |
906 | void AllowDelete() { mbForbidDelete = false; } |
907 | |
908 | drawinglayer::attribute::SdrAllFillAttributesHelperPtr getSdrAllFillAttributesHelper() const; |
909 | bool supportsFullDrawingLayerFillAttributeSet() const; |
910 | |
911 | public: |
912 | // if writer is NULL, dumps the layout structure as XML in layout.xml |
913 | virtual void dumpAsXml(xmlTextWriterPtr writer = nullptr) const; |
914 | void dumpTopMostAsXml(xmlTextWriterPtr writer = nullptr) const; |
915 | void dumpInfosAsXml(xmlTextWriterPtr writer) const; |
916 | virtual void dumpAsXmlAttributes(xmlTextWriterPtr writer) const; |
917 | void dumpChildrenAsXml(xmlTextWriterPtr writer) const; |
918 | bool IsCollapse() const; |
919 | }; |
920 | |
921 | inline bool SwFrame::IsInDocBody() const |
922 | { |
923 | if ( mbInfInvalid ) |
924 | const_cast<SwFrame*>(this)->SetInfFlags(); |
925 | return mbInfBody; |
926 | } |
927 | inline bool SwFrame::IsInFootnote() const |
928 | { |
929 | if ( mbInfInvalid ) |
930 | const_cast<SwFrame*>(this)->SetInfFlags(); |
931 | return mbInfFootnote; |
932 | } |
933 | inline bool SwFrame::IsInTab() const |
934 | { |
935 | if ( mbInfInvalid ) |
936 | const_cast<SwFrame*>(this)->SetInfFlags(); |
937 | return mbInfTab; |
938 | } |
939 | inline bool SwFrame::IsInFly() const |
940 | { |
941 | if ( mbInfInvalid ) |
942 | const_cast<SwFrame*>(this)->SetInfFlags(); |
943 | return mbInfFly; |
944 | } |
945 | inline bool SwFrame::IsInSct() const |
946 | { |
947 | if ( mbInfInvalid ) |
948 | const_cast<SwFrame*>(this)->SetInfFlags(); |
949 | return mbInfSct; |
950 | } |
951 | bool SwFrame::IsVertical() const |
952 | { |
953 | if( mbInvalidVert ) |
954 | const_cast<SwFrame*>(this)->SetDirFlags( true ); |
955 | return mbVertical; |
956 | } |
957 | inline bool SwFrame::IsVertLR() const |
958 | { |
959 | return mbVertLR; |
960 | } |
961 | inline bool SwFrame::IsVertLRBT() const |
962 | { |
963 | return mbVertLRBT; |
964 | } |
965 | inline bool SwFrame::IsRightToLeft() const |
966 | { |
967 | if( mbInvalidR2L ) |
968 | const_cast<SwFrame*>(this)->SetDirFlags( false ); |
969 | return mbRightToLeft; |
970 | } |
971 | |
972 | inline void SwFrame::SetCompletePaint() const |
973 | { |
974 | const_cast<SwFrame*>(this)->mbCompletePaint = true; |
975 | } |
976 | inline void SwFrame::ResetCompletePaint() const |
977 | { |
978 | const_cast<SwFrame*>(this)->mbCompletePaint = false; |
979 | } |
980 | |
981 | inline void SwFrame::SetRetouche() const |
982 | { |
983 | const_cast<SwFrame*>(this)->mbRetouche = true; |
984 | } |
985 | inline void SwFrame::ResetRetouche() const |
986 | { |
987 | const_cast<SwFrame*>(this)->mbRetouche = false; |
988 | } |
989 | |
990 | inline SwLayoutFrame *SwFrame::GetNextLayoutLeaf() |
991 | { |
992 | return const_cast<SwLayoutFrame*>(static_cast<const SwFrame*>(this)->GetNextLayoutLeaf()); |
993 | } |
994 | inline SwLayoutFrame *SwFrame::GetPrevLayoutLeaf() |
995 | { |
996 | return const_cast<SwLayoutFrame*>(static_cast<const SwFrame*>(this)->GetPrevLayoutLeaf()); |
997 | } |
998 | inline const SwLayoutFrame *SwFrame::GetNextLayoutLeaf() const |
999 | { |
1000 | return ImplGetNextLayoutLeaf( true ); |
1001 | } |
1002 | inline const SwLayoutFrame *SwFrame::GetPrevLayoutLeaf() const |
1003 | { |
1004 | return ImplGetNextLayoutLeaf( false ); |
1005 | } |
1006 | |
1007 | inline void SwFrame::InvalidateSize() |
1008 | { |
1009 | if ( isFrameAreaSizeValid() ) |
1010 | { |
1011 | ImplInvalidateSize(); |
1012 | } |
1013 | } |
1014 | inline void SwFrame::InvalidatePrt() |
1015 | { |
1016 | if ( isFramePrintAreaValid() ) |
1017 | { |
1018 | ImplInvalidatePrt(); |
1019 | } |
1020 | } |
1021 | inline void SwFrame::InvalidatePos() |
1022 | { |
1023 | if ( isFrameAreaPositionValid() ) |
1024 | { |
1025 | ImplInvalidatePos(); |
1026 | } |
1027 | } |
1028 | inline void SwFrame::InvalidateLineNum() |
1029 | { |
1030 | if ( mbValidLineNum ) |
1031 | ImplInvalidateLineNum(); |
1032 | } |
1033 | inline void SwFrame::InvalidateAll() |
1034 | { |
1035 | if ( InvalidationAllowed( INVALID_ALL ) ) |
1036 | { |
1037 | if ( isFrameAreaDefinitionValid() ) |
1038 | { |
1039 | ImplInvalidatePos(); |
1040 | } |
1041 | |
1042 | setFrameAreaSizeValid(false); |
1043 | setFrameAreaPositionValid(false); |
1044 | setFramePrintAreaValid(false); |
1045 | |
1046 | // #i28701# |
1047 | ActionOnInvalidation( INVALID_ALL ); |
1048 | } |
1049 | } |
1050 | inline void SwFrame::InvalidateNextPos( bool bNoFootnote ) |
1051 | { |
1052 | if ( mpNext && !mpNext->IsSctFrame() ) |
1053 | mpNext->InvalidatePos(); |
1054 | else |
1055 | ImplInvalidateNextPos( bNoFootnote ); |
1056 | } |
1057 | |
1058 | inline void SwFrame::OptCalc() const |
1059 | { |
1060 | if ( !isFrameAreaPositionValid() || !isFramePrintAreaValid() || !isFrameAreaSizeValid() ) |
1061 | { |
1062 | const_cast<SwFrame*>(this)->OptPrepareMake(); |
1063 | } |
1064 | } |
1065 | inline const SwPageFrame *SwFrame::FindPageFrame() const |
1066 | { |
1067 | return const_cast<SwFrame*>(this)->FindPageFrame(); |
1068 | } |
1069 | inline const SwFrame *SwFrame::FindColFrame() const |
1070 | { |
1071 | return const_cast<SwFrame*>(this)->FindColFrame(); |
1072 | } |
1073 | inline const SwFrame *SwFrame::FindFooterOrHeader() const |
1074 | { |
1075 | return const_cast<SwFrame*>(this)->FindFooterOrHeader(); |
1076 | } |
1077 | inline SwTabFrame *SwFrame::FindTabFrame() |
1078 | { |
1079 | return IsInTab() ? ImplFindTabFrame() : nullptr; |
1080 | } |
1081 | inline const SwFootnoteBossFrame *SwFrame::FindFootnoteBossFrame( bool bFootnote ) const |
1082 | { |
1083 | return const_cast<SwFrame*>(this)->FindFootnoteBossFrame( bFootnote ); |
1084 | } |
1085 | inline SwFootnoteFrame *SwFrame::FindFootnoteFrame() |
1086 | { |
1087 | return IsInFootnote() ? ImplFindFootnoteFrame() : nullptr; |
1088 | } |
1089 | inline SwFlyFrame *SwFrame::FindFlyFrame() |
1090 | { |
1091 | return IsInFly() ? ImplFindFlyFrame() : nullptr; |
1092 | } |
1093 | inline SwSectionFrame *SwFrame::FindSctFrame() |
1094 | { |
1095 | return IsInSct() ? ImplFindSctFrame() : nullptr; |
1096 | } |
1097 | |
1098 | inline const SwBodyFrame *SwFrame::FindBodyFrame() const |
1099 | { |
1100 | return IsInDocBody() ? ImplFindBodyFrame() : nullptr; |
1101 | } |
1102 | |
1103 | inline const SwTabFrame *SwFrame::FindTabFrame() const |
1104 | { |
1105 | return IsInTab() ? const_cast<SwFrame*>(this)->ImplFindTabFrame() : nullptr; |
1106 | } |
1107 | inline const SwFootnoteFrame *SwFrame::FindFootnoteFrame() const |
1108 | { |
1109 | return IsInFootnote() ? const_cast<SwFrame*>(this)->ImplFindFootnoteFrame() : nullptr; |
1110 | } |
1111 | inline const SwFlyFrame *SwFrame::FindFlyFrame() const |
1112 | { |
1113 | return IsInFly() ? const_cast<SwFrame*>(this)->ImplFindFlyFrame() : nullptr; |
1114 | } |
1115 | inline const SwSectionFrame *SwFrame::FindSctFrame() const |
1116 | { |
1117 | return IsInSct() ? const_cast<SwFrame*>(this)->ImplFindSctFrame() : nullptr; |
1118 | } |
1119 | inline SwFrame *SwFrame::FindNext() |
1120 | { |
1121 | if ( mpNext ) |
1122 | return mpNext; |
1123 | else |
1124 | return FindNext_(); |
1125 | } |
1126 | inline const SwFrame *SwFrame::FindNext() const |
1127 | { |
1128 | if ( mpNext ) |
1129 | return mpNext; |
1130 | else |
1131 | return const_cast<SwFrame*>(this)->FindNext_(); |
1132 | } |
1133 | inline SwFrame *SwFrame::FindPrev() |
1134 | { |
1135 | if ( mpPrev && !mpPrev->IsSctFrame() ) |
1136 | return mpPrev; |
1137 | else |
1138 | return FindPrev_(); |
1139 | } |
1140 | inline const SwFrame *SwFrame::FindPrev() const |
1141 | { |
1142 | if ( mpPrev && !mpPrev->IsSctFrame() ) |
1143 | return mpPrev; |
1144 | else |
1145 | return const_cast<SwFrame*>(this)->FindPrev_(); |
1146 | } |
1147 | |
1148 | inline bool SwFrame::IsLayoutFrame() const |
1149 | { |
1150 | return bool(GetType() & FRM_LAYOUTSwFrameType(0x3bFF)); |
1151 | } |
1152 | inline bool SwFrame::IsRootFrame() const |
1153 | { |
1154 | return mnFrameType == SwFrameType::Root; |
1155 | } |
1156 | inline bool SwFrame::IsPageFrame() const |
1157 | { |
1158 | return mnFrameType == SwFrameType::Page; |
1159 | } |
1160 | inline bool SwFrame::IsColumnFrame() const |
1161 | { |
1162 | return mnFrameType == SwFrameType::Column; |
1163 | } |
1164 | inline bool SwFrame::IsFootnoteBossFrame() const |
1165 | { |
1166 | return bool(GetType() & FRM_FTNBOSS(SwFrameType::Page | SwFrameType::Column)); |
1167 | } |
1168 | inline bool SwFrame::IsHeaderFrame() const |
1169 | { |
1170 | return mnFrameType == SwFrameType::Header; |
1171 | } |
1172 | inline bool SwFrame::IsFooterFrame() const |
1173 | { |
1174 | return mnFrameType == SwFrameType::Footer; |
1175 | } |
1176 | inline bool SwFrame::IsFootnoteContFrame() const |
1177 | { |
1178 | return mnFrameType == SwFrameType::FtnCont; |
1179 | } |
1180 | inline bool SwFrame::IsFootnoteFrame() const |
1181 | { |
1182 | return mnFrameType == SwFrameType::Ftn; |
1183 | } |
1184 | inline bool SwFrame::IsBodyFrame() const |
1185 | { |
1186 | return mnFrameType == SwFrameType::Body; |
1187 | } |
1188 | inline bool SwFrame::IsFlyFrame() const |
1189 | { |
1190 | return mnFrameType == SwFrameType::Fly; |
1191 | } |
1192 | inline bool SwFrame::IsSctFrame() const |
1193 | { |
1194 | return mnFrameType == SwFrameType::Section; |
1195 | } |
1196 | inline bool SwFrame::IsTabFrame() const |
1197 | { |
1198 | return mnFrameType == SwFrameType::Tab; |
1199 | } |
1200 | inline bool SwFrame::IsRowFrame() const |
1201 | { |
1202 | return mnFrameType == SwFrameType::Row; |
1203 | } |
1204 | inline bool SwFrame::IsCellFrame() const |
1205 | { |
1206 | return mnFrameType == SwFrameType::Cell; |
1207 | } |
1208 | inline bool SwFrame::IsContentFrame() const |
1209 | { |
1210 | return bool(GetType() & FRM_CNTNT(SwFrameType::Txt | SwFrameType::NoTxt)); |
1211 | } |
1212 | inline bool SwFrame::IsTextFrame() const |
1213 | { |
1214 | return mnFrameType == SwFrameType::Txt; |
1215 | } |
1216 | inline bool SwFrame::IsNoTextFrame() const |
1217 | { |
1218 | return mnFrameType == SwFrameType::NoTxt; |
1219 | } |
1220 | inline bool SwFrame::IsFlowFrame() const |
1221 | { |
1222 | return bool(GetType() & (FRM_CNTNT(SwFrameType::Txt | SwFrameType::NoTxt)|SwFrameType::Tab|SwFrameType::Section)); |
1223 | } |
1224 | inline bool SwFrame::IsRetoucheFrame() const |
1225 | { |
1226 | return bool(GetType() & (FRM_CNTNT(SwFrameType::Txt | SwFrameType::NoTxt)|SwFrameType::Tab|SwFrameType::Section|SwFrameType::Ftn)); |
1227 | } |
1228 | inline bool SwFrame::IsAccessibleFrame() const |
1229 | { |
1230 | return bool(GetType() & FRM_ACCESSIBLE(SwFrameType::Root | SwFrameType::Page | SwFrameType::Header | SwFrameType::Footer | SwFrameType::Ftn | SwFrameType::Fly | SwFrameType ::Tab | SwFrameType::Cell | SwFrameType::Txt)); |
1231 | } |
1232 | |
1233 | //use this to protect a SwFrame for a given scope from getting deleted |
1234 | class SwFrameDeleteGuard |
1235 | { |
1236 | private: |
1237 | SwFrame *m_pForbidFrame; |
1238 | public: |
1239 | //Flag pFrame for SwFrameDeleteGuard lifetime that we shouldn't delete |
1240 | //it in e.g. SwSectionFrame::MergeNext etc because we will need it |
1241 | //again after the SwFrameDeleteGuard dtor |
1242 | explicit SwFrameDeleteGuard(SwFrame* pFrame) |
1243 | : m_pForbidFrame((pFrame && !pFrame->IsDeleteForbidden()) ? |
1244 | pFrame : nullptr) |
1245 | { |
1246 | if (m_pForbidFrame) |
1247 | m_pForbidFrame->ForbidDelete(); |
1248 | } |
1249 | |
1250 | SwFrameDeleteGuard(const SwFrameDeleteGuard&) =delete; |
1251 | |
1252 | ~SwFrameDeleteGuard() |
1253 | { |
1254 | if (m_pForbidFrame) |
1255 | m_pForbidFrame->AllowDelete(); |
1256 | } |
1257 | |
1258 | SwFrameDeleteGuard& operator=(const SwFrameDeleteGuard&) =delete; |
1259 | }; |
1260 | |
1261 | typedef long (SwFrame::*SwFrameGet)() const; |
1262 | typedef bool (SwFrame::*SwFrameMax)( long ); |
1263 | typedef void (SwFrame::*SwFrameMakePos)( const SwFrame*, const SwFrame*, bool ); |
1264 | typedef long (*SwOperator)( long, long ); |
1265 | typedef void (SwFrame::*SwFrameSet)( long, long ); |
1266 | |
1267 | struct SwRectFnCollection |
1268 | { |
1269 | SwRectGet fnGetTop; |
1270 | SwRectGet fnGetBottom; |
1271 | SwRectGet fnGetLeft; |
1272 | SwRectGet fnGetRight; |
1273 | SwRectGet fnGetWidth; |
1274 | SwRectGet fnGetHeight; |
1275 | SwRectPoint fnGetPos; |
1276 | SwRectSize fnGetSize; |
1277 | |
1278 | SwRectSet fnSetTop; |
1279 | SwRectSet fnSetBottom; |
1280 | SwRectSet fnSetLeft; |
1281 | SwRectSet fnSetRight; |
1282 | SwRectSet fnSetWidth; |
1283 | SwRectSet fnSetHeight; |
1284 | |
1285 | SwRectSet fnSubTop; |
1286 | SwRectSet fnAddBottom; |
1287 | SwRectSet fnSubLeft; |
1288 | SwRectSet fnAddRight; |
1289 | SwRectSet fnAddWidth; |
1290 | SwRectSet fnAddHeight; |
1291 | |
1292 | SwRectSet fnSetPosX; |
1293 | SwRectSet fnSetPosY; |
1294 | |
1295 | SwFrameGet fnGetTopMargin; |
1296 | SwFrameGet fnGetBottomMargin; |
1297 | SwFrameGet fnGetLeftMargin; |
1298 | SwFrameGet fnGetRightMargin; |
1299 | SwFrameSet fnSetXMargins; |
1300 | SwFrameSet fnSetYMargins; |
1301 | SwFrameGet fnGetPrtTop; |
1302 | SwFrameGet fnGetPrtBottom; |
1303 | SwFrameGet fnGetPrtLeft; |
1304 | SwFrameGet fnGetPrtRight; |
1305 | SwRectDist fnTopDist; |
1306 | SwRectDist fnBottomDist; |
1307 | SwRectDist fnLeftDist; |
1308 | SwRectDist fnRightDist; |
1309 | SwFrameMax fnSetLimit; |
1310 | SwRectMax fnOverStep; |
1311 | |
1312 | SwRectSetPos fnSetPos; |
1313 | SwFrameMakePos fnMakePos; |
1314 | SwOperator fnXDiff; |
1315 | SwOperator fnYDiff; |
1316 | SwOperator fnXInc; |
1317 | SwOperator fnYInc; |
1318 | |
1319 | SwRectSetTwice fnSetLeftAndWidth; |
1320 | SwRectSetTwice fnSetTopAndHeight; |
1321 | }; |
1322 | |
1323 | typedef SwRectFnCollection* SwRectFn; |
1324 | |
1325 | // This class allows to use proper methods regardless of orientation (LTR/RTL, horizontal or vertical) |
1326 | extern SwRectFn fnRectHori, fnRectVert, fnRectVertL2R, fnRectVertL2RB2T; |
1327 | class SwRectFnSet { |
1328 | public: |
1329 | explicit SwRectFnSet(const SwFrame *pFrame) |
1330 | : m_bVert(pFrame->IsVertical()) |
1331 | , m_bVertL2R(pFrame->IsVertLR()) |
1332 | , m_bVertL2RB2T(pFrame->IsVertLRBT()) |
1333 | { |
1334 | m_fnRect = m_bVert ? (m_bVertL2R ? (m_bVertL2RB2T ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert) : fnRectHori; |
1335 | } |
1336 | |
1337 | void Refresh(const SwFrame *pFrame) |
1338 | { |
1339 | m_bVert = pFrame->IsVertical(); |
1340 | m_bVertL2R = pFrame->IsVertLR(); |
1341 | m_bVertL2RB2T = pFrame->IsVertLRBT(); |
1342 | m_fnRect = m_bVert ? (m_bVertL2R ? (m_bVertL2RB2T ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert) : fnRectHori; |
1343 | } |
1344 | |
1345 | bool IsVert() const { return m_bVert; } |
1346 | bool IsVertL2R() const { return m_bVertL2R; } |
1347 | SwRectFn FnRect() const { return m_fnRect; } |
1348 | |
1349 | bool PosDiff(const SwRect &rRect1, const SwRect &rRect2) const |
1350 | { |
1351 | return ((rRect1.*m_fnRect->fnGetTop)() != (rRect2.*m_fnRect->fnGetTop)() |
1352 | || (rRect1.*m_fnRect->fnGetLeft)() != (rRect2.*m_fnRect->fnGetLeft)()); |
1353 | } |
1354 | |
1355 | long GetTop (const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetTop) (); } |
1356 | long GetBottom(const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetBottom)(); } |
1357 | long GetLeft (const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetLeft) (); } |
1358 | long GetRight (const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetRight) (); } |
1359 | long GetWidth (const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetWidth) (); } |
1360 | long GetHeight(const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetHeight)(); } |
1361 | Point GetPos (const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetPos) (); } |
1362 | Size GetSize (const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetSize) (); } |
1363 | |
1364 | void SetTop (SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSetTop) (nNew); } |
1365 | void SetBottom(SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSetBottom)(nNew); } |
1366 | void SetLeft (SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSetLeft) (nNew); } |
1367 | void SetRight (SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSetRight) (nNew); } |
1368 | void SetWidth (SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSetWidth) (nNew); } |
1369 | void SetHeight(SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSetHeight)(nNew); } |
1370 | |
1371 | void SubTop (SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSubTop) (nNew); } |
1372 | void AddBottom(SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnAddBottom)(nNew); } |
1373 | void SubLeft (SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSubLeft) (nNew); } |
1374 | void AddRight (SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnAddRight) (nNew); } |
1375 | void AddWidth (SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnAddWidth) (nNew); } |
1376 | void AddHeight(SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnAddHeight)(nNew); } |
1377 | |
1378 | void SetPosX(SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSetPosX)(nNew); } |
1379 | void SetPosY(SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSetPosY)(nNew); } |
1380 | |
1381 | long GetTopMargin (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetTopMargin) (); } |
1382 | long GetBottomMargin(const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetBottomMargin)(); } |
1383 | long GetLeftMargin (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetLeftMargin) (); } |
1384 | long GetRightMargin (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetRightMargin) (); } |
1385 | void SetXMargins(SwFrame& rFrame, long nLeft, long nRight) const { (rFrame.*m_fnRect->fnSetXMargins)(nLeft, nRight); } |
1386 | void SetYMargins(SwFrame& rFrame, long nTop, long nBottom) const { (rFrame.*m_fnRect->fnSetYMargins)(nTop, nBottom); } |
1387 | long GetPrtTop (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetPrtTop) (); } |
1388 | long GetPrtBottom (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetPrtBottom) (); } |
1389 | long GetPrtLeft (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetPrtLeft) (); } |
1390 | long GetPrtRight (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetPrtRight) (); } |
1391 | long TopDist (const SwRect& rRect, long nPos) const { return (rRect.*m_fnRect->fnTopDist) (nPos); } |
1392 | long BottomDist(const SwRect& rRect, long nPos) const { return (rRect.*m_fnRect->fnBottomDist) (nPos); } |
1393 | long LeftDist (const SwRect& rRect, long nPos) const { return (rRect.*m_fnRect->fnLeftDist) (nPos); } |
1394 | long RightDist (const SwRect& rRect, long nPos) const { return (rRect.*m_fnRect->fnRightDist) (nPos); } |
1395 | void SetLimit (SwFrame& rFrame, long nNew) const { (rFrame.*m_fnRect->fnSetLimit) (nNew); } |
1396 | bool OverStep (const SwRect& rRect, long nPos) const { return (rRect.*m_fnRect->fnOverStep) (nPos); } |
1397 | |
1398 | void SetPos(SwRect& rRect, const Point& rNew) const { (rRect.*m_fnRect->fnSetPos)(rNew); } |
1399 | void MakePos(SwFrame& rFrame, const SwFrame* pUp, const SwFrame* pPrv, bool bNotify) const { (rFrame.*m_fnRect->fnMakePos)(pUp, pPrv, bNotify); } |
1400 | long XDiff(long n1, long n2) const { return (m_fnRect->fnXDiff) (n1, n2); } |
1401 | long YDiff(long n1, long n2) const { return (m_fnRect->fnYDiff) (n1, n2); } |
1402 | long XInc (long n1, long n2) const { return (m_fnRect->fnXInc) (n1, n2); } |
1403 | long YInc (long n1, long n2) const { return (m_fnRect->fnYInc) (n1, n2); } |
1404 | |
1405 | void SetLeftAndWidth(SwRect& rRect, long nLeft, long nWidth) const { (rRect.*m_fnRect->fnSetLeftAndWidth)(nLeft, nWidth); } |
1406 | void SetTopAndHeight(SwRect& rRect, long nTop, long nHeight) const { (rRect.*m_fnRect->fnSetTopAndHeight)(nTop, nHeight); } |
1407 | |
1408 | private: |
1409 | bool m_bVert; |
1410 | bool m_bVertL2R; |
1411 | bool m_bVertL2RB2T; |
1412 | SwRectFn m_fnRect; |
1413 | }; |
1414 | |
1415 | #endif |
1416 | |
1417 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |