File: | home/maarten/src/libreoffice/core/include/tools/ref.hxx |
Warning: | line 95, column 31 Use of memory after it is freed |
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 <sal/config.h> | |||
21 | ||||
22 | #include <string_view> | |||
23 | ||||
24 | #include "DomainMapperTableHandler.hxx" | |||
25 | #include "DomainMapper_Impl.hxx" | |||
26 | #include "StyleSheetTable.hxx" | |||
27 | #include <com/sun/star/style/ParagraphAdjust.hpp> | |||
28 | #include <com/sun/star/table/TableBorderDistances.hpp> | |||
29 | #include <com/sun/star/table/TableBorder.hpp> | |||
30 | #include <com/sun/star/table/BorderLine2.hpp> | |||
31 | #include <com/sun/star/table/BorderLineStyle.hpp> | |||
32 | #include <com/sun/star/table/XCellRange.hpp> | |||
33 | #include <com/sun/star/text/HoriOrientation.hpp> | |||
34 | #include <com/sun/star/text/SizeType.hpp> | |||
35 | #include <com/sun/star/text/XTextField.hpp> | |||
36 | #include <com/sun/star/text/XTextRangeCompare.hpp> | |||
37 | #include <com/sun/star/beans/XPropertySet.hpp> | |||
38 | #include <com/sun/star/beans/XPropertyState.hpp> | |||
39 | #include <com/sun/star/container/XEnumeration.hpp> | |||
40 | #include <com/sun/star/container/XEnumerationAccess.hpp> | |||
41 | #include <com/sun/star/drawing/FillStyle.hpp> | |||
42 | #include "TablePositionHandler.hxx" | |||
43 | #include "TagLogger.hxx" | |||
44 | #include "util.hxx" | |||
45 | #include <osl/diagnose.h> | |||
46 | #include <sal/log.hxx> | |||
47 | #include <tools/diagnose_ex.h> | |||
48 | #include <comphelper/sequence.hxx> | |||
49 | #include <comphelper/propertyvalue.hxx> | |||
50 | #include <com/sun/star/lang/IndexOutOfBoundsException.hpp> | |||
51 | #include <boost/lexical_cast.hpp> | |||
52 | ||||
53 | #ifdef DBG_UTIL | |||
54 | #include "PropertyMapHelper.hxx" | |||
55 | #include <rtl/ustring.hxx> | |||
56 | #endif | |||
57 | ||||
58 | namespace writerfilter::dmapper { | |||
59 | ||||
60 | using namespace ::com::sun::star; | |||
61 | using namespace ::std; | |||
62 | ||||
63 | #define DEF_BORDER_DIST190 190 //0,19cm | |||
64 | #define CNF_FIRST_ROW0x800 0x800 | |||
65 | #define CNF_LAST_ROW0x400 0x400 | |||
66 | #define CNF_FIRST_COLUMN0x200 0x200 | |||
67 | #define CNF_LAST_COLUMN0x100 0x100 | |||
68 | #define CNF_ODD_VBAND0x080 0x080 | |||
69 | #define CNF_EVEN_VBAND0x040 0x040 | |||
70 | #define CNF_ODD_HBAND0x020 0x020 | |||
71 | #define CNF_EVEN_HBAND0x010 0x010 | |||
72 | #define CNF_FIRST_ROW_LAST_COLUMN0x008 0x008 | |||
73 | #define CNF_FIRST_ROW_FIRST_COLUMN0x004 0x004 | |||
74 | #define CNF_LAST_ROW_LAST_COLUMN0x002 0x002 | |||
75 | #define CNF_LAST_ROW_FIRST_COLUMN0x001 0x001 | |||
76 | #define CNF_ALL0xFFF 0xFFF | |||
77 | ||||
78 | // total number of table columns | |||
79 | #define MAXTABLECELLS63 63 | |||
80 | ||||
81 | DomainMapperTableHandler::DomainMapperTableHandler( | |||
82 | css::uno::Reference<css::text::XTextAppendAndConvert> const& xText, | |||
83 | DomainMapper_Impl& rDMapper_Impl) | |||
84 | : m_xText(xText), | |||
85 | m_rDMapper_Impl( rDMapper_Impl ), | |||
86 | m_bHadFootOrEndnote(false) | |||
87 | { | |||
88 | } | |||
89 | ||||
90 | DomainMapperTableHandler::~DomainMapperTableHandler() | |||
91 | { | |||
92 | } | |||
93 | ||||
94 | void DomainMapperTableHandler::startTable(const TablePropertyMapPtr& pProps) | |||
95 | { | |||
96 | m_aTableProperties = pProps; | |||
97 | m_aTableRanges.clear(); | |||
98 | ||||
99 | #ifdef DBG_UTIL | |||
100 | TagLogger::getInstance().startElement("tablehandler.table"); | |||
101 | ||||
102 | if (pProps) | |||
103 | pProps->dumpXml(); | |||
104 | #endif | |||
105 | } | |||
106 | ||||
107 | static void lcl_mergeBorder( PropertyIds nId, const PropertyMapPtr& pOrig, const PropertyMapPtr& pDest ) | |||
108 | { | |||
109 | std::optional<PropertyMap::Property> pOrigVal = pOrig->getProperty(nId); | |||
110 | ||||
111 | if ( pOrigVal ) | |||
112 | { | |||
113 | pDest->Insert( nId, pOrigVal->second, false ); | |||
114 | } | |||
115 | } | |||
116 | ||||
117 | static void lcl_computeCellBorders( const PropertyMapPtr& pTableBorders, const PropertyMapPtr& pCellProps, | |||
118 | sal_uInt32 nCell, sal_uInt32 nFirstCell, sal_uInt32 nLastCell, sal_Int32 nRow, bool bIsEndRow, bool bMergedVertically ) | |||
119 | { | |||
120 | const bool bIsStartCol = nCell == nFirstCell; | |||
121 | const bool bIsEndCol = nCell == nLastCell; | |||
122 | std::optional<PropertyMap::Property> pVerticalVal = pCellProps->getProperty(META_PROP_VERTICAL_BORDER); | |||
123 | std::optional<PropertyMap::Property> pHorizontalVal = pCellProps->getProperty(META_PROP_HORIZONTAL_BORDER); | |||
124 | ||||
125 | // Handle the vertical and horizontal borders | |||
126 | uno::Any aVertProp; | |||
127 | if ( !pVerticalVal) | |||
128 | { | |||
129 | pVerticalVal = pTableBorders->getProperty(META_PROP_VERTICAL_BORDER); | |||
130 | if ( pVerticalVal ) | |||
131 | aVertProp = pVerticalVal->second; | |||
132 | } | |||
133 | else | |||
134 | { | |||
135 | aVertProp = pVerticalVal->second; | |||
136 | pCellProps->Erase( pVerticalVal->first ); | |||
137 | } | |||
138 | ||||
139 | uno::Any aHorizProp; | |||
140 | if ( !pHorizontalVal ) | |||
141 | { | |||
142 | pHorizontalVal = pTableBorders->getProperty(META_PROP_HORIZONTAL_BORDER); | |||
143 | if ( pHorizontalVal ) | |||
144 | aHorizProp = pHorizontalVal->second; | |||
145 | } | |||
146 | else | |||
147 | { | |||
148 | aHorizProp = pHorizontalVal->second; | |||
149 | pCellProps->Erase( pHorizontalVal->first ); | |||
150 | } | |||
151 | ||||
152 | if ( bIsStartCol ) | |||
153 | lcl_mergeBorder( PROP_LEFT_BORDER, pTableBorders, pCellProps ); | |||
154 | ||||
155 | if ( bIsEndCol ) | |||
156 | lcl_mergeBorder( PROP_RIGHT_BORDER, pTableBorders, pCellProps ); | |||
157 | ||||
158 | // <w:insideV> counts if there are multiple cells in this row. | |||
159 | if ( pVerticalVal ) | |||
160 | { | |||
161 | if ( !bIsEndCol && nCell >= nFirstCell ) | |||
162 | pCellProps->Insert( PROP_RIGHT_BORDER, aVertProp, false ); | |||
163 | if ( !bIsStartCol && nCell <= nLastCell ) | |||
164 | pCellProps->Insert( PROP_LEFT_BORDER, aVertProp, false ); | |||
165 | } | |||
166 | ||||
167 | if ( nRow == 0 ) | |||
168 | { | |||
169 | lcl_mergeBorder( PROP_TOP_BORDER, pTableBorders, pCellProps ); | |||
170 | if ( pHorizontalVal && !bMergedVertically ) | |||
171 | pCellProps->Insert( PROP_BOTTOM_BORDER, aHorizProp, false ); | |||
172 | } | |||
173 | ||||
174 | if ( bMergedVertically ) | |||
175 | lcl_mergeBorder( PROP_BOTTOM_BORDER, pTableBorders, pCellProps ); | |||
176 | ||||
177 | if ( bIsEndRow ) | |||
178 | { | |||
179 | lcl_mergeBorder( PROP_BOTTOM_BORDER, pTableBorders, pCellProps ); | |||
180 | if ( pHorizontalVal ) | |||
181 | pCellProps->Insert( PROP_TOP_BORDER, aHorizProp, false ); | |||
182 | } | |||
183 | ||||
184 | if ( nRow > 0 && !bIsEndRow ) | |||
185 | { | |||
186 | if ( pHorizontalVal ) | |||
187 | { | |||
188 | pCellProps->Insert( PROP_TOP_BORDER, aHorizProp, false ); | |||
189 | pCellProps->Insert( PROP_BOTTOM_BORDER, aHorizProp, false ); | |||
190 | } | |||
191 | } | |||
192 | } | |||
193 | ||||
194 | #ifdef DBG_UTIL | |||
195 | ||||
196 | static void lcl_debug_BorderLine(table::BorderLine const & rLine) | |||
197 | { | |||
198 | TagLogger::getInstance().startElement("BorderLine"); | |||
199 | TagLogger::getInstance().attribute("Color", rLine.Color); | |||
200 | TagLogger::getInstance().attribute("InnerLineWidth", rLine.InnerLineWidth); | |||
201 | TagLogger::getInstance().attribute("OuterLineWidth", rLine.OuterLineWidth); | |||
202 | TagLogger::getInstance().attribute("LineDistance", rLine.LineDistance); | |||
203 | TagLogger::getInstance().endElement(); | |||
204 | } | |||
205 | ||||
206 | static void lcl_debug_TableBorder(table::TableBorder const & rBorder) | |||
207 | { | |||
208 | TagLogger::getInstance().startElement("TableBorder"); | |||
209 | lcl_debug_BorderLine(rBorder.TopLine); | |||
210 | TagLogger::getInstance().attribute("IsTopLineValid", sal_uInt32(rBorder.IsTopLineValid)); | |||
211 | lcl_debug_BorderLine(rBorder.BottomLine); | |||
212 | TagLogger::getInstance().attribute("IsBottomLineValid", sal_uInt32(rBorder.IsBottomLineValid)); | |||
213 | lcl_debug_BorderLine(rBorder.LeftLine); | |||
214 | TagLogger::getInstance().attribute("IsLeftLineValid", sal_uInt32(rBorder.IsLeftLineValid)); | |||
215 | lcl_debug_BorderLine(rBorder.RightLine); | |||
216 | TagLogger::getInstance().attribute("IsRightLineValid", sal_uInt32(rBorder.IsRightLineValid)); | |||
217 | lcl_debug_BorderLine(rBorder.VerticalLine); | |||
218 | TagLogger::getInstance().attribute("IsVerticalLineValid", sal_uInt32(rBorder.IsVerticalLineValid)); | |||
219 | lcl_debug_BorderLine(rBorder.HorizontalLine); | |||
220 | TagLogger::getInstance().attribute("IsHorizontalLineValid", sal_uInt32(rBorder.IsHorizontalLineValid)); | |||
221 | TagLogger::getInstance().attribute("Distance", rBorder.Distance); | |||
222 | TagLogger::getInstance().attribute("IsDistanceValid", sal_uInt32(rBorder.IsDistanceValid)); | |||
223 | TagLogger::getInstance().endElement(); | |||
224 | } | |||
225 | #endif | |||
226 | ||||
227 | struct TableInfo | |||
228 | { | |||
229 | sal_Int32 nLeftBorderDistance; | |||
230 | sal_Int32 nRightBorderDistance; | |||
231 | sal_Int32 nTopBorderDistance; | |||
232 | sal_Int32 nBottomBorderDistance; | |||
233 | sal_Int32 nTblLook; | |||
234 | sal_Int32 nNestLevel; | |||
235 | PropertyMapPtr pTableDefaults; | |||
236 | PropertyMapPtr pTableBorders; | |||
237 | TableStyleSheetEntry* pTableStyle; | |||
238 | css::beans::PropertyValues aTableProperties; | |||
239 | std::vector< PropertyIds > aTablePropertyIds; | |||
240 | ||||
241 | TableInfo() | |||
242 | : nLeftBorderDistance(DEF_BORDER_DIST190) | |||
243 | , nRightBorderDistance(DEF_BORDER_DIST190) | |||
244 | , nTopBorderDistance(0) | |||
245 | , nBottomBorderDistance(0) | |||
246 | , nTblLook(0x4a0) | |||
247 | , nNestLevel(0) | |||
248 | , pTableDefaults(new PropertyMap) | |||
249 | , pTableBorders(new PropertyMap) | |||
250 | , pTableStyle(nullptr) | |||
251 | { | |||
252 | } | |||
253 | ||||
254 | }; | |||
255 | ||||
256 | namespace | |||
257 | { | |||
258 | ||||
259 | bool lcl_extractTableBorderProperty(const PropertyMapPtr& pTableProperties, const PropertyIds nId, TableInfo const & rInfo, table::BorderLine2& rLine) | |||
260 | { | |||
261 | if (!pTableProperties) | |||
262 | return false; | |||
263 | ||||
264 | const std::optional<PropertyMap::Property> aTblBorder = pTableProperties->getProperty(nId); | |||
265 | if( aTblBorder ) | |||
266 | { | |||
267 | OSL_VERIFY(aTblBorder->second >>= rLine)do { if (!(aTblBorder->second >>= rLine)) do { if (true && (!(0))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN ), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "267" ": "), "OSL_ASSERT: %s", "0"); } } while (false); } while (0); | |||
268 | ||||
269 | rInfo.pTableBorders->Insert( nId, uno::makeAny( rLine ) ); | |||
270 | rInfo.pTableDefaults->Erase( nId ); | |||
271 | ||||
272 | return true; | |||
273 | } | |||
274 | ||||
275 | return false; | |||
276 | } | |||
277 | ||||
278 | void lcl_extractHoriOrient(std::vector<beans::PropertyValue>& rFrameProperties, sal_Int32& nHoriOrient) | |||
279 | { | |||
280 | // Shifts the frame left by the given value. | |||
281 | for (const beans::PropertyValue & rFrameProperty : rFrameProperties) | |||
282 | { | |||
283 | if (rFrameProperty.Name == "HoriOrient") | |||
284 | { | |||
285 | sal_Int32 nValue = rFrameProperty.Value.get<sal_Int32>(); | |||
286 | if (nValue != text::HoriOrientation::NONE) | |||
287 | nHoriOrient = nValue; | |||
288 | return; | |||
289 | } | |||
290 | } | |||
291 | } | |||
292 | ||||
293 | void lcl_DecrementHoriOrientPosition(std::vector<beans::PropertyValue>& rFrameProperties, sal_Int32 nAmount) | |||
294 | { | |||
295 | // Shifts the frame left by the given value. | |||
296 | for (beans::PropertyValue & rPropertyValue : rFrameProperties) | |||
297 | { | |||
298 | if (rPropertyValue.Name == "HoriOrientPosition") | |||
299 | { | |||
300 | sal_Int32 nValue = rPropertyValue.Value.get<sal_Int32>(); | |||
301 | nValue -= nAmount; | |||
302 | rPropertyValue.Value <<= nValue; | |||
303 | return; | |||
304 | } | |||
305 | } | |||
306 | } | |||
307 | ||||
308 | void lcl_adjustBorderDistance(TableInfo& rInfo, const table::BorderLine2& rLeftBorder, | |||
309 | const table::BorderLine2& rRightBorder) | |||
310 | { | |||
311 | // MS Word appears to do these things to adjust the cell horizontal area: | |||
312 | // | |||
313 | // bll = left borderline width | |||
314 | // blr = right borderline width | |||
315 | // cea = cell's edit area rectangle | |||
316 | // cea_w = cea width | |||
317 | // cml = cell's left margin (padding) defined in cell settings | |||
318 | // cmr = cell's right margin (padding) defined in cell settings | |||
319 | // cw = cell width (distance between middles of left borderline and right borderline) | |||
320 | // pad_l = actual cea left padding = (its left pos relative to middle of bll) | |||
321 | // pad_r = actual cea right padding = abs (its right pos relative to middle of blr) | |||
322 | // | |||
323 | // pad_l = max(bll/2, cml) -> cea does not overlap left borderline | |||
324 | // cea_w = cw-max(pad_l+blr/2, cml+cmr) -> cea does not overlap right borderline | |||
325 | // pad_r = max(pad_l+blr/2, cml+cmr) - pad_l | |||
326 | // | |||
327 | // It means that e.g. for border widths of 6 pt (~2.12 mm), left margin 0 mm, and right margin | |||
328 | // 2 mm, actual left and right margins will (unexpectedly) coincide with inner edges of cell's | |||
329 | // borderlines - the right margin won't create spacing between right of edit rectangle and the | |||
330 | // inner edge of right borderline. | |||
331 | ||||
332 | const sal_Int32 nActualL | |||
333 | = std::max<sal_Int32>(rLeftBorder.LineWidth / 2, rInfo.nLeftBorderDistance); | |||
334 | const sal_Int32 nActualR | |||
335 | = std::max<sal_Int32>(nActualL + rRightBorder.LineWidth / 2, | |||
336 | rInfo.nLeftBorderDistance + rInfo.nRightBorderDistance) | |||
337 | - nActualL; | |||
338 | rInfo.nLeftBorderDistance = nActualL; | |||
339 | rInfo.nRightBorderDistance = nActualR; | |||
340 | } | |||
341 | ||||
342 | } | |||
343 | ||||
344 | TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo & rInfo, std::vector<beans::PropertyValue>& rFrameProperties) | |||
345 | { | |||
346 | // will receive the table style if any | |||
347 | TableStyleSheetEntry* pTableStyle = nullptr; | |||
348 | ||||
349 | if( m_aTableProperties ) | |||
350 | { | |||
351 | //create properties from the table attributes | |||
352 | //...pPropMap->Insert( PROP_LEFT_MARGIN, uno::makeAny( m_nLeftMargin - m_nGapHalf )); | |||
353 | //pPropMap->Insert( PROP_HORI_ORIENT, uno::makeAny( text::HoriOrientation::RIGHT )); | |||
354 | sal_Int32 nGapHalf = 0; | |||
355 | sal_Int32 nLeftMargin = 0; | |||
356 | ||||
357 | comphelper::SequenceAsHashMap aGrabBag; | |||
358 | ||||
359 | if (nullptr != m_rDMapper_Impl.getTableManager().getCurrentTableRealPosition()) | |||
360 | { | |||
361 | TablePositionHandler *pTablePositions = m_rDMapper_Impl.getTableManager().getCurrentTableRealPosition(); | |||
362 | ||||
363 | uno::Sequence< beans::PropertyValue > aGrabBagTS( 10 ); | |||
364 | ||||
365 | aGrabBagTS[0].Name = "bottomFromText"; | |||
366 | aGrabBagTS[0].Value <<= pTablePositions->getBottomFromText(); | |||
367 | ||||
368 | aGrabBagTS[1].Name = "horzAnchor"; | |||
369 | aGrabBagTS[1].Value <<= pTablePositions->getHorzAnchor(); | |||
370 | ||||
371 | aGrabBagTS[2].Name = "leftFromText"; | |||
372 | aGrabBagTS[2].Value <<= pTablePositions->getLeftFromText(); | |||
373 | ||||
374 | aGrabBagTS[3].Name = "rightFromText"; | |||
375 | aGrabBagTS[3].Value <<= pTablePositions->getRightFromText(); | |||
376 | ||||
377 | aGrabBagTS[4].Name = "tblpX"; | |||
378 | aGrabBagTS[4].Value <<= pTablePositions->getX(); | |||
379 | ||||
380 | aGrabBagTS[5].Name = "tblpXSpec"; | |||
381 | aGrabBagTS[5].Value <<= pTablePositions->getXSpec(); | |||
382 | ||||
383 | aGrabBagTS[6].Name = "tblpY"; | |||
384 | aGrabBagTS[6].Value <<= pTablePositions->getY(); | |||
385 | ||||
386 | aGrabBagTS[7].Name = "tblpYSpec"; | |||
387 | aGrabBagTS[7].Value <<= pTablePositions->getYSpec(); | |||
388 | ||||
389 | aGrabBagTS[8].Name = "topFromText"; | |||
390 | aGrabBagTS[8].Value <<= pTablePositions->getTopFromText(); | |||
391 | ||||
392 | aGrabBagTS[9].Name = "vertAnchor"; | |||
393 | aGrabBagTS[9].Value <<= pTablePositions->getVertAnchor(); | |||
394 | ||||
395 | aGrabBag["TablePosition"] <<= aGrabBagTS; | |||
396 | } | |||
397 | ||||
398 | std::optional<PropertyMap::Property> aTableStyleVal = m_aTableProperties->getProperty(META_PROP_TABLE_STYLE_NAME); | |||
399 | if(aTableStyleVal) | |||
400 | { | |||
401 | // Apply table style properties recursively | |||
402 | OUString sTableStyleName; | |||
403 | aTableStyleVal->second >>= sTableStyleName; | |||
404 | StyleSheetTablePtr pStyleSheetTable = m_rDMapper_Impl.GetStyleSheetTable(); | |||
405 | const StyleSheetEntryPtr pStyleSheet = pStyleSheetTable->FindStyleSheetByISTD( sTableStyleName ); | |||
406 | pTableStyle = dynamic_cast<TableStyleSheetEntry*>( pStyleSheet.get( ) ); | |||
407 | m_aTableProperties->Erase( aTableStyleVal->first ); | |||
408 | ||||
409 | aGrabBag["TableStyleName"] <<= sTableStyleName; | |||
410 | ||||
411 | if( pStyleSheet ) | |||
412 | { | |||
413 | // First get the style properties, then the table ones | |||
414 | PropertyMapPtr pTableProps( m_aTableProperties.get() ); | |||
415 | TablePropertyMapPtr pEmptyProps( new TablePropertyMap ); | |||
416 | ||||
417 | m_aTableProperties = pEmptyProps; | |||
418 | ||||
419 | PropertyMapPtr pMergedProperties = pStyleSheet->GetMergedInheritedProperties(pStyleSheetTable); | |||
420 | ||||
421 | table::BorderLine2 aBorderLine; | |||
422 | TableInfo rStyleInfo; | |||
423 | if (lcl_extractTableBorderProperty(pMergedProperties, PROP_TOP_BORDER, rStyleInfo, aBorderLine)) | |||
424 | { | |||
425 | aGrabBag["TableStyleTopBorder"] <<= aBorderLine; | |||
426 | } | |||
427 | if (lcl_extractTableBorderProperty(pMergedProperties, PROP_BOTTOM_BORDER, rStyleInfo, aBorderLine)) | |||
428 | { | |||
429 | aGrabBag["TableStyleBottomBorder"] <<= aBorderLine; | |||
430 | } | |||
431 | if (lcl_extractTableBorderProperty(pMergedProperties, PROP_LEFT_BORDER, rStyleInfo, aBorderLine)) | |||
432 | { | |||
433 | aGrabBag["TableStyleLeftBorder"] <<= aBorderLine; | |||
434 | } | |||
435 | if (lcl_extractTableBorderProperty(pMergedProperties, PROP_RIGHT_BORDER, rStyleInfo, aBorderLine)) | |||
436 | { | |||
437 | aGrabBag["TableStyleRightBorder"] <<= aBorderLine; | |||
438 | } | |||
439 | ||||
440 | #ifdef DBG_UTIL | |||
441 | TagLogger::getInstance().startElement("mergedProps"); | |||
442 | if (pMergedProperties) | |||
443 | pMergedProperties->dumpXml(); | |||
444 | TagLogger::getInstance().endElement(); | |||
445 | #endif | |||
446 | ||||
447 | m_aTableProperties->InsertProps(pMergedProperties); | |||
448 | m_aTableProperties->InsertProps(pTableProps); | |||
449 | ||||
450 | #ifdef DBG_UTIL | |||
451 | TagLogger::getInstance().startElement("TableProperties"); | |||
452 | m_aTableProperties->dumpXml(); | |||
453 | TagLogger::getInstance().endElement(); | |||
454 | #endif | |||
455 | if (pTableStyle) | |||
456 | { | |||
457 | // apply tblHeader setting of the table style | |||
458 | PropertyMapPtr pHeaderStyleProps = pTableStyle->GetProperties(CNF_FIRST_ROW0x800); | |||
459 | if ( pHeaderStyleProps->getProperty(PROP_HEADER_ROW_COUNT) ) | |||
460 | m_aTableProperties->Insert(PROP_HEADER_ROW_COUNT, uno::makeAny( sal_Int32(1)), false); | |||
461 | } | |||
462 | } | |||
463 | } | |||
464 | ||||
465 | // This is the one preserving just all the table look attributes. | |||
466 | std::optional<PropertyMap::Property> oTableLook = m_aTableProperties->getProperty(META_PROP_TABLE_LOOK); | |||
467 | if (oTableLook) | |||
468 | { | |||
469 | aGrabBag["TableStyleLook"] = oTableLook->second; | |||
470 | m_aTableProperties->Erase(oTableLook->first); | |||
471 | } | |||
472 | ||||
473 | // This is just the "val" attribute's numeric value. | |||
474 | const std::optional<PropertyMap::Property> aTblLook = m_aTableProperties->getProperty(PROP_TBL_LOOK); | |||
475 | if(aTblLook) | |||
476 | { | |||
477 | aTblLook->second >>= rInfo.nTblLook; | |||
478 | m_aTableProperties->Erase( aTblLook->first ); | |||
479 | } | |||
480 | ||||
481 | // apply cell margin settings of the table style | |||
482 | const std::optional<PropertyMap::Property> oLeftMargin = m_aTableProperties->getProperty(META_PROP_CELL_MAR_LEFT); | |||
483 | if (oLeftMargin) | |||
484 | { | |||
485 | oLeftMargin->second >>= rInfo.nLeftBorderDistance; | |||
486 | m_aTableProperties->Erase(oLeftMargin->first); | |||
487 | } | |||
488 | const std::optional<PropertyMap::Property> oRightMargin = m_aTableProperties->getProperty(META_PROP_CELL_MAR_RIGHT); | |||
489 | if (oRightMargin) | |||
490 | { | |||
491 | oRightMargin->second >>= rInfo.nRightBorderDistance; | |||
492 | m_aTableProperties->Erase(oRightMargin->first); | |||
493 | } | |||
494 | const std::optional<PropertyMap::Property> oTopMargin = m_aTableProperties->getProperty(META_PROP_CELL_MAR_TOP); | |||
495 | if (oTopMargin) | |||
496 | { | |||
497 | oTopMargin->second >>= rInfo.nTopBorderDistance; | |||
498 | m_aTableProperties->Erase(oTopMargin->first); | |||
499 | } | |||
500 | const std::optional<PropertyMap::Property> oBottomMargin = m_aTableProperties->getProperty(META_PROP_CELL_MAR_BOTTOM); | |||
501 | if (oBottomMargin) | |||
502 | { | |||
503 | oBottomMargin->second >>= rInfo.nBottomBorderDistance; | |||
504 | m_aTableProperties->Erase(oBottomMargin->first); | |||
505 | } | |||
506 | ||||
507 | // Set the table default attributes for the cells | |||
508 | rInfo.pTableDefaults->InsertProps(m_aTableProperties.get()); | |||
509 | ||||
510 | #ifdef DBG_UTIL | |||
511 | TagLogger::getInstance().startElement("TableDefaults"); | |||
512 | rInfo.pTableDefaults->dumpXml(); | |||
513 | TagLogger::getInstance().endElement(); | |||
514 | #endif | |||
515 | ||||
516 | if (!aGrabBag.empty()) | |||
517 | { | |||
518 | m_aTableProperties->Insert( PROP_TABLE_INTEROP_GRAB_BAG, uno::makeAny( aGrabBag.getAsConstPropertyValueList() ) ); | |||
519 | } | |||
520 | ||||
521 | m_aTableProperties->getValue( TablePropertyMap::GAP_HALF, nGapHalf ); | |||
522 | ||||
523 | std::optional<PropertyMap::Property> oLeftMarginFromStyle = m_aTableProperties->getProperty(PROP_LEFT_MARGIN); | |||
524 | if (oLeftMarginFromStyle) | |||
525 | { | |||
526 | oLeftMarginFromStyle->second >>= nLeftMargin; | |||
527 | // don't need to erase, we will push back the adjusted value | |||
528 | // of this (or the direct formatting, if that exists) later | |||
529 | } | |||
530 | m_aTableProperties->getValue( TablePropertyMap::LEFT_MARGIN, nLeftMargin ); | |||
531 | ||||
532 | m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_LEFT, | |||
533 | rInfo.nLeftBorderDistance ); | |||
534 | m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_RIGHT, | |||
535 | rInfo.nRightBorderDistance ); | |||
536 | m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_TOP, | |||
537 | rInfo.nTopBorderDistance ); | |||
538 | m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_BOTTOM, | |||
539 | rInfo.nBottomBorderDistance ); | |||
540 | ||||
541 | table::TableBorderDistances aDistances; | |||
542 | aDistances.IsTopDistanceValid = | |||
543 | aDistances.IsBottomDistanceValid = | |||
544 | aDistances.IsLeftDistanceValid = | |||
545 | aDistances.IsRightDistanceValid = true; | |||
546 | aDistances.TopDistance = static_cast<sal_Int16>( rInfo.nTopBorderDistance ); | |||
547 | aDistances.BottomDistance = static_cast<sal_Int16>( rInfo.nBottomBorderDistance ); | |||
548 | aDistances.LeftDistance = static_cast<sal_Int16>( rInfo.nLeftBorderDistance ); | |||
549 | aDistances.RightDistance = static_cast<sal_Int16>( rInfo.nRightBorderDistance ); | |||
550 | ||||
551 | m_aTableProperties->Insert( PROP_TABLE_BORDER_DISTANCES, uno::makeAny( aDistances ) ); | |||
552 | ||||
553 | if (!rFrameProperties.empty()) | |||
554 | lcl_DecrementHoriOrientPosition(rFrameProperties, rInfo.nLeftBorderDistance); | |||
555 | ||||
556 | // Set table above/bottom spacing to 0. | |||
557 | m_aTableProperties->Insert( PROP_TOP_MARGIN, uno::makeAny( sal_Int32( 0 ) ) ); | |||
558 | m_aTableProperties->Insert( PROP_BOTTOM_MARGIN, uno::makeAny( sal_Int32( 0 ) ) ); | |||
559 | ||||
560 | //table border settings | |||
561 | table::TableBorder aTableBorder; | |||
562 | table::BorderLine2 aBorderLine, aLeftBorder, aRightBorder; | |||
563 | ||||
564 | if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_TOP_BORDER, rInfo, aBorderLine)) | |||
565 | { | |||
566 | aTableBorder.TopLine = aBorderLine; | |||
567 | aTableBorder.IsTopLineValid = true; | |||
568 | } | |||
569 | if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_BOTTOM_BORDER, rInfo, aBorderLine)) | |||
570 | { | |||
571 | aTableBorder.BottomLine = aBorderLine; | |||
572 | aTableBorder.IsBottomLineValid = true; | |||
573 | } | |||
574 | if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_LEFT_BORDER, rInfo, aLeftBorder)) | |||
575 | { | |||
576 | aTableBorder.LeftLine = aLeftBorder; | |||
577 | aTableBorder.IsLeftLineValid = true; | |||
578 | } | |||
579 | if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_RIGHT_BORDER, rInfo, | |||
580 | aRightBorder)) | |||
581 | { | |||
582 | aTableBorder.RightLine = aRightBorder; | |||
583 | aTableBorder.IsRightLineValid = true; | |||
584 | } | |||
585 | if (lcl_extractTableBorderProperty(m_aTableProperties.get(), META_PROP_HORIZONTAL_BORDER, rInfo, aBorderLine)) | |||
586 | { | |||
587 | aTableBorder.HorizontalLine = aBorderLine; | |||
588 | aTableBorder.IsHorizontalLineValid = true; | |||
589 | } | |||
590 | if (lcl_extractTableBorderProperty(m_aTableProperties.get(), META_PROP_VERTICAL_BORDER, rInfo, aBorderLine)) | |||
591 | { | |||
592 | aTableBorder.VerticalLine = aBorderLine; | |||
593 | aTableBorder.IsVerticalLineValid = true; | |||
594 | } | |||
595 | ||||
596 | aTableBorder.Distance = 0; | |||
597 | aTableBorder.IsDistanceValid = false; | |||
598 | ||||
599 | m_aTableProperties->Insert( PROP_TABLE_BORDER, uno::makeAny( aTableBorder ) ); | |||
600 | ||||
601 | #ifdef DBG_UTIL | |||
602 | lcl_debug_TableBorder(aTableBorder); | |||
603 | #endif | |||
604 | ||||
605 | // Table position in Office is computed in 2 different ways : | |||
606 | // - top level tables: the goal is to have in-cell text starting at table indent pos (tblInd), | |||
607 | // so table's position depends on table's cells margin | |||
608 | // - nested tables: the goal is to have left-most border starting at table_indent pos | |||
609 | ||||
610 | // Only top level table position depends on border width of Column A. | |||
611 | if ( !m_aCellProperties.empty() && !m_aCellProperties[0].empty() ) | |||
612 | { | |||
613 | // aLeftBorder already contains tblBorder; overwrite if cell is different. | |||
614 | std::optional<PropertyMap::Property> aCellBorder | |||
615 | = m_aCellProperties[0][0]->getProperty(PROP_LEFT_BORDER); | |||
616 | if ( aCellBorder ) | |||
617 | aCellBorder->second >>= aLeftBorder; | |||
618 | aCellBorder = m_aCellProperties[0][0]->getProperty(PROP_RIGHT_BORDER); | |||
619 | if (aCellBorder) | |||
620 | aCellBorder->second >>= aRightBorder; | |||
621 | } | |||
622 | if (rInfo.nNestLevel == 1 && aLeftBorder.LineWidth && !rFrameProperties.empty()) | |||
623 | { | |||
624 | lcl_DecrementHoriOrientPosition(rFrameProperties, aLeftBorder.LineWidth * 0.5); | |||
625 | } | |||
626 | lcl_adjustBorderDistance(rInfo, aLeftBorder, aRightBorder); | |||
627 | ||||
628 | // tdf#106742: since MS Word 2013 (compatibilityMode >= 15), top-level tables are handled the same as nested tables; | |||
629 | // the default behavior when DOCX doesn't define "compatibilityMode" option is to add the cell spacing | |||
630 | ||||
631 | // Undefined should not be possible any more for DOCX, but it is for RTF. | |||
632 | // In any case, continue to treat undefined as version 12 during import. | |||
633 | sal_Int32 nMode = m_rDMapper_Impl.GetSettingsTable()->GetWordCompatibilityMode(); | |||
634 | ||||
635 | if (((nMode < 0) || (0 < nMode && nMode <= 14)) && rInfo.nNestLevel == 1) | |||
636 | { | |||
637 | const sal_Int32 nAdjustedMargin = nLeftMargin - nGapHalf - rInfo.nLeftBorderDistance; | |||
638 | m_aTableProperties->Insert( PROP_LEFT_MARGIN, uno::makeAny( nAdjustedMargin ) ); | |||
639 | } | |||
640 | else | |||
641 | { | |||
642 | // Writer starts a table in the middle of the border. | |||
643 | // Word starts a table at the left edge of the border, | |||
644 | // so emulate that by adding the half the width. (also see docxattributeoutput) | |||
645 | if ( rInfo.nNestLevel > 1 && nLeftMargin < 0 ) | |||
646 | nLeftMargin = 0; | |||
647 | const sal_Int32 nAdjustedMargin = nLeftMargin - nGapHalf + (aLeftBorder.LineWidth / 2); | |||
648 | m_aTableProperties->Insert( PROP_LEFT_MARGIN, uno::makeAny( nAdjustedMargin ) ); | |||
649 | } | |||
650 | ||||
651 | sal_Int32 nTableWidth = 0; | |||
652 | sal_Int32 nTableWidthType = text::SizeType::FIX; | |||
653 | m_aTableProperties->getValue( TablePropertyMap::TABLE_WIDTH, nTableWidth ); | |||
654 | m_aTableProperties->getValue( TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType ); | |||
655 | if( nTableWidthType == text::SizeType::FIX ) | |||
656 | { | |||
657 | if( nTableWidth > 0 ) | |||
658 | m_aTableProperties->Insert( PROP_WIDTH, uno::makeAny( nTableWidth )); | |||
659 | else | |||
660 | { | |||
661 | // tdf#109524: If there is no width for the table, make it simply 100% by default. | |||
662 | // TODO: use cell contents to evaluate width (according to ECMA-376-1:2016 17.18.87) | |||
663 | nTableWidth = 100; | |||
664 | nTableWidthType = text::SizeType::VARIABLE; | |||
665 | } | |||
666 | } | |||
667 | if (nTableWidthType != text::SizeType::FIX) | |||
668 | { | |||
669 | m_aTableProperties->Insert( PROP_RELATIVE_WIDTH, uno::makeAny( sal_Int16( nTableWidth ) ) ); | |||
670 | m_aTableProperties->Insert( PROP_IS_WIDTH_RELATIVE, uno::makeAny( true ) ); | |||
671 | } | |||
672 | ||||
673 | sal_Int32 nHoriOrient = text::HoriOrientation::LEFT_AND_WIDTH; | |||
674 | // Fetch Horizontal Orientation in rFrameProperties if not set in m_aTableProperties | |||
675 | if ( !m_aTableProperties->getValue( TablePropertyMap::HORI_ORIENT, nHoriOrient ) ) | |||
676 | lcl_extractHoriOrient( rFrameProperties, nHoriOrient ); | |||
677 | m_aTableProperties->Insert( PROP_HORI_ORIENT, uno::makeAny( sal_Int16(nHoriOrient) ) ); | |||
678 | //fill default value - if not available | |||
679 | m_aTableProperties->Insert( PROP_HEADER_ROW_COUNT, uno::makeAny( sal_Int32(0)), false); | |||
680 | ||||
681 | // if table is only a single row, and row is set as don't split, set the same value for the whole table. | |||
682 | if( m_aRowProperties.size() == 1 && m_aRowProperties[0] ) | |||
683 | { | |||
684 | std::optional<PropertyMap::Property> oSplitAllowed = m_aRowProperties[0]->getProperty(PROP_IS_SPLIT_ALLOWED); | |||
685 | if( oSplitAllowed ) | |||
686 | { | |||
687 | bool bRowCanSplit = true; | |||
688 | oSplitAllowed->second >>= bRowCanSplit; | |||
689 | if( !bRowCanSplit ) | |||
690 | m_aTableProperties->Insert( PROP_SPLIT, uno::makeAny(bRowCanSplit) ); | |||
691 | } | |||
692 | } | |||
693 | ||||
694 | rInfo.aTableProperties = m_aTableProperties->GetPropertyValues(); | |||
695 | rInfo.aTablePropertyIds = m_aTableProperties->GetPropertyIds(); | |||
696 | ||||
697 | #ifdef DBG_UTIL | |||
698 | TagLogger::getInstance().startElement("debug.tableprops"); | |||
699 | m_aTableProperties->dumpXml(); | |||
700 | TagLogger::getInstance().endElement(); | |||
701 | #endif | |||
702 | ||||
703 | } | |||
704 | ||||
705 | return pTableStyle; | |||
706 | } | |||
707 | ||||
708 | CellPropertyValuesSeq_t DomainMapperTableHandler::endTableGetCellProperties(TableInfo & rInfo, std::vector<HorizontallyMergedCell>& rMerges) | |||
709 | { | |||
710 | #ifdef DBG_UTIL | |||
711 | TagLogger::getInstance().startElement("getCellProperties"); | |||
712 | #endif | |||
713 | ||||
714 | CellPropertyValuesSeq_t aCellProperties( m_aCellProperties.size() ); | |||
715 | ||||
716 | if ( m_aCellProperties.empty() ) | |||
717 | { | |||
718 | #ifdef DBG_UTIL | |||
719 | TagLogger::getInstance().endElement(); | |||
720 | #endif | |||
721 | return aCellProperties; | |||
722 | } | |||
723 | // std::vector< std::vector<PropertyMapPtr> > m_aCellProperties | |||
724 | PropertyMapVector2::const_iterator aRowOfCellsIterator = m_aCellProperties.begin(); | |||
725 | PropertyMapVector2::const_iterator aRowOfCellsIteratorEnd = m_aCellProperties.end(); | |||
726 | PropertyMapVector2::const_iterator aLastRowIterator = m_aCellProperties.end() - 1; | |||
727 | sal_Int32 nRow = 0; | |||
728 | ||||
729 | css::uno::Sequence<css::beans::PropertyValues>* pCellProperties = aCellProperties.getArray(); | |||
730 | PropertyMapVector1::const_iterator aRowIter = m_aRowProperties.begin(); | |||
731 | while( aRowOfCellsIterator != aRowOfCellsIteratorEnd ) | |||
732 | { | |||
733 | //aRowOfCellsIterator points to a vector of PropertyMapPtr | |||
734 | PropertyMapVector1::const_iterator aCellIterator = aRowOfCellsIterator->begin(); | |||
735 | PropertyMapVector1::const_iterator aCellIteratorEnd = aRowOfCellsIterator->end(); | |||
736 | ||||
737 | sal_Int32 nRowStyleMask = 0; | |||
738 | ||||
739 | if (aRowOfCellsIterator==m_aCellProperties.begin()) | |||
740 | { | |||
741 | if(rInfo.nTblLook&0x20) | |||
742 | nRowStyleMask |= CNF_FIRST_ROW0x800; // first row style used | |||
743 | } | |||
744 | else if (aRowOfCellsIterator==aLastRowIterator) | |||
745 | { | |||
746 | if(rInfo.nTblLook&0x40) | |||
747 | nRowStyleMask |= CNF_LAST_ROW0x400; // last row style used | |||
748 | } | |||
749 | else if (*aRowIter && (*aRowIter)->isSet(PROP_TBL_HEADER)) | |||
750 | nRowStyleMask |= CNF_FIRST_ROW0x800; // table header implies first row | |||
751 | if(!nRowStyleMask) // if no row style used yet | |||
752 | { | |||
753 | // banding used only if not first and or last row style used | |||
754 | if(!(rInfo.nTblLook&0x200)) | |||
755 | { // hbanding used | |||
756 | int n = nRow + 1; | |||
757 | if(rInfo.nTblLook&0x20) | |||
758 | n++; | |||
759 | if(n & 1) | |||
760 | nRowStyleMask = CNF_ODD_HBAND0x020; | |||
761 | else | |||
762 | nRowStyleMask = CNF_EVEN_HBAND0x010; | |||
763 | } | |||
764 | } | |||
765 | ||||
766 | // Note that this is intentionally called "cell" and not "column". | |||
767 | // Don't make the mistake that all cell x's will be in the same column. | |||
768 | // Merged cells (grid span) in a row will affect the actual column. (fake cells were added to handle gridBefore/After) | |||
769 | sal_Int32 nCell = 0; | |||
770 | pCellProperties[nRow].realloc( aRowOfCellsIterator->size() ); | |||
771 | beans::PropertyValues* pSingleCellProperties = pCellProperties[nRow].getArray(); | |||
772 | ||||
773 | while( aCellIterator != aCellIteratorEnd ) | |||
774 | { | |||
775 | PropertyMapPtr pAllCellProps( new PropertyMap ); | |||
776 | ||||
777 | PropertyMapVector1::const_iterator aLastCellIterator = aRowOfCellsIterator->end() - 1; | |||
778 | bool bIsEndCol = aCellIterator == aLastCellIterator; | |||
779 | bool bIsEndRow = aRowOfCellsIterator == aLastRowIterator; | |||
780 | ||||
781 | //aCellIterator points to a PropertyMapPtr; | |||
782 | if( *aCellIterator ) | |||
783 | { | |||
784 | // remove directly applied insideV/H borders since they are meaningless without a context (tdf#82177) | |||
785 | (*aCellIterator)->Erase(META_PROP_VERTICAL_BORDER); | |||
786 | (*aCellIterator)->Erase(META_PROP_HORIZONTAL_BORDER); | |||
787 | ||||
788 | pAllCellProps->InsertProps(rInfo.pTableDefaults); | |||
789 | ||||
790 | sal_Int32 nCellStyleMask = 0; | |||
791 | if (aCellIterator==aRowOfCellsIterator->begin()) | |||
792 | { | |||
793 | if(rInfo.nTblLook&0x80) | |||
794 | nCellStyleMask = CNF_FIRST_COLUMN0x200; // first col style used | |||
795 | } | |||
796 | else if (bIsEndCol) | |||
797 | { | |||
798 | if(rInfo.nTblLook&0x100) | |||
799 | nCellStyleMask = CNF_LAST_COLUMN0x100; // last col style used | |||
800 | } | |||
801 | if(!nCellStyleMask) // if no cell style is used yet | |||
802 | { | |||
803 | if(!(rInfo.nTblLook&0x400)) | |||
804 | { // vbanding used | |||
805 | int n = nCell + 1; | |||
806 | if(rInfo.nTblLook&0x80) | |||
807 | n++; | |||
808 | if(n & 1) | |||
809 | nCellStyleMask = CNF_ODD_VBAND0x080; | |||
810 | else | |||
811 | nCellStyleMask = CNF_EVEN_VBAND0x040; | |||
812 | } | |||
813 | } | |||
814 | sal_Int32 nCnfStyleMask = nCellStyleMask + nRowStyleMask; | |||
815 | if(nCnfStyleMask == CNF_FIRST_COLUMN0x200 + CNF_FIRST_ROW0x800) | |||
816 | nCnfStyleMask |= CNF_FIRST_ROW_FIRST_COLUMN0x004; | |||
817 | else if(nCnfStyleMask == CNF_FIRST_COLUMN0x200 + CNF_LAST_ROW0x400) | |||
818 | nCnfStyleMask |= CNF_LAST_ROW_FIRST_COLUMN0x001; | |||
819 | else if(nCnfStyleMask == CNF_LAST_COLUMN0x100 + CNF_FIRST_ROW0x800) | |||
820 | nCnfStyleMask |= CNF_FIRST_ROW_LAST_COLUMN0x008; | |||
821 | else if(nCnfStyleMask == CNF_LAST_COLUMN0x100 + CNF_LAST_ROW0x400) | |||
822 | nCnfStyleMask |= CNF_LAST_ROW_LAST_COLUMN0x002; | |||
823 | ||||
824 | if ( rInfo.pTableStyle ) | |||
825 | { | |||
826 | PropertyMapPtr pStyleProps = rInfo.pTableStyle->GetProperties( nCnfStyleMask ); | |||
827 | ||||
828 | // Check if we need to clean up some empty border definitions to match what Word does. | |||
829 | static const PropertyIds pBorders[] = | |||
830 | { | |||
831 | PROP_TOP_BORDER, PROP_LEFT_BORDER, PROP_BOTTOM_BORDER, PROP_RIGHT_BORDER | |||
832 | }; | |||
833 | for (const PropertyIds& rBorder : pBorders) | |||
834 | { | |||
835 | std::optional<PropertyMap::Property> oStyleCellBorder = pStyleProps->getProperty(rBorder); | |||
836 | std::optional<PropertyMap::Property> oDirectCellBorder = (*aCellIterator)->getProperty(rBorder); | |||
837 | if (oStyleCellBorder && oDirectCellBorder) | |||
838 | { | |||
839 | // We have a cell border from the table style and as direct formatting as well. | |||
840 | table::BorderLine2 aStyleCellBorder = oStyleCellBorder->second.get<table::BorderLine2>(); | |||
841 | table::BorderLine2 aDirectCellBorder = oDirectCellBorder->second.get<table::BorderLine2>(); | |||
842 | if (aStyleCellBorder.LineStyle != table::BorderLineStyle::NONE && aDirectCellBorder.LineStyle == table::BorderLineStyle::NONE) | |||
843 | { | |||
844 | // The style one would be visible, but then cleared away as direct formatting. | |||
845 | // Delete both, so that table formatting can become visible. | |||
846 | pStyleProps->Erase(rBorder); | |||
847 | (*aCellIterator)->Erase(rBorder); | |||
848 | } | |||
849 | else | |||
850 | { | |||
851 | std::optional<PropertyMap::Property> oTableBorder = rInfo.pTableBorders->getProperty(rBorder); | |||
852 | if (oTableBorder) | |||
853 | { | |||
854 | table::BorderLine2 aTableBorder = oTableBorder->second.get<table::BorderLine2>(); | |||
855 | // Both style and direct formatting says that the cell has no border. | |||
856 | bool bNoCellBorder = aStyleCellBorder.LineStyle == table::BorderLineStyle::NONE && aDirectCellBorder.LineStyle == table::BorderLineStyle::NONE; | |||
857 | if (aTableBorder.LineStyle != table::BorderLineStyle::NONE && bNoCellBorder) | |||
858 | { | |||
859 | // But at a table-level, there is a border, then again delete both cell properties. | |||
860 | pStyleProps->Erase(rBorder); | |||
861 | (*aCellIterator)->Erase(rBorder); | |||
862 | } | |||
863 | } | |||
864 | } | |||
865 | } | |||
866 | } | |||
867 | ||||
868 | pAllCellProps->InsertProps( pStyleProps ); | |||
869 | } | |||
870 | ||||
871 | // Remove properties from style/row that aren't allowed in cells | |||
872 | pAllCellProps->Erase( PROP_HEADER_ROW_COUNT ); | |||
873 | pAllCellProps->Erase( PROP_TBL_HEADER ); | |||
874 | ||||
875 | // Then add the cell properties | |||
876 | pAllCellProps->InsertProps(*aCellIterator); | |||
877 | std::swap(*(*aCellIterator), *pAllCellProps ); | |||
878 | ||||
879 | #ifdef DBG_UTIL | |||
880 | TagLogger::getInstance().startElement("cell"); | |||
881 | TagLogger::getInstance().attribute("cell", nCell); | |||
882 | TagLogger::getInstance().attribute("row", nRow); | |||
883 | #endif | |||
884 | ||||
885 | // Do not apply horizontal and vertical borders to a one cell table. | |||
886 | if (m_aCellProperties.size() <= 1 && aRowOfCellsIterator->size() <= 1) | |||
887 | { | |||
888 | rInfo.pTableBorders->Erase(META_PROP_HORIZONTAL_BORDER); | |||
889 | rInfo.pTableBorders->Erase(META_PROP_VERTICAL_BORDER); | |||
890 | } | |||
891 | // Do not apply vertical borders to a one column table. | |||
892 | else if (m_aCellProperties.size() > 1 && aRowOfCellsIterator->size() <= 1) | |||
893 | { | |||
894 | bool isOneCol = true; | |||
895 | for (size_t i = nRow; i < m_aCellProperties.size(); i++) | |||
896 | { | |||
897 | if (m_aCellProperties[i].size() > 1) | |||
898 | { | |||
899 | isOneCol = false; | |||
900 | break; | |||
901 | } | |||
902 | } | |||
903 | if (isOneCol) | |||
904 | rInfo.pTableBorders->Erase(META_PROP_VERTICAL_BORDER); | |||
905 | } | |||
906 | // Do not apply horizontal borders to a one row table. | |||
907 | else if (m_aCellProperties.size() == 1 && aRowOfCellsIterator->size() > 1) | |||
908 | { | |||
909 | rInfo.pTableBorders->Erase(META_PROP_HORIZONTAL_BORDER); | |||
910 | } | |||
911 | ||||
912 | // tdf#129452 Checking if current cell is vertically merged with all the other cells below to the bottom. | |||
913 | // This must be done in order to apply the bottom border of the table to the first cell in a vertical merge. | |||
914 | std::optional<PropertyMap::Property> oProp = m_aCellProperties[nRow][nCell]->getProperty(PROP_VERTICAL_MERGE); | |||
915 | bool bMergedVertically = oProp && oProp->second.get<bool>(); // starting cell | |||
916 | if ( bMergedVertically ) | |||
917 | { | |||
918 | const sal_uInt32 nColumn = m_rDMapper_Impl.getTableManager().findColumn(nRow, nCell); | |||
919 | sal_Int32 nLastMergedRow = 0; | |||
920 | for (size_t i = nRow + 1; bMergedVertically && i < m_aCellProperties.size(); i++) | |||
921 | { | |||
922 | const sal_uInt32 nColumnCell = m_rDMapper_Impl.getTableManager().findColumnCell(i, nColumn); | |||
923 | if ( m_aCellProperties[i].size() > sal::static_int_cast<std::size_t>(nColumnCell) ) | |||
924 | { | |||
925 | oProp = m_aCellProperties[i][nColumnCell]->getProperty(PROP_VERTICAL_MERGE); | |||
926 | bMergedVertically = oProp && !oProp->second.get<bool>(); //continuing cell | |||
927 | if ( bMergedVertically ) | |||
928 | nLastMergedRow = i; | |||
929 | } | |||
930 | else | |||
931 | bMergedVertically = false; | |||
932 | } | |||
933 | ||||
934 | // Only consider the bottom border setting from the last merged cell. | |||
935 | // Note: in MSO, left/right apply per-unmerged-row. Can't do that in LO, so just using the top cell's borders should be fine. | |||
936 | if ( nRow < nLastMergedRow ) | |||
937 | { | |||
938 | (*aCellIterator)->Erase(PROP_BOTTOM_BORDER); | |||
939 | const sal_uInt32 nColumnCell = m_rDMapper_Impl.getTableManager().findColumnCell(nLastMergedRow, nColumn); | |||
940 | lcl_mergeBorder( PROP_BOTTOM_BORDER, m_aCellProperties[nLastMergedRow][nColumnCell], *aCellIterator ); | |||
941 | } | |||
942 | } | |||
943 | ||||
944 | const sal_uInt32 nFirstCell = m_rDMapper_Impl.getTableManager().getGridBefore(nRow); | |||
945 | const sal_uInt32 nLastCell = m_aCellProperties[nRow].size() - m_rDMapper_Impl.getTableManager().getGridAfter(nRow) - 1; | |||
946 | lcl_computeCellBorders( rInfo.pTableBorders, *aCellIterator, nCell, nFirstCell, nLastCell, nRow, bIsEndRow, bMergedVertically ); | |||
947 | ||||
948 | //now set the default left+right border distance TODO: there's an sprm containing the default distance! | |||
949 | aCellIterator->get()->Insert( PROP_LEFT_BORDER_DISTANCE, | |||
950 | uno::makeAny(rInfo.nLeftBorderDistance ), false); | |||
951 | aCellIterator->get()->Insert( PROP_RIGHT_BORDER_DISTANCE, | |||
952 | uno::makeAny(rInfo.nRightBorderDistance ), false); | |||
953 | aCellIterator->get()->Insert( PROP_TOP_BORDER_DISTANCE, | |||
954 | uno::makeAny(rInfo.nTopBorderDistance ), false); | |||
955 | aCellIterator->get()->Insert( PROP_BOTTOM_BORDER_DISTANCE, | |||
956 | uno::makeAny(rInfo.nBottomBorderDistance ), false); | |||
957 | ||||
958 | // Horizontal merge is not a UNO property, extract that info here to rMerges, and then remove it from the map. | |||
959 | const std::optional<PropertyMap::Property> aHorizontalMergeVal = (*aCellIterator)->getProperty(PROP_HORIZONTAL_MERGE); | |||
960 | if (aHorizontalMergeVal) | |||
961 | { | |||
962 | if (aHorizontalMergeVal->second.get<bool>()) | |||
963 | { | |||
964 | // first cell in a merge | |||
965 | HorizontallyMergedCell aMerge(nRow, nCell); | |||
966 | rMerges.push_back(aMerge); | |||
967 | } | |||
968 | else if (!rMerges.empty()) | |||
969 | { | |||
970 | // resuming an earlier merge | |||
971 | HorizontallyMergedCell& rMerge = rMerges.back(); | |||
972 | rMerge.m_nLastRow = nRow; | |||
973 | rMerge.m_nLastCol = nCell; | |||
974 | } | |||
975 | (*aCellIterator)->Erase(PROP_HORIZONTAL_MERGE); | |||
976 | } | |||
977 | pSingleCellProperties[nCell] = (*aCellIterator)->GetPropertyValues(); | |||
978 | #ifdef DBG_UTIL | |||
979 | TagLogger::getInstance().endElement(); | |||
980 | #endif | |||
981 | } | |||
982 | ++nCell; | |||
983 | ++aCellIterator; | |||
984 | } | |||
985 | ++nRow; | |||
986 | ++aRowOfCellsIterator; | |||
987 | ++aRowIter; | |||
988 | } | |||
989 | ||||
990 | #ifdef DBG_UTIL | |||
991 | TagLogger::getInstance().endElement(); | |||
992 | #endif | |||
993 | ||||
994 | return aCellProperties; | |||
995 | } | |||
996 | ||||
997 | /// Do all cells in this row have a CellHideMark property? | |||
998 | static bool lcl_hideMarks(PropertyMapVector1& rCellProperties) | |||
999 | { | |||
1000 | for (const PropertyMapPtr & p : rCellProperties) | |||
1001 | { | |||
1002 | // if anything is vertically merged, the row must not be set to fixed | |||
1003 | // as Writer's layout doesn't handle that well | |||
1004 | if (!p->isSet(PROP_CELL_HIDE_MARK) || p->isSet(PROP_VERTICAL_MERGE)) | |||
1005 | return false; | |||
1006 | } | |||
1007 | return true; | |||
1008 | } | |||
1009 | ||||
1010 | /// Are all cells in this row empty? | |||
1011 | static bool lcl_emptyRow(std::vector<RowSequence_t>& rTableRanges, sal_Int32 nRow) | |||
1012 | { | |||
1013 | if (nRow >= static_cast<sal_Int32>(rTableRanges.size())) | |||
1014 | { | |||
1015 | SAL_WARN("writerfilter.dmapper", "m_aCellProperties not in sync with rTableRanges?")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN , "writerfilter.dmapper")) { case SAL_DETAIL_LOG_ACTION_IGNORE : break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail ::getResult( ::sal::detail::StreamStart() << "m_aCellProperties not in sync with rTableRanges?" ) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper" ), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1015" ": "), ::sal::detail::unwrapStream( ::sal::detail ::StreamStart() << "m_aCellProperties not in sync with rTableRanges?" ), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "m_aCellProperties not in sync with rTableRanges?"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper" ), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1015" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "m_aCellProperties not in sync with rTableRanges?" ) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper" ), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1015" ": "), ::sal::detail::unwrapStream( ::sal::detail ::StreamStart() << "m_aCellProperties not in sync with rTableRanges?" ), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "m_aCellProperties not in sync with rTableRanges?"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper" ), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1015" ": "), sal_detail_stream, 0); }; std::abort(); break ; } } } while (false); | |||
1016 | return false; | |||
1017 | } | |||
1018 | ||||
1019 | RowSequence_t rRowSeq = rTableRanges[nRow]; | |||
1020 | if (!rRowSeq.hasElements()) | |||
1021 | { | |||
1022 | SAL_WARN("writerfilter.dmapper", "m_aCellProperties not in sync with rTableRanges?")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN , "writerfilter.dmapper")) { case SAL_DETAIL_LOG_ACTION_IGNORE : break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail ::getResult( ::sal::detail::StreamStart() << "m_aCellProperties not in sync with rTableRanges?" ) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper" ), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1022" ": "), ::sal::detail::unwrapStream( ::sal::detail ::StreamStart() << "m_aCellProperties not in sync with rTableRanges?" ), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "m_aCellProperties not in sync with rTableRanges?"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper" ), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1022" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "m_aCellProperties not in sync with rTableRanges?" ) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper" ), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1022" ": "), ::sal::detail::unwrapStream( ::sal::detail ::StreamStart() << "m_aCellProperties not in sync with rTableRanges?" ), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "m_aCellProperties not in sync with rTableRanges?"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper" ), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1022" ": "), sal_detail_stream, 0); }; std::abort(); break ; } } } while (false); | |||
1023 | return false; | |||
1024 | } | |||
1025 | ||||
1026 | if (!rRowSeq[0][0].is()) | |||
1027 | { | |||
1028 | // This can happen when we can't import the table, e.g. we're inside a | |||
1029 | // comment. | |||
1030 | SAL_WARN("writerfilter.dmapper", "rRowSeq[0][0] is an empty reference")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN , "writerfilter.dmapper")) { case SAL_DETAIL_LOG_ACTION_IGNORE : break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail ::getResult( ::sal::detail::StreamStart() << "rRowSeq[0][0] is an empty reference" ) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper" ), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1030" ": "), ::sal::detail::unwrapStream( ::sal::detail ::StreamStart() << "rRowSeq[0][0] is an empty reference" ), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "rRowSeq[0][0] is an empty reference"; ::sal::detail ::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper" ), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1030" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "rRowSeq[0][0] is an empty reference") == 1) { :: sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper" ), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1030" ": "), ::sal::detail::unwrapStream( ::sal::detail ::StreamStart() << "rRowSeq[0][0] is an empty reference" ), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "rRowSeq[0][0] is an empty reference"; ::sal::detail ::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper" ), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1030" ": "), sal_detail_stream, 0); }; std::abort(); break ; } } } while (false); | |||
1031 | return false; | |||
1032 | } | |||
1033 | ||||
1034 | uno::Reference<text::XTextRangeCompare> xTextRangeCompare(rRowSeq[0][0]->getText(), uno::UNO_QUERY); | |||
1035 | try | |||
1036 | { | |||
1037 | // See SwXText::Impl::ConvertCell(), we need to compare the start of | |||
1038 | // the start and the end of the end. However for our text ranges, only | |||
1039 | // the starts are set, so compareRegionStarts() does what we need. | |||
1040 | bool bRangesAreNotEqual = std::any_of(rRowSeq.begin(), rRowSeq.end(), | |||
1041 | [&xTextRangeCompare](const CellSequence_t& rCellSeq) { | |||
1042 | return xTextRangeCompare->compareRegionStarts(rCellSeq[0], rCellSeq[1]) != 0; }); | |||
1043 | if (bRangesAreNotEqual) | |||
1044 | return false; | |||
1045 | } | |||
1046 | catch (const lang::IllegalArgumentException&) | |||
1047 | { | |||
1048 | TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "compareRegionStarts() failed")do { css::uno::Any tools_warn_exception( DbgGetCaughtException () ); do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN , "writerfilter.dmapper")) { case SAL_DETAIL_LOG_ACTION_IGNORE : break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail ::getResult( ::sal::detail::StreamStart() << "compareRegionStarts() failed" << " " << exceptionToString(tools_warn_exception )) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ( "writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1048" ": "), ::sal::detail::unwrapStream( ::sal::detail ::StreamStart() << "compareRegionStarts() failed" << " " << exceptionToString(tools_warn_exception)), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "compareRegionStarts() failed" << " " << exceptionToString(tools_warn_exception); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1048" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "compareRegionStarts() failed" << " " << exceptionToString(tools_warn_exception)) == 1) { ::sal_detail_log ( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1048" ": "), ::sal::detail::unwrapStream( ::sal::detail ::StreamStart() << "compareRegionStarts() failed" << " " << exceptionToString(tools_warn_exception)), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "compareRegionStarts() failed" << " " << exceptionToString(tools_warn_exception); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1048" ": "), sal_detail_stream, 0); }; std::abort(); break ; } } } while (false); } while (false); | |||
1049 | return false; | |||
1050 | } | |||
1051 | return true; | |||
1052 | } | |||
1053 | ||||
1054 | css::uno::Sequence<css::beans::PropertyValues> DomainMapperTableHandler::endTableGetRowProperties() | |||
1055 | { | |||
1056 | #ifdef DBG_UTIL | |||
1057 | TagLogger::getInstance().startElement("getRowProperties"); | |||
1058 | #endif | |||
1059 | ||||
1060 | css::uno::Sequence<css::beans::PropertyValues> aRowProperties( m_aRowProperties.size() ); | |||
1061 | sal_Int32 nRow = 0; | |||
1062 | for( const auto& rRow : m_aRowProperties ) | |||
1063 | { | |||
1064 | #ifdef DBG_UTIL | |||
1065 | TagLogger::getInstance().startElement("rowProps.row"); | |||
1066 | #endif | |||
1067 | if (rRow) | |||
1068 | { | |||
1069 | //set default to 'break across pages" | |||
1070 | rRow->Insert( PROP_IS_SPLIT_ALLOWED, uno::makeAny(true ), false ); | |||
1071 | // tblHeader is only our property, remove before the property map hits UNO | |||
1072 | rRow->Erase(PROP_TBL_HEADER); | |||
1073 | ||||
1074 | if (lcl_hideMarks(m_aCellProperties[nRow]) && lcl_emptyRow(m_aTableRanges, nRow)) | |||
1075 | { | |||
1076 | // We have CellHideMark on all cells, and also all cells are empty: | |||
1077 | // Force the row height to be exactly as specified, and not just as the minimum suggestion. | |||
1078 | rRow->Insert(PROP_SIZE_TYPE, uno::makeAny(text::SizeType::FIX)); | |||
1079 | } | |||
1080 | ||||
1081 | aRowProperties[nRow] = rRow->GetPropertyValues(); | |||
1082 | #ifdef DBG_UTIL | |||
1083 | rRow->dumpXml(); | |||
1084 | lcl_DumpPropertyValues(aRowProperties[nRow]); | |||
1085 | #endif | |||
1086 | } | |||
1087 | ++nRow; | |||
1088 | #ifdef DBG_UTIL | |||
1089 | TagLogger::getInstance().endElement(); | |||
1090 | #endif | |||
1091 | } | |||
1092 | ||||
1093 | #ifdef DBG_UTIL | |||
1094 | TagLogger::getInstance().endElement(); | |||
1095 | #endif | |||
1096 | ||||
1097 | return aRowProperties; | |||
1098 | } | |||
1099 | ||||
1100 | // table style has got bigger precedence than docDefault style, | |||
1101 | // but lower precedence than the paragraph styles and direct paragraph formatting | |||
1102 | void DomainMapperTableHandler::ApplyParagraphPropertiesFromTableStyle(TableParagraph rParaProp, std::vector< PropertyIds > aAllTableParaProperties, css::beans::PropertyValues rCellProperties) | |||
1103 | { | |||
1104 | for( auto const& eId : aAllTableParaProperties ) | |||
1105 | { | |||
1106 | // apply paragraph and character properties of the table style on table paragraphs | |||
1107 | // if there is no direct paragraph formatting | |||
1108 | bool bIsParaLevel = rParaProp.m_pPropertyMap->isSet(eId); | |||
1109 | if ( !bIsParaLevel || isCharacterProperty(eId) ) | |||
1110 | { | |||
1111 | if ( (eId == PROP_PARA_LEFT_MARGIN || eId == PROP_PARA_FIRST_LINE_INDENT) && | |||
1112 | rParaProp.m_pPropertyMap->isSet(PROP_NUMBERING_RULES) ) | |||
1113 | { | |||
1114 | // indentation of direct numbering has bigger precedence, than table style | |||
1115 | continue; | |||
1116 | } | |||
1117 | ||||
1118 | OUString sPropertyName = getPropertyName(eId); | |||
1119 | ||||
1120 | if ( bIsParaLevel && ( rParaProp.m_aParaOverrideApplied.find(sPropertyName) != rParaProp.m_aParaOverrideApplied.end() || | |||
1121 | sPropertyName.startsWith("CharFontName") ) ) | |||
1122 | { | |||
1123 | // don't apply table style, if this character property was applied on paragraph level | |||
1124 | // (or in the case of paragraph level font name settings to avoid regressions) | |||
1125 | continue; | |||
1126 | } | |||
1127 | ||||
1128 | auto pCellProp = std::find_if(rCellProperties.begin(), rCellProperties.end(), | |||
1129 | [&](const beans::PropertyValue& rProp) { return rProp.Name == sPropertyName; }); | |||
1130 | // this cell applies the table style property | |||
1131 | if (pCellProp != rCellProperties.end()) | |||
1132 | { | |||
1133 | bool bDocDefault; | |||
1134 | // handle paragraph background color defined in CellColorHandler | |||
1135 | if (eId == PROP_FILL_COLOR) | |||
1136 | { | |||
1137 | // table style defines paragraph background color, use the correct property name | |||
1138 | auto pFillStyleProp = std::find_if(rCellProperties.begin(), rCellProperties.end(), | |||
1139 | [&](const beans::PropertyValue& rProp) { return rProp.Name == "FillStyle"; }); | |||
1140 | if ( pFillStyleProp != rCellProperties.end() && | |||
1141 | pFillStyleProp->Value == uno::makeAny(drawing::FillStyle_SOLID) ) | |||
1142 | { | |||
1143 | sPropertyName = "ParaBackColor"; | |||
1144 | } | |||
1145 | else | |||
1146 | { | |||
1147 | // FillStyle_NONE, skip table style usage for paragraph background color | |||
1148 | continue; | |||
1149 | } | |||
1150 | } | |||
1151 | OUString sParaStyleName; | |||
1152 | rParaProp.m_rPropertySet->getPropertyValue("ParaStyleName") >>= sParaStyleName; | |||
1153 | StyleSheetEntryPtr pEntry = m_rDMapper_Impl.GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(sParaStyleName); | |||
1154 | uno::Any aParaStyle = m_rDMapper_Impl.GetPropertyFromStyleSheet(eId, pEntry, true, true, &bDocDefault); | |||
1155 | // A very strange compatibility rule says that the DEFAULT style's specified fontsize of 11 or 12 | |||
1156 | // or a specified left justify will always be overridden by the table-style. | |||
1157 | // Normally this rule is applied, so always do this unless a compatSetting indicates otherwise. | |||
1158 | bool bCompatOverride = false; | |||
1159 | if ( (eId == PROP_CHAR_HEIGHT || eId == PROP_PARA_ADJUST) && sParaStyleName == m_rDMapper_Impl.GetDefaultParaStyleName() ) | |||
1160 | { | |||
1161 | if ( eId == PROP_CHAR_HEIGHT ) | |||
1162 | bCompatOverride = aParaStyle == uno::Any(double(11)) || aParaStyle == uno::Any(double(12)); | |||
1163 | else if ( eId == PROP_PARA_ADJUST ) | |||
1164 | { | |||
1165 | style::ParagraphAdjust eAdjust(style::ParagraphAdjust_CENTER); | |||
1166 | aParaStyle >>= eAdjust; | |||
1167 | bCompatOverride = eAdjust == style::ParagraphAdjust_LEFT; | |||
1168 | } | |||
1169 | ||||
1170 | // The wording is confusing here. Normally, the paragraph style DOES override the table-style. | |||
1171 | // But for these two special situations, do not override the table-style. So the default is false. | |||
1172 | // If false, then "CompatOverride" the normal behaviour, and apply the table-style's value. | |||
1173 | bCompatOverride &= !m_rDMapper_Impl.GetSettingsTable()->GetCompatSettingValue("overrideTableStyleFontSizeAndJustification"); | |||
1174 | } | |||
1175 | ||||
1176 | // use table style when no paragraph style setting or a docDefault value is applied instead of it | |||
1177 | if ( aParaStyle == uno::Any() || bDocDefault || bCompatOverride ) try | |||
1178 | { | |||
1179 | // check property state of paragraph | |||
1180 | uno::Reference<text::XParagraphCursor> xParagraph( | |||
1181 | rParaProp.m_rEndParagraph->getText()->createTextCursorByRange(rParaProp.m_rEndParagraph), uno::UNO_QUERY_THROW ); | |||
1182 | // select paragraph | |||
1183 | xParagraph->gotoStartOfParagraph( true ); | |||
1184 | uno::Reference< beans::XPropertyState > xParaProperties( xParagraph, uno::UNO_QUERY_THROW ); | |||
1185 | if ( xParaProperties->getPropertyState(sPropertyName) == css::beans::PropertyState_DEFAULT_VALUE ) | |||
1186 | { | |||
1187 | if ( eId != PROP_FILL_COLOR ) | |||
1188 | { | |||
1189 | // apply style setting when the paragraph doesn't modify it | |||
1190 | rParaProp.m_rPropertySet->setPropertyValue( sPropertyName, pCellProp->Value ); | |||
1191 | } | |||
1192 | else | |||
1193 | { | |||
1194 | // we need this for complete import of table-style based paragraph background color | |||
1195 | rParaProp.m_rPropertySet->setPropertyValue( "FillColor", pCellProp->Value ); | |||
1196 | rParaProp.m_rPropertySet->setPropertyValue( "FillStyle", uno::makeAny(drawing::FillStyle_SOLID) ); | |||
1197 | } | |||
1198 | } | |||
1199 | else | |||
1200 | { | |||
1201 | // apply style setting only on text portions without direct modification of it | |||
1202 | uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xParagraph, uno::UNO_QUERY); | |||
1203 | uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration(); | |||
1204 | uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParaEnum->nextElement(), uno::UNO_QUERY); | |||
1205 | uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration(); | |||
1206 | while ( xRunEnum->hasMoreElements() ) | |||
1207 | { | |||
1208 | uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY); | |||
1209 | uno::Reference< beans::XPropertyState > xRunProperties( xRun, uno::UNO_QUERY_THROW ); | |||
1210 | if ( xRunProperties->getPropertyState(sPropertyName) == css::beans::PropertyState_DEFAULT_VALUE ) | |||
1211 | { | |||
1212 | uno::Reference< beans::XPropertySet > xRunPropertySet( xRun, uno::UNO_QUERY_THROW ); | |||
1213 | xRunPropertySet->setPropertyValue( sPropertyName, pCellProp->Value ); | |||
1214 | } | |||
1215 | } | |||
1216 | } | |||
1217 | } | |||
1218 | catch ( const uno::Exception & ) | |||
1219 | { | |||
1220 | TOOLS_INFO_EXCEPTION("writerfilter.dmapper", "Exception during table style correction")do { css::uno::Any tools_warn_exception( DbgGetCaughtException () ); do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO , "writerfilter.dmapper")) { case SAL_DETAIL_LOG_ACTION_IGNORE : break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail ::getResult( ::sal::detail::StreamStart() << "Exception during table style correction" << " " << exceptionToString(tools_warn_exception )) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ( "writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1220" ": "), ::sal::detail::unwrapStream( ::sal::detail ::StreamStart() << "Exception during table style correction" << " " << exceptionToString(tools_warn_exception )), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "Exception during table style correction" << " " << exceptionToString(tools_warn_exception); ::sal::detail ::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("writerfilter.dmapper" ), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1220" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "Exception during table style correction" << " " << exceptionToString(tools_warn_exception)) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("writerfilter.dmapper" ), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1220" ": "), ::sal::detail::unwrapStream( ::sal::detail ::StreamStart() << "Exception during table style correction" << " " << exceptionToString(tools_warn_exception )), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "Exception during table style correction" << " " << exceptionToString(tools_warn_exception); ::sal::detail ::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("writerfilter.dmapper" ), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1220" ": "), sal_detail_stream, 0); }; std::abort(); break ; } } } while (false); } while (false); | |||
1221 | } | |||
1222 | } | |||
1223 | } | |||
1224 | } | |||
1225 | } | |||
1226 | ||||
1227 | // convert formula range identifier ABOVE, BELOW, LEFT and RIGHT | |||
1228 | static void lcl_convertFormulaRanges(const uno::Reference<text::XTextTable> & xTable) | |||
1229 | { | |||
1230 | uno::Reference<table::XCellRange> xCellRange(xTable, uno::UNO_QUERY_THROW); | |||
1231 | uno::Reference<container::XIndexAccess> xTableRows(xTable->getRows(), uno::UNO_QUERY_THROW); | |||
1232 | sal_Int32 nRows = xTableRows->getCount(); | |||
1233 | for (sal_Int32 nRow = 0; nRow < nRows; ++nRow) | |||
1234 | { | |||
1235 | for (sal_Int16 nCol = 0; nCol < MAXTABLECELLS63; ++nCol) | |||
1236 | { | |||
1237 | try | |||
1238 | { | |||
1239 | uno::Reference<beans::XPropertySet> xCellProperties(xCellRange->getCellByPosition(nCol, nRow), uno::UNO_QUERY_THROW); | |||
1240 | uno::Sequence<beans::PropertyValue> aCellGrabBag; | |||
1241 | xCellProperties->getPropertyValue("CellInteropGrabBag") >>= aCellGrabBag; | |||
1242 | OUString sFormula; | |||
1243 | bool bReplace = false; | |||
1244 | for (const auto& rProp : std::as_const(aCellGrabBag)) | |||
1245 | { | |||
1246 | if ( rProp.Name == "CellFormulaConverted" ) | |||
1247 | { | |||
1248 | rProp.Value >>= sFormula; | |||
1249 | struct RangeDirection | |||
1250 | { | |||
1251 | OUString m_sName; | |||
1252 | sal_Int16 m_nCol; | |||
1253 | sal_Int16 m_nRow; | |||
1254 | }; | |||
1255 | static const RangeDirection pDirections[] = | |||
1256 | { | |||
1257 | { OUString(" LEFT "), -1, 0}, | |||
1258 | { OUString(" RIGHT "), 1, 0}, | |||
1259 | { OUString(" ABOVE "), 0, -1}, | |||
1260 | { OUString(" BELOW "), 0, 1 } | |||
1261 | }; | |||
1262 | for (const RangeDirection& rRange : pDirections) | |||
1263 | { | |||
1264 | if ( sFormula.indexOf(rRange.m_sName) > -1 ) | |||
1265 | { | |||
1266 | // range starts at the first cell above/below/left/right, but ends at the | |||
1267 | // table border or at the first non-value cell after a value cell | |||
1268 | bool bFoundFirst = false; | |||
1269 | OUString sNextCell; | |||
1270 | OUString sLastCell; | |||
1271 | OUString sLastValueCell; | |||
1272 | // walk through the cells of the range | |||
1273 | try | |||
1274 | { | |||
1275 | sal_Int32 nCell = 0; | |||
1276 | while (++nCell) | |||
1277 | { | |||
1278 | uno::Reference<beans::XPropertySet> xCell( | |||
1279 | xCellRange->getCellByPosition(nCol + nCell * rRange.m_nCol, nRow + nCell * rRange.m_nRow), | |||
1280 | uno::UNO_QUERY_THROW); | |||
1281 | // empty cell or cell with text content is end of the range | |||
1282 | uno::Reference<text::XText> xText(xCell, uno::UNO_QUERY_THROW); | |||
1283 | sLastCell = xCell->getPropertyValue("CellName").get<OUString>(); | |||
1284 | if (sNextCell.isEmpty()) | |||
1285 | sNextCell = sLastCell; | |||
1286 | try | |||
1287 | { | |||
1288 | // accept numbers with comma and percent | |||
1289 | OUString sCellText = xText->getString().replace(',', '.'); | |||
1290 | if (sCellText.endsWith("%")) | |||
1291 | sCellText = sCellText.copy(0, sCellText.getLength()-1); | |||
1292 | boost::lexical_cast<double>(sCellText); | |||
1293 | } | |||
1294 | catch( boost::bad_lexical_cast const& ) | |||
1295 | { | |||
1296 | if ( !bFoundFirst ) | |||
1297 | { | |||
1298 | // still search value cells | |||
1299 | continue; | |||
1300 | } | |||
1301 | else | |||
1302 | { | |||
1303 | // end of range | |||
1304 | break; | |||
1305 | } | |||
1306 | } | |||
1307 | sLastValueCell = sLastCell; | |||
1308 | bFoundFirst = true; | |||
1309 | } | |||
1310 | } | |||
1311 | catch ( const lang::IndexOutOfBoundsException & ) | |||
1312 | { | |||
1313 | } | |||
1314 | ||||
1315 | if ( !sNextCell.isEmpty() ) | |||
1316 | { | |||
1317 | OUString sRange = "<" + sNextCell + ":" + | |||
1318 | ( sLastValueCell.isEmpty() ? sLastCell : sLastValueCell ) + ">"; | |||
1319 | sFormula = sFormula.replaceAll(rRange.m_sName, sRange); | |||
1320 | bReplace = true; | |||
1321 | } | |||
1322 | } | |||
1323 | } | |||
1324 | ||||
1325 | // update formula field | |||
1326 | if (bReplace) | |||
1327 | { | |||
1328 | uno::Reference<text::XText> xCell(xCellRange->getCellByPosition(nCol, nRow), uno::UNO_QUERY); | |||
1329 | uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xCell, uno::UNO_QUERY); | |||
1330 | uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration(); | |||
1331 | uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParaEnum->nextElement(), uno::UNO_QUERY); | |||
1332 | uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration(); | |||
1333 | while ( xRunEnum->hasMoreElements() ) | |||
1334 | { | |||
1335 | uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY); | |||
1336 | uno::Reference< beans::XPropertySet > xRunProperties( xRun, uno::UNO_QUERY_THROW ); | |||
1337 | if ( xRunProperties->getPropertyValue("TextPortionType") == uno::makeAny(OUString("TextField")) ) | |||
1338 | { | |||
1339 | uno::Reference<text::XTextField> const xField(xRunProperties->getPropertyValue("TextField").get<uno::Reference<text::XTextField>>()); | |||
1340 | uno::Reference< beans::XPropertySet > xFieldProperties( xField, uno::UNO_QUERY_THROW ); | |||
1341 | // cell can contain multiple text fields, but only one is handled now (~formula cell) | |||
1342 | if ( rProp.Value != xFieldProperties->getPropertyValue("Content") ) | |||
1343 | continue; | |||
1344 | xFieldProperties->setPropertyValue("Content", uno::makeAny(sFormula)); | |||
1345 | // update grab bag | |||
1346 | auto aGrabBag = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(aCellGrabBag); | |||
1347 | beans::PropertyValue aValue; | |||
1348 | aValue.Name = "CellFormulaConverted"; | |||
1349 | aValue.Value <<= sFormula; | |||
1350 | aGrabBag.push_back(aValue); | |||
1351 | xCellProperties->setPropertyValue("CellInteropGrabBag", uno::makeAny(comphelper::containerToSequence(aGrabBag))); | |||
1352 | } | |||
1353 | } | |||
1354 | } | |||
1355 | } | |||
1356 | } | |||
1357 | } | |||
1358 | catch ( const lang::IndexOutOfBoundsException & ) | |||
1359 | { | |||
1360 | // jump to next table row | |||
1361 | break; | |||
1362 | } | |||
1363 | } | |||
1364 | } | |||
1365 | } | |||
1366 | ||||
1367 | void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTableStartsAtCellStart) | |||
1368 | { | |||
1369 | #ifdef DBG_UTIL | |||
1370 | TagLogger::getInstance().startElement("tablehandler.endTable"); | |||
1371 | #endif | |||
1372 | ||||
1373 | // If we want to make this table a floating one. | |||
1374 | std::vector<beans::PropertyValue> aFrameProperties = comphelper::sequenceToContainer<std::vector<beans::PropertyValue> > | |||
1375 | (m_rDMapper_Impl.getTableManager().getCurrentTablePosition()); | |||
1376 | TableInfo aTableInfo; | |||
1377 | aTableInfo.nNestLevel = nestedTableLevel; | |||
1378 | aTableInfo.pTableStyle = endTableGetTableStyle(aTableInfo, aFrameProperties); | |||
| ||||
1379 | // expands to uno::Sequence< Sequence< beans::PropertyValues > > | |||
1380 | ||||
1381 | std::vector<HorizontallyMergedCell> aMerges; | |||
1382 | CellPropertyValuesSeq_t aCellProperties = endTableGetCellProperties(aTableInfo, aMerges); | |||
1383 | ||||
1384 | css::uno::Sequence<css::beans::PropertyValues> aRowProperties = endTableGetRowProperties(); | |||
1385 | ||||
1386 | #ifdef DBG_UTIL | |||
1387 | lcl_DumpPropertyValueSeq(aRowProperties); | |||
1388 | #endif | |||
1389 | ||||
1390 | if (!m_aTableRanges.empty()) | |||
1391 | { | |||
1392 | uno::Reference<text::XTextRange> xStart; | |||
1393 | uno::Reference<text::XTextRange> xEnd; | |||
1394 | ||||
1395 | bool bFloating = !aFrameProperties.empty(); | |||
1396 | ||||
1397 | // OOXML table style may contain paragraph properties, apply these on cell paragraphs | |||
1398 | if ( m_aTableRanges[0].hasElements() && m_aTableRanges[0][0].hasElements() ) | |||
1399 | { | |||
1400 | // collect all paragraph properties used in table styles | |||
1401 | PropertyMapPtr pAllTableProps( new PropertyMap ); | |||
1402 | pAllTableProps->InsertProps(aTableInfo.pTableDefaults); | |||
1403 | if ( aTableInfo.pTableStyle ) | |||
1404 | pAllTableProps->InsertProps(aTableInfo.pTableStyle->GetProperties( CNF_ALL0xFFF )); | |||
1405 | for (const auto& eId : pAllTableProps->GetPropertyIds()) | |||
1406 | { | |||
1407 | if ( !isParagraphProperty(eId) && !isCharacterProperty(eId) ) | |||
1408 | pAllTableProps->Erase(eId); | |||
1409 | } | |||
1410 | std::vector< PropertyIds > aAllTableParaProperties = pAllTableProps->GetPropertyIds(); | |||
1411 | ||||
1412 | if ( !aAllTableParaProperties.empty() ) | |||
1413 | { | |||
1414 | TableParagraphVectorPtr pTableParagraphs = m_rDMapper_Impl.getTableManager().getCurrentParagraphs(); | |||
1415 | for (size_t nRow = 0; nRow < m_aTableRanges.size(); ++nRow) | |||
1416 | { | |||
1417 | // Note that this is "cell" since you must not treat it as "column". | |||
1418 | for (size_t nCell = 0; nCell < m_aTableRanges[nRow].size(); ++nCell) | |||
1419 | { | |||
1420 | auto rStartPara = m_aTableRanges[nRow][nCell][0]; | |||
1421 | if (!rStartPara.is()) | |||
1422 | continue; | |||
1423 | auto rEndPara = m_aTableRanges[nRow][nCell][1]; | |||
1424 | uno::Reference<text::XTextRangeCompare> xTextRangeCompare(rStartPara->getText(), uno::UNO_QUERY); | |||
1425 | bool bApply = false; | |||
1426 | // search paragraphs of the cell | |||
1427 | std::vector<TableParagraph>::iterator aIt = pTableParagraphs->begin(); | |||
1428 | while ( aIt != pTableParagraphs->end() ) try | |||
1429 | { | |||
1430 | if (!bApply && xTextRangeCompare->compareRegionStarts(rStartPara, aIt->m_rStartParagraph) == 0) | |||
1431 | bApply = true; | |||
1432 | if (bApply) | |||
1433 | { | |||
1434 | bool bEndOfApply = (xTextRangeCompare->compareRegionEnds(rEndPara, aIt->m_rEndParagraph) == 0); | |||
1435 | ApplyParagraphPropertiesFromTableStyle(*aIt, aAllTableParaProperties, aCellProperties[nRow][nCell]); | |||
1436 | // erase processed paragraph from list of pending paragraphs | |||
1437 | aIt = pTableParagraphs->erase(aIt); | |||
1438 | if (bEndOfApply) | |||
1439 | break; | |||
1440 | } | |||
1441 | else | |||
1442 | ++aIt; | |||
1443 | } | |||
1444 | catch( const lang::IllegalArgumentException & ) | |||
1445 | { | |||
1446 | // skip compareRegion with nested tables | |||
1447 | ++aIt; | |||
1448 | } | |||
1449 | } | |||
1450 | } | |||
1451 | } | |||
1452 | } | |||
1453 | ||||
1454 | // Additional checks: if we can do this. | |||
1455 | if (bFloating && m_aTableRanges[0].hasElements() && m_aTableRanges[0][0].hasElements()) | |||
1456 | { | |||
1457 | xStart = m_aTableRanges[0][0][0]; | |||
1458 | uno::Sequence< uno::Sequence< uno::Reference<text::XTextRange> > >& rLastRow = m_aTableRanges[m_aTableRanges.size() - 1]; | |||
1459 | if (rLastRow.hasElements()) | |||
1460 | { | |||
1461 | uno::Sequence< uno::Reference<text::XTextRange> >& rLastCell = rLastRow[rLastRow.getLength() - 1]; | |||
1462 | xEnd = rLastCell[1]; | |||
1463 | } | |||
1464 | } | |||
1465 | uno::Reference<text::XTextTable> xTable; | |||
1466 | try | |||
1467 | { | |||
1468 | if (m_xText.is()) | |||
1469 | { | |||
1470 | xTable = m_xText->convertToTable(comphelper::containerToSequence(m_aTableRanges), aCellProperties, aRowProperties, aTableInfo.aTableProperties); | |||
1471 | ||||
1472 | if (xTable.is()) | |||
1473 | { | |||
1474 | if (!aMerges.empty()) | |||
1475 | { | |||
1476 | static const std::vector<std::u16string_view> aBorderNames | |||
1477 | = { u"TopBorder", u"LeftBorder", u"BottomBorder", u"RightBorder" }; | |||
1478 | ||||
1479 | // Perform horizontal merges in reverse order, so the fact that merging changes the position of cells won't cause a problem for us. | |||
1480 | for (std::vector<HorizontallyMergedCell>::reverse_iterator it = aMerges.rbegin(); it != aMerges.rend(); ++it) | |||
1481 | { | |||
1482 | uno::Reference<table::XCellRange> xCellRange(xTable, uno::UNO_QUERY_THROW); | |||
1483 | uno::Reference<beans::XPropertySet> xFirstCell( | |||
1484 | xCellRange->getCellByPosition(it->m_nFirstCol, it->m_nFirstRow), | |||
1485 | uno::UNO_QUERY_THROW); | |||
1486 | OUString aFirst | |||
1487 | = xFirstCell->getPropertyValue("CellName").get<OUString>(); | |||
1488 | // tdf#105852: Only try to merge if m_nLastCol is set (i.e. there were some merge continuation cells) | |||
1489 | if (it->m_nLastCol != -1) | |||
1490 | { | |||
1491 | // Save border properties of the first cell | |||
1492 | // before merge. | |||
1493 | table::BorderLine2 aBorderValues[4]; | |||
1494 | for (size_t i = 0; i < aBorderNames.size(); ++i) | |||
1495 | xFirstCell->getPropertyValue(aBorderNames[i]) | |||
1496 | >>= aBorderValues[i]; | |||
1497 | ||||
1498 | uno::Reference<beans::XPropertySet> xLastCell( | |||
1499 | xCellRange->getCellByPosition(it->m_nLastCol, it->m_nLastRow), | |||
1500 | uno::UNO_QUERY_THROW); | |||
1501 | OUString aLast | |||
1502 | = xLastCell->getPropertyValue("CellName").get<OUString>(); | |||
1503 | ||||
1504 | uno::Reference<text::XTextTableCursor> xCursor = xTable->createCursorByCellName(aFirst); | |||
1505 | xCursor->gotoCellByName(aLast, true); | |||
1506 | ||||
1507 | xCursor->mergeRange(); | |||
1508 | ||||
1509 | // Handle conflicting properties: mergeRange() | |||
1510 | // takes the last cell, Word takes the first | |||
1511 | // cell. | |||
1512 | for (size_t i = 0; i < aBorderNames.size(); ++i) | |||
1513 | { | |||
1514 | if (aBorderValues[i].LineStyle != table::BorderLineStyle::NONE) | |||
1515 | xFirstCell->setPropertyValue( | |||
1516 | aBorderNames[i], uno::makeAny(aBorderValues[i])); | |||
1517 | } | |||
1518 | } | |||
1519 | } | |||
1520 | } | |||
1521 | ||||
1522 | // convert special range IDs ABOVE, BELOW, LEFT and RIGHT | |||
1523 | lcl_convertFormulaRanges(xTable); | |||
1524 | } | |||
1525 | } | |||
1526 | } | |||
1527 | catch ( const lang::IllegalArgumentException & ) | |||
1528 | { | |||
1529 | TOOLS_INFO_EXCEPTION("writerfilter.dmapper", "Conversion to table error")do { css::uno::Any tools_warn_exception( DbgGetCaughtException () ); do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO , "writerfilter.dmapper")) { case SAL_DETAIL_LOG_ACTION_IGNORE : break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail ::getResult( ::sal::detail::StreamStart() << "Conversion to table error" << " " << exceptionToString(tools_warn_exception )) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ( "writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1529" ": "), ::sal::detail::unwrapStream( ::sal::detail ::StreamStart() << "Conversion to table error" << " " << exceptionToString(tools_warn_exception)), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "Conversion to table error" << " " << exceptionToString (tools_warn_exception); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO ), ("writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1529" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "Conversion to table error" << " " << exceptionToString(tools_warn_exception)) == 1) { ::sal_detail_log ( (::SAL_DETAIL_LOG_LEVEL_INFO), ("writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1529" ": "), ::sal::detail::unwrapStream( ::sal::detail ::StreamStart() << "Conversion to table error" << " " << exceptionToString(tools_warn_exception)), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "Conversion to table error" << " " << exceptionToString (tools_warn_exception); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO ), ("writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1529" ": "), sal_detail_stream, 0); }; std::abort(); break ; } } } while (false); } while (false); | |||
1530 | #ifdef DBG_UTIL | |||
1531 | TagLogger::getInstance().chars(std::string("failed to import table!")); | |||
1532 | #endif | |||
1533 | } | |||
1534 | catch ( const uno::Exception & ) | |||
1535 | { | |||
1536 | TOOLS_INFO_EXCEPTION("writerfilter.dmapper", "Exception during table creation")do { css::uno::Any tools_warn_exception( DbgGetCaughtException () ); do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO , "writerfilter.dmapper")) { case SAL_DETAIL_LOG_ACTION_IGNORE : break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail ::getResult( ::sal::detail::StreamStart() << "Exception during table creation" << " " << exceptionToString(tools_warn_exception )) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ( "writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1536" ": "), ::sal::detail::unwrapStream( ::sal::detail ::StreamStart() << "Exception during table creation" << " " << exceptionToString(tools_warn_exception)), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "Exception during table creation" << " " << exceptionToString(tools_warn_exception); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1536" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "Exception during table creation" << " " << exceptionToString(tools_warn_exception)) == 1) { ::sal_detail_log ( (::SAL_DETAIL_LOG_LEVEL_INFO), ("writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1536" ": "), ::sal::detail::unwrapStream( ::sal::detail ::StreamStart() << "Exception during table creation" << " " << exceptionToString(tools_warn_exception)), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "Exception during table creation" << " " << exceptionToString(tools_warn_exception); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx" ":" "1536" ": "), sal_detail_stream, 0); }; std::abort(); break ; } } } while (false); } while (false); | |||
1537 | } | |||
1538 | ||||
1539 | // If we have a table with a start and an end position, we should make it a floating one. | |||
1540 | // Unless the table had a foot or endnote, as Writer doesn't support those in TextFrames. | |||
1541 | if (xTable.is() && xStart.is() && xEnd.is() && !m_bHadFootOrEndnote) | |||
1542 | { | |||
1543 | uno::Reference<beans::XPropertySet> xTableProperties(xTable, uno::UNO_QUERY); | |||
1544 | bool bIsRelative = false; | |||
1545 | xTableProperties->getPropertyValue("IsWidthRelative") >>= bIsRelative; | |||
1546 | if (!bIsRelative) | |||
1547 | { | |||
1548 | beans::PropertyValue aValue; | |||
1549 | aValue.Name = "Width"; | |||
1550 | aValue.Value = xTableProperties->getPropertyValue("Width"); | |||
1551 | aFrameProperties.push_back(aValue); | |||
1552 | } | |||
1553 | else | |||
1554 | { | |||
1555 | beans::PropertyValue aValue; | |||
1556 | aValue.Name = "FrameWidthPercent"; | |||
1557 | aValue.Value = xTableProperties->getPropertyValue("RelativeWidth"); | |||
1558 | aFrameProperties.push_back(aValue); | |||
1559 | ||||
1560 | // Applying the relative width to the frame, needs to have the table width to be 100% of the frame width | |||
1561 | xTableProperties->setPropertyValue("RelativeWidth", uno::makeAny(sal_Int16(100))); | |||
1562 | } | |||
1563 | ||||
1564 | // A non-zero left margin would move the table out of the frame, move the frame itself instead. | |||
1565 | xTableProperties->setPropertyValue("LeftMargin", uno::makeAny(sal_Int32(0))); | |||
1566 | ||||
1567 | if (nestedTableLevel >= 2) | |||
1568 | { | |||
1569 | // Floating tables inside a table always stay inside the cell. | |||
1570 | aFrameProperties.push_back( | |||
1571 | comphelper::makePropertyValue("IsFollowingTextFlow", true)); | |||
1572 | } | |||
1573 | ||||
1574 | // In case the document ends with a table, we're called after | |||
1575 | // SectionPropertyMap::CloseSectionGroup(), so we'll have no idea | |||
1576 | // about the text area width, nor can fix this by delaying the text | |||
1577 | // frame conversion: just do it here. | |||
1578 | // Also, when the anchor is within a table, then do it here as well, | |||
1579 | // as xStart/xEnd would not point to the start/end at conversion | |||
1580 | // time anyway. | |||
1581 | // Next exception: it's pointless to delay the conversion if the | |||
1582 | // table is not in the body text. | |||
1583 | sal_Int32 nTableWidth = 0; | |||
1584 | m_aTableProperties->getValue(TablePropertyMap::TABLE_WIDTH, nTableWidth); | |||
1585 | sal_Int32 nTableWidthType = text::SizeType::FIX; | |||
1586 | m_aTableProperties->getValue(TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType); | |||
1587 | if (m_rDMapper_Impl.GetSectionContext() && nestedTableLevel <= 1 && !m_rDMapper_Impl.IsInHeaderFooter()) | |||
1588 | m_rDMapper_Impl.m_aPendingFloatingTables.emplace_back(xStart, xEnd, comphelper::containerToSequence(aFrameProperties), nTableWidth, nTableWidthType); | |||
1589 | else | |||
1590 | { | |||
1591 | // m_xText points to the body text, get the current xText from m_rDMapper_Impl, in case e.g. we would be in a header. | |||
1592 | uno::Reference<text::XTextAppendAndConvert> xTextAppendAndConvert(m_rDMapper_Impl.GetTopTextAppend(), uno::UNO_QUERY); | |||
1593 | // Only execute the conversion if the table is not anchored at | |||
1594 | // the start of an outer table cell, that's not yet | |||
1595 | // implemented. | |||
1596 | if (xTextAppendAndConvert.is() && !bTableStartsAtCellStart) | |||
1597 | xTextAppendAndConvert->convertToTextFrame(xStart, xEnd, comphelper::containerToSequence(aFrameProperties)); | |||
1598 | } | |||
1599 | } | |||
1600 | ||||
1601 | // We're right after a table conversion. | |||
1602 | m_rDMapper_Impl.m_bConvertedTable = true; | |||
1603 | } | |||
1604 | ||||
1605 | m_aTableProperties.clear(); | |||
1606 | m_aCellProperties.clear(); | |||
1607 | m_aRowProperties.clear(); | |||
1608 | m_bHadFootOrEndnote = false; | |||
1609 | ||||
1610 | #ifdef DBG_UTIL | |||
1611 | TagLogger::getInstance().endElement(); | |||
1612 | TagLogger::getInstance().endElement(); | |||
1613 | #endif | |||
1614 | } | |||
1615 | ||||
1616 | void DomainMapperTableHandler::startRow(const TablePropertyMapPtr& pProps) | |||
1617 | { | |||
1618 | m_aRowProperties.push_back( pProps.get() ); | |||
1619 | m_aCellProperties.emplace_back( ); | |||
1620 | ||||
1621 | #ifdef DBG_UTIL | |||
1622 | TagLogger::getInstance().startElement("table.row"); | |||
1623 | if (pProps != nullptr) | |||
1624 | pProps->dumpXml(); | |||
1625 | #endif | |||
1626 | ||||
1627 | m_aRowRanges.clear(); | |||
1628 | } | |||
1629 | ||||
1630 | void DomainMapperTableHandler::endRow() | |||
1631 | { | |||
1632 | m_aTableRanges.push_back(comphelper::containerToSequence(m_aRowRanges)); | |||
1633 | #ifdef DBG_UTIL | |||
1634 | TagLogger::getInstance().endElement(); | |||
1635 | #endif | |||
1636 | } | |||
1637 | ||||
1638 | void DomainMapperTableHandler::startCell(const css::uno::Reference< css::text::XTextRange > & start, | |||
1639 | const TablePropertyMapPtr& pProps ) | |||
1640 | { | |||
1641 | sal_uInt32 nRow = m_aRowProperties.size(); | |||
1642 | if ( pProps ) | |||
1643 | m_aCellProperties[nRow - 1].push_back( pProps.get() ); | |||
1644 | else | |||
1645 | { | |||
1646 | // Adding an empty cell properties map to be able to get | |||
1647 | // the table defaults properties | |||
1648 | TablePropertyMapPtr pEmptyProps( new TablePropertyMap( ) ); | |||
1649 | m_aCellProperties[nRow - 1].push_back( pEmptyProps.get() ); | |||
1650 | } | |||
1651 | ||||
1652 | #ifdef DBG_UTIL | |||
1653 | TagLogger::getInstance().startElement("table.cell"); | |||
1654 | TagLogger::getInstance().startElement("table.cell.start"); | |||
1655 | TagLogger::getInstance().chars(XTextRangeToString(start)); | |||
1656 | TagLogger::getInstance().endElement(); | |||
1657 | if (pProps) | |||
1658 | pProps->printProperties(); | |||
1659 | #endif | |||
1660 | ||||
1661 | //add a new 'row' of properties | |||
1662 | m_aCellRange.clear(); | |||
1663 | uno::Reference<text::XTextRange> xStart; | |||
1664 | if (start) | |||
1665 | xStart = start->getStart(); | |||
1666 | m_aCellRange.push_back(xStart); | |||
1667 | } | |||
1668 | ||||
1669 | void DomainMapperTableHandler::endCell(const css::uno::Reference< css::text::XTextRange > & end) | |||
1670 | { | |||
1671 | #ifdef DBG_UTIL | |||
1672 | TagLogger::getInstance().startElement("table.cell.end"); | |||
1673 | TagLogger::getInstance().chars(XTextRangeToString(end)); | |||
1674 | TagLogger::getInstance().endElement(); | |||
1675 | TagLogger::getInstance().endElement(); | |||
1676 | #endif | |||
1677 | ||||
1678 | uno::Reference<text::XTextRange> xEnd; | |||
1679 | if (end) | |||
1680 | xEnd = end->getEnd(); | |||
1681 | m_aCellRange.push_back(xEnd); | |||
1682 | m_aRowRanges.push_back(comphelper::containerToSequence(m_aCellRange)); | |||
1683 | } | |||
1684 | ||||
1685 | void DomainMapperTableHandler::setHadFootOrEndnote(bool bHadFootOrEndnote) | |||
1686 | { | |||
1687 | m_bHadFootOrEndnote = bHadFootOrEndnote; | |||
1688 | } | |||
1689 | ||||
1690 | DomainMapper_Impl& DomainMapperTableHandler::getDomainMapperImpl() | |||
1691 | { | |||
1692 | return m_rDMapper_Impl; | |||
1693 | } | |||
1694 | ||||
1695 | } | |||
1696 | ||||
1697 | /* 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_TOOLS_REF_HXX | ||||
20 | #define INCLUDED_TOOLS_REF_HXX | ||||
21 | |||||
22 | #include <sal/config.h> | ||||
23 | #include <cassert> | ||||
24 | #include <tools/toolsdllapi.h> | ||||
25 | #include <utility> | ||||
26 | |||||
27 | /** | ||||
28 | This implements similar functionality to boost::intrusive_ptr | ||||
29 | */ | ||||
30 | |||||
31 | namespace tools { | ||||
32 | |||||
33 | /** T must be a class that extends SvRefBase */ | ||||
34 | template<typename T> class SAL_DLLPUBLIC_RTTI__attribute__ ((type_visibility("default"))) SvRef final { | ||||
35 | public: | ||||
36 | SvRef(): pObj(nullptr) {} | ||||
37 | |||||
38 | SvRef(SvRef&& rObj) noexcept | ||||
39 | { | ||||
40 | pObj = rObj.pObj; | ||||
41 | rObj.pObj = nullptr; | ||||
42 | } | ||||
43 | |||||
44 | SvRef(SvRef const & rObj): pObj(rObj.pObj) | ||||
45 | { | ||||
46 | if (pObj != nullptr) pObj->AddNextRef(); | ||||
47 | } | ||||
48 | |||||
49 | SvRef(T * pObjP): pObj(pObjP) | ||||
50 | { | ||||
51 | if (pObj != nullptr) pObj->AddFirstRef(); | ||||
52 | } | ||||
53 | |||||
54 | ~SvRef() | ||||
55 | { | ||||
56 | if (pObj != nullptr) pObj->ReleaseRef(); | ||||
57 | } | ||||
58 | |||||
59 | void clear() | ||||
60 | { | ||||
61 | if (pObj != nullptr) { | ||||
62 | T * pRefObj = pObj; | ||||
63 | pObj = nullptr; | ||||
64 | pRefObj->ReleaseRef(); | ||||
65 | } | ||||
66 | } | ||||
67 | |||||
68 | SvRef & operator =(SvRef const & rObj) | ||||
69 | { | ||||
70 | if (rObj.pObj != nullptr) { | ||||
71 | rObj.pObj->AddNextRef(); | ||||
72 | } | ||||
73 | T * pRefObj = pObj; | ||||
74 | pObj = rObj.pObj; | ||||
75 | if (pRefObj != nullptr) { | ||||
76 | pRefObj->ReleaseRef(); | ||||
77 | } | ||||
78 | return *this; | ||||
79 | } | ||||
80 | |||||
81 | SvRef & operator =(SvRef && rObj) | ||||
82 | { | ||||
83 | if (pObj != nullptr) { | ||||
84 | pObj->ReleaseRef(); | ||||
85 | } | ||||
86 | pObj = rObj.pObj; | ||||
87 | rObj.pObj = nullptr; | ||||
88 | return *this; | ||||
89 | } | ||||
90 | |||||
91 | bool is() const { return pObj != nullptr; } | ||||
92 | |||||
93 | explicit operator bool() const { return is(); } | ||||
94 | |||||
95 | T * get() const { return pObj; } | ||||
| |||||
96 | |||||
97 | T * operator ->() const { assert(pObj != nullptr)(static_cast <bool> (pObj != nullptr) ? void (0) : __assert_fail ("pObj != nullptr", "/home/maarten/src/libreoffice/core/include/tools/ref.hxx" , 97, __extension__ __PRETTY_FUNCTION__)); return pObj; } | ||||
98 | |||||
99 | T & operator *() const { assert(pObj != nullptr)(static_cast <bool> (pObj != nullptr) ? void (0) : __assert_fail ("pObj != nullptr", "/home/maarten/src/libreoffice/core/include/tools/ref.hxx" , 99, __extension__ __PRETTY_FUNCTION__)); return *pObj; } | ||||
100 | |||||
101 | bool operator ==(const SvRef<T> &rhs) const { return pObj == rhs.pObj; } | ||||
102 | bool operator !=(const SvRef<T> &rhs) const { return !(*this == rhs); } | ||||
103 | |||||
104 | private: | ||||
105 | T * pObj; | ||||
106 | }; | ||||
107 | |||||
108 | /** | ||||
109 | * This implements similar functionality to std::make_shared. | ||||
110 | */ | ||||
111 | template<typename T, typename... Args> | ||||
112 | SvRef<T> make_ref(Args&& ... args) | ||||
113 | { | ||||
114 | return SvRef<T>(new T(std::forward<Args>(args)...)); | ||||
115 | } | ||||
116 | |||||
117 | } | ||||
118 | |||||
119 | /** Classes that want to be referenced-counted via SvRef<T>, should extend this base class */ | ||||
120 | class TOOLS_DLLPUBLIC__attribute__ ((visibility("default"))) SvRefBase | ||||
121 | { | ||||
122 | // work around a clang 3.5 optimization bug: if the bNoDelete is *first* | ||||
123 | // it mis-compiles "if (--nRefCount == 0)" and never deletes any object | ||||
124 | unsigned int nRefCount : 31; | ||||
125 | // the only reason this is not bool is because MSVC cannot handle mixed type bitfields | ||||
126 | unsigned int bNoDelete : 1; | ||||
127 | |||||
128 | protected: | ||||
129 | virtual ~SvRefBase() COVERITY_NOEXCEPT_FALSE; | ||||
130 | |||||
131 | public: | ||||
132 | SvRefBase() : nRefCount(0), bNoDelete(1) {} | ||||
133 | SvRefBase(const SvRefBase &) : nRefCount(0), bNoDelete(1) {} | ||||
134 | |||||
135 | SvRefBase & operator=(const SvRefBase &) { return *this; } | ||||
136 | |||||
137 | void RestoreNoDelete() | ||||
138 | { bNoDelete = 1; } | ||||
139 | |||||
140 | void AddNextRef() | ||||
141 | { | ||||
142 | assert( nRefCount < (1 << 30) && "Do not add refs to dead objects" )(static_cast <bool> (nRefCount < (1 << 30) && "Do not add refs to dead objects") ? void (0) : __assert_fail ("nRefCount < (1 << 30) && \"Do not add refs to dead objects\"" , "/home/maarten/src/libreoffice/core/include/tools/ref.hxx", 142, __extension__ __PRETTY_FUNCTION__)); | ||||
143 | ++nRefCount; | ||||
144 | } | ||||
145 | |||||
146 | void AddFirstRef() | ||||
147 | { | ||||
148 | assert( nRefCount < (1 << 30) && "Do not add refs to dead objects" )(static_cast <bool> (nRefCount < (1 << 30) && "Do not add refs to dead objects") ? void (0) : __assert_fail ("nRefCount < (1 << 30) && \"Do not add refs to dead objects\"" , "/home/maarten/src/libreoffice/core/include/tools/ref.hxx", 148, __extension__ __PRETTY_FUNCTION__)); | ||||
149 | if( bNoDelete ) | ||||
150 | bNoDelete = 0; | ||||
151 | ++nRefCount; | ||||
152 | } | ||||
153 | |||||
154 | void ReleaseRef() | ||||
155 | { | ||||
156 | assert( nRefCount >= 1)(static_cast <bool> (nRefCount >= 1) ? void (0) : __assert_fail ("nRefCount >= 1", "/home/maarten/src/libreoffice/core/include/tools/ref.hxx" , 156, __extension__ __PRETTY_FUNCTION__)); | ||||
157 | if( --nRefCount == 0 && !bNoDelete
| ||||
158 | { | ||||
159 | // I'm not sure about the original purpose of this line, but right now | ||||
160 | // it serves the purpose that anything that attempts to do an AddRef() | ||||
161 | // after an object is deleted will trip an assert. | ||||
162 | nRefCount = 1 << 30; | ||||
163 | delete this; | ||||
164 | } | ||||
165 | } | ||||
166 | |||||
167 | unsigned int GetRefCount() const | ||||
168 | { return nRefCount; } | ||||
169 | }; | ||||
170 | |||||
171 | template<typename T> | ||||
172 | class SvCompatWeakBase; | ||||
173 | |||||
174 | /** SvCompatWeakHdl acts as an intermediary between SvCompatWeakRef<T> and T. | ||||
175 | */ | ||||
176 | template<typename T> | ||||
177 | class SvCompatWeakHdl final : public SvRefBase | ||||
178 | { | ||||
179 | friend class SvCompatWeakBase<T>; | ||||
180 | T* _pObj; | ||||
181 | |||||
182 | SvCompatWeakHdl( T* pObj ) : _pObj( pObj ) {} | ||||
183 | |||||
184 | public: | ||||
185 | void ResetWeakBase( ) { _pObj = nullptr; } | ||||
186 | T* GetObj() { return _pObj; } | ||||
187 | }; | ||||
188 | |||||
189 | /** We only have one place that extends this, in include/sfx2/frame.hxx, class SfxFrame. | ||||
190 | Its function is to notify the SvCompatWeakHdl when an SfxFrame object is deleted. | ||||
191 | */ | ||||
192 | template<typename T> | ||||
193 | class SvCompatWeakBase | ||||
194 | { | ||||
195 | tools::SvRef< SvCompatWeakHdl<T> > _xHdl; | ||||
196 | |||||
197 | public: | ||||
198 | /** Does not use initializer due to compiler warnings, | ||||
199 | because the lifetime of the _xHdl object can exceed the lifetime of this class. | ||||
200 | */ | ||||
201 | SvCompatWeakBase( T* pObj ) { _xHdl = new SvCompatWeakHdl<T>( pObj ); } | ||||
202 | |||||
203 | ~SvCompatWeakBase() { _xHdl->ResetWeakBase(); } | ||||
204 | |||||
205 | SvCompatWeakHdl<T>* GetHdl() { return _xHdl.get(); } | ||||
206 | }; | ||||
207 | |||||
208 | /** We only have one weak reference in LO, in include/sfx2/frame.hxx, class SfxFrameWeak. | ||||
209 | */ | ||||
210 | template<typename T> | ||||
211 | class SAL_WARN_UNUSED__attribute__((warn_unused)) SvCompatWeakRef | ||||
212 | { | ||||
213 | tools::SvRef< SvCompatWeakHdl<T> > _xHdl; | ||||
214 | public: | ||||
215 | SvCompatWeakRef( ) {} | ||||
216 | SvCompatWeakRef( T* pObj ) | ||||
217 | { if( pObj ) _xHdl = pObj->GetHdl(); } | ||||
218 | #if defined(__COVERITY__) | ||||
219 | ~SvCompatWeakRef() COVERITY_NOEXCEPT_FALSE {} | ||||
220 | #endif | ||||
221 | SvCompatWeakRef& operator = ( T * pObj ) | ||||
222 | { _xHdl = pObj ? pObj->GetHdl() : nullptr; return *this; } | ||||
223 | bool is() const | ||||
224 | { return _xHdl.is() && _xHdl->GetObj(); } | ||||
225 | explicit operator bool() const { return is(); } | ||||
226 | T* operator -> () const | ||||
227 | { return _xHdl.is() ? _xHdl->GetObj() : nullptr; } | ||||
228 | operator T* () const | ||||
229 | { return _xHdl.is() ? _xHdl->GetObj() : nullptr; } | ||||
230 | }; | ||||
231 | |||||
232 | #endif | ||||
233 | |||||
234 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |