xref: /core/sw/source/core/unocore/unotbl.cxx (revision 24314773)
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 <tuple>
21 #include <utility>
22 #include <memory>
23 #include <vector>
24 #include <algorithm>
25 
26 #include <o3tl/any.hxx>
27 #include <svx/svxids.hrc>
28 #include <editeng/memberids.h>
29 #include <float.h>
30 #include <swtypes.hxx>
31 #include <cmdid.h>
32 #include <unocoll.hxx>
33 #include <unomid.h>
34 #include <unomap.hxx>
35 #include <unotbl.hxx>
36 #include <section.hxx>
37 #include <unocrsr.hxx>
38 #include <hints.hxx>
39 #include <swtblfmt.hxx>
40 #include <doc.hxx>
41 #include <IDocumentUndoRedo.hxx>
42 #include <IDocumentContentOperations.hxx>
43 #include <IDocumentFieldsAccess.hxx>
44 #include <IDocumentState.hxx>
45 #include <IDocumentLayoutAccess.hxx>
46 #include <shellres.hxx>
47 #include <docary.hxx>
48 #include <ndole.hxx>
49 #include <ndtxt.hxx>
50 #include <frame.hxx>
51 #include <vcl/svapp.hxx>
52 #include <fmtfsize.hxx>
53 #include <tblafmt.hxx>
54 #include <tabcol.hxx>
55 #include <cellatr.hxx>
56 #include <fmtpdsc.hxx>
57 #include <pagedesc.hxx>
58 #include <viewsh.hxx>
59 #include <rootfrm.hxx>
60 #include <tabfrm.hxx>
61 #include <redline.hxx>
62 #include <unoport.hxx>
63 #include <unocrsrhelper.hxx>
64 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
65 #include <com/sun/star/text/WrapTextMode.hpp>
66 #include <com/sun/star/text/TextContentAnchorType.hpp>
67 #include <com/sun/star/text/TableColumnSeparator.hpp>
68 #include <com/sun/star/text/VertOrientation.hpp>
69 #include <com/sun/star/text/XTextSection.hpp>
70 #include <com/sun/star/table/TableBorder.hpp>
71 #include <com/sun/star/table/TableBorder2.hpp>
72 #include <com/sun/star/table/BorderLine2.hpp>
73 #include <com/sun/star/table/TableBorderDistances.hpp>
74 #include <com/sun/star/beans/PropertyAttribute.hpp>
75 #include <com/sun/star/chart/XChartDataChangeEventListener.hpp>
76 #include <com/sun/star/chart/ChartDataChangeEvent.hpp>
77 #include <com/sun/star/table/CellContentType.hpp>
78 #include <unotextrange.hxx>
79 #include <unotextcursor.hxx>
80 #include <unoparagraph.hxx>
81 #include <svl/zforlist.hxx>
82 #include <editeng/formatbreakitem.hxx>
83 #include <editeng/shaditem.hxx>
84 #include <editeng/lrspitem.hxx>
85 #include <editeng/ulspitem.hxx>
86 #include <fmtornt.hxx>
87 #include <editeng/keepitem.hxx>
88 #include <fmtlsplt.hxx>
89 #include <swundo.hxx>
90 #include <osl/mutex.hxx>
91 #include <SwStyleNameMapper.hxx>
92 #include <frmatr.hxx>
93 #include <sortopt.hxx>
94 #include <rtl/math.hxx>
95 #include <sal/log.hxx>
96 #include <editeng/frmdiritem.hxx>
97 #include <comphelper/interfacecontainer2.hxx>
98 #include <comphelper/servicehelper.hxx>
99 #include <comphelper/string.hxx>
100 #include <cppuhelper/supportsservice.hxx>
101 #include <comphelper/sequence.hxx>
102 #include <comphelper/sequenceashashmap.hxx>
103 #include <swtable.hxx>
104 #include <docsh.hxx>
105 #include <fesh.hxx>
106 #include <itabenum.hxx>
107 
108 using namespace ::com::sun::star;
109 using ::editeng::SvxBorderLine;
110 
111 namespace
112 {
113     template<typename Tcoretype, typename Tunotype>
114     struct FindUnoInstanceHint final : SfxHint
115     {
116         FindUnoInstanceHint(Tcoretype* pCore) : m_pCore(pCore), m_pResult(nullptr) {};
117         const Tcoretype* const m_pCore;
118         mutable Tunotype* m_pResult;
119     };
120     SwFrameFormat* lcl_EnsureCoreConnected(SwFrameFormat* pFormat, cppu::OWeakObject* pObject)
121     {
122         if(!pFormat)
123             throw uno::RuntimeException("Lost connection to core objects", pObject);
124         return pFormat;
125     }
126     SwTable* lcl_EnsureTableNotComplex(SwTable* pTable, cppu::OWeakObject* pObject)
127     {
128         if(pTable->IsTableComplex())
129             throw uno::RuntimeException("Table too complex", pObject);
130         return pTable;
131     }
132 
133     chart::ChartDataChangeEvent createChartEvent(uno::Reference<uno::XInterface> const& xSource)
134     {
135         //TODO: find appropriate settings of the Event
136         chart::ChartDataChangeEvent event;
137         event.Source = xSource;
138         event.Type = chart::ChartDataChangeType_ALL;
139         event.StartColumn = 0;
140         event.EndColumn = 1;
141         event.StartRow = 0;
142         event.EndRow = 1;
143         return event;
144     }
145 
146     void lcl_SendChartEvent(
147             uno::Reference<uno::XInterface> const& xSource,
148             ::comphelper::OInterfaceContainerHelper2& rListeners)
149     {
150         rListeners.notifyEach(
151                 &chart::XChartDataChangeEventListener::chartDataChanged,
152                 createChartEvent(xSource));
153     }
154 
155     void lcl_SendChartEvent(
156             uno::Reference<uno::XInterface> const& xSource,
157             ::cppu::OMultiTypeInterfaceContainerHelper const& rListeners)
158     {
159         auto pContainer(rListeners.getContainer(cppu::UnoType<chart::XChartDataChangeEventListener>::get()));
160         if (pContainer)
161             pContainer->notifyEach(
162                     &chart::XChartDataChangeEventListener::chartDataChanged,
163                     createChartEvent(xSource));
164     }
165 }
166 
167 #define UNO_TABLE_COLUMN_SUM 10000
168 
169 
170 static bool lcl_LineToSvxLine(const table::BorderLine& rLine, SvxBorderLine& rSvxLine)
171 {
172     rSvxLine.SetColor(Color(rLine.Color));
173 
174     rSvxLine.GuessLinesWidths( SvxBorderLineStyle::NONE,
175                                 convertMm100ToTwip( rLine.OuterLineWidth ),
176                                 convertMm100ToTwip( rLine.InnerLineWidth ),
177                                 convertMm100ToTwip( rLine.LineDistance ) );
178 
179     return rLine.InnerLineWidth > 0 || rLine.OuterLineWidth > 0;
180 }
181 
182 /// @throws lang::IllegalArgumentException
183 /// @throws uno::RuntimeException
184 static void lcl_SetSpecialProperty(SwFrameFormat* pFormat,
185                                    const SfxItemPropertySimpleEntry* pEntry,
186                                    const uno::Any& aValue)
187 {
188     // special treatment for "non-items"
189     switch(pEntry->nWID)
190     {
191         case  FN_TABLE_HEADLINE_REPEAT:
192         case  FN_TABLE_HEADLINE_COUNT:
193         {
194             SwTable* pTable = SwTable::FindTable( pFormat );
195             UnoActionContext aAction(pFormat->GetDoc());
196             if( pEntry->nWID == FN_TABLE_HEADLINE_REPEAT)
197             {
198                 pFormat->GetDoc()->SetRowsToRepeat( *pTable, aValue.get<bool>() ? 1 : 0 );
199             }
200             else
201             {
202                 sal_Int32 nRepeat = 0;
203                 aValue >>= nRepeat;
204                 if( nRepeat >= 0 && nRepeat < SAL_MAX_UINT16 )
205                     pFormat->GetDoc()->SetRowsToRepeat( *pTable, static_cast<sal_uInt16>(nRepeat) );
206             }
207         }
208         break;
209 
210         case  FN_TABLE_IS_RELATIVE_WIDTH:
211         case  FN_TABLE_WIDTH:
212         case  FN_TABLE_RELATIVE_WIDTH:
213         {
214             SwFormatFrameSize aSz( pFormat->GetFrameSize() );
215             if(FN_TABLE_WIDTH == pEntry->nWID)
216             {
217                 sal_Int32 nWidth = 0;
218                 aValue >>= nWidth;
219                 aSz.SetWidthPercent(0);
220                 aSz.SetWidth ( convertMm100ToTwip ( nWidth ) );
221             }
222             else if(FN_TABLE_RELATIVE_WIDTH == pEntry->nWID)
223             {
224                 sal_Int16 nSet = 0;
225                 aValue >>= nSet;
226                 if(nSet && nSet <=100)
227                     aSz.SetWidthPercent( static_cast<sal_uInt8>(nSet) );
228             }
229             else if(FN_TABLE_IS_RELATIVE_WIDTH == pEntry->nWID)
230             {
231                 if(!aValue.get<bool>())
232                     aSz.SetWidthPercent(0);
233                 else
234                 {
235                     lang::IllegalArgumentException aExcept;
236                     aExcept.Message = "relative width cannot be switched on with this property";
237                     throw aExcept;
238                 }
239             }
240             pFormat->GetDoc()->SetAttr(aSz, *pFormat);
241         }
242         break;
243 
244         case RES_PAGEDESC:
245         {
246             OUString sPageStyle;
247             aValue >>= sPageStyle;
248             const SwPageDesc* pDesc = nullptr;
249             if (!sPageStyle.isEmpty())
250             {
251                 SwStyleNameMapper::FillUIName(sPageStyle, sPageStyle, SwGetPoolIdFromName::PageDesc);
252                 pDesc = SwPageDesc::GetByName(*pFormat->GetDoc(), sPageStyle);
253             }
254             SwFormatPageDesc aDesc( pDesc );
255             pFormat->GetDoc()->SetAttr(aDesc, *pFormat);
256         }
257         break;
258 
259         default:
260             throw lang::IllegalArgumentException();
261     }
262 }
263 
264 static uno::Any lcl_GetSpecialProperty(SwFrameFormat* pFormat, const SfxItemPropertySimpleEntry* pEntry )
265 {
266     switch(pEntry->nWID)
267     {
268         case  FN_TABLE_HEADLINE_REPEAT:
269         case  FN_TABLE_HEADLINE_COUNT:
270         {
271             SwTable* pTable = SwTable::FindTable( pFormat );
272             const sal_uInt16 nRepeat = pTable->GetRowsToRepeat();
273             if(pEntry->nWID == FN_TABLE_HEADLINE_REPEAT)
274                 return uno::makeAny<bool>(nRepeat > 0);
275             return uno::makeAny<sal_Int32>(nRepeat);
276         }
277 
278         case  FN_TABLE_WIDTH:
279         case  FN_TABLE_IS_RELATIVE_WIDTH:
280         case  FN_TABLE_RELATIVE_WIDTH:
281         {
282             uno::Any aRet;
283             const SwFormatFrameSize& rSz = pFormat->GetFrameSize();
284             if(FN_TABLE_WIDTH == pEntry->nWID)
285                 rSz.QueryValue(aRet, MID_FRMSIZE_WIDTH|CONVERT_TWIPS);
286             else if(FN_TABLE_RELATIVE_WIDTH == pEntry->nWID)
287                 rSz.QueryValue(aRet, MID_FRMSIZE_REL_WIDTH);
288             else
289                 aRet <<= (0 != rSz.GetWidthPercent());
290             return aRet;
291         }
292 
293         case RES_PAGEDESC:
294         {
295             const SfxItemSet& rSet = pFormat->GetAttrSet();
296             const SfxPoolItem* pItem;
297             if(SfxItemState::SET == rSet.GetItemState(RES_PAGEDESC, false, &pItem))
298             {
299                 const SwPageDesc* pDsc = static_cast<const SwFormatPageDesc*>(pItem)->GetPageDesc();
300                 if(pDsc)
301                     return uno::makeAny<OUString>(SwStyleNameMapper::GetProgName(pDsc->GetName(), SwGetPoolIdFromName::PageDesc ));
302             }
303             return uno::makeAny(OUString());
304         }
305 
306         case RES_ANCHOR:
307             return uno::makeAny(text::TextContentAnchorType_AT_PARAGRAPH);
308 
309         case FN_UNO_ANCHOR_TYPES:
310         {
311             uno::Sequence<text::TextContentAnchorType> aTypes{text::TextContentAnchorType_AT_PARAGRAPH};
312             return uno::makeAny(aTypes);
313         }
314 
315         case FN_UNO_WRAP :
316             return uno::makeAny(text::WrapTextMode_NONE);
317 
318         case FN_PARAM_LINK_DISPLAY_NAME :
319             return uno::makeAny(pFormat->GetName());
320 
321         case FN_UNO_REDLINE_NODE_START:
322         case FN_UNO_REDLINE_NODE_END:
323         {
324             SwTable* pTable = SwTable::FindTable( pFormat );
325             SwNode* pTableNode = pTable->GetTableNode();
326             if(FN_UNO_REDLINE_NODE_END == pEntry->nWID)
327                 pTableNode = pTableNode->EndOfSectionNode();
328             for(const SwRangeRedline* pRedline : pFormat->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable())
329             {
330                 const SwNode& rRedPointNode = pRedline->GetNode();
331                 const SwNode& rRedMarkNode = pRedline->GetNode(false);
332                 if(&rRedPointNode == pTableNode || &rRedMarkNode == pTableNode)
333                 {
334                     const SwNode& rStartOfRedline = SwNodeIndex(rRedPointNode) <= SwNodeIndex(rRedMarkNode) ?
335                         rRedPointNode : rRedMarkNode;
336                     bool bIsStart = &rStartOfRedline == pTableNode;
337                     return uno::makeAny(SwXRedlinePortion::CreateRedlineProperties(*pRedline, bIsStart));
338                 }
339             }
340         }
341     }
342     return uno::Any();
343 }
344 
345 /** get position of a cell with a given name
346  *
347  * If everything was OK, the indices for column and row are changed (both >= 0).
348  * In case of errors, at least one of them is < 0.
349  *
350  * Also since the implementations of tables does not really have columns using
351  * this function is appropriate only for tables that are not complex (i.e.
352  * where IsTableComplex() returns false).
353  *
354  * @param rCellName e.g. A1..Z1, a1..z1, AA1..AZ1, Aa1..Az1, BA1..BZ1, Ba1..Bz1, ...
355  * @param [IN,OUT] o_rColumn (0-based)
356  * @param [IN,OUT] o_rRow (0-based)
357  */
358 //TODO: potential for throwing proper exceptions instead of having every caller to check for errors
359 void SwXTextTable::GetCellPosition(const OUString& rCellName, sal_Int32& o_rColumn, sal_Int32& o_rRow)
360 {
361     o_rColumn = o_rRow = -1;    // default return values indicating failure
362     const sal_Int32 nLen = rCellName.getLength();
363     if(!nLen)
364     {
365         SAL_WARN("sw.uno", "failed to get column or row index");
366         return;
367     }
368     sal_Int32 nRowPos = 0;
369     while (nRowPos<nLen)
370     {
371         if (rCellName[nRowPos]>='0' && rCellName[nRowPos]<='9')
372         {
373             break;
374         }
375         ++nRowPos;
376     }
377     if (nRowPos>0 && nRowPos<nLen)
378     {
379         sal_Int32 nColIdx = 0;
380         for (sal_Int32 i = 0;  i < nRowPos;  ++i)
381         {
382             nColIdx *= 52;
383             if (i < nRowPos - 1)
384                 ++nColIdx;
385             const sal_Unicode cChar = rCellName[i];
386             if ('A' <= cChar && cChar <= 'Z')
387                 nColIdx += cChar - 'A';
388             else if ('a' <= cChar && cChar <= 'z')
389                 nColIdx += 26 + cChar - 'a';
390             else
391             {
392                 nColIdx = -1;   // sth failed
393                 break;
394             }
395         }
396 
397         o_rColumn = nColIdx;
398         o_rRow    = rCellName.copy(nRowPos).toInt32() - 1; // - 1 because indices ought to be 0 based
399     }
400 }
401 
402 /** compare position of two cells (check rows first)
403  *
404  * @note this function probably also make sense only
405  *       for cell names of non-complex tables
406  *
407  * @param rCellName1 e.g. "A1" (non-empty string with valid cell name)
408  * @param rCellName2 e.g. "A1" (non-empty string with valid cell name)
409  * @return -1 if cell_1 < cell_2; 0 if both cells are equal; +1 if cell_1 > cell_2
410  */
411 int sw_CompareCellsByRowFirst( const OUString &rCellName1, const OUString &rCellName2 )
412 {
413     sal_Int32 nCol1 = -1, nRow1 = -1, nCol2 = -1, nRow2 = -1;
414     SwXTextTable::GetCellPosition( rCellName1, nCol1, nRow1 );
415     SwXTextTable::GetCellPosition( rCellName2, nCol2, nRow2 );
416 
417     if (nRow1 < nRow2 || (nRow1 == nRow2 && nCol1 < nCol2))
418         return -1;
419     else if (nCol1 == nCol2 && nRow1 == nRow2)
420         return 0;
421     else
422         return +1;
423 }
424 
425 /** compare position of two cells (check columns first)
426  *
427  * @note this function probably also make sense only
428  *       for cell names of non-complex tables
429  *
430  * @param rCellName1 e.g. "A1" (non-empty string with valid cell name)
431  * @param rCellName2 e.g. "A1" (non-empty string with valid cell name)
432  * @return -1 if cell_1 < cell_2; 0 if both cells are equal; +1 if cell_1 > cell_2
433  */
434 int sw_CompareCellsByColFirst( const OUString &rCellName1, const OUString &rCellName2 )
435 {
436     sal_Int32 nCol1 = -1, nRow1 = -1, nCol2 = -1, nRow2 = -1;
437     SwXTextTable::GetCellPosition( rCellName1, nCol1, nRow1 );
438     SwXTextTable::GetCellPosition( rCellName2, nCol2, nRow2 );
439 
440     if (nCol1 < nCol2 || (nCol1 == nCol2 && nRow1 < nRow2))
441         return -1;
442     else if (nRow1 == nRow2 && nCol1 == nCol2)
443         return 0;
444     else
445         return +1;
446 }
447 
448 /** compare position of two cell ranges
449  *
450  * @note this function probably also make sense only
451  *       for cell names of non-complex tables
452  *
453  * @param rRange1StartCell e.g. "A1" (non-empty string with valid cell name)
454  * @param rRange1EndCell   e.g. "A1" (non-empty string with valid cell name)
455  * @param rRange2StartCell e.g. "A1" (non-empty string with valid cell name)
456  * @param rRange2EndCell   e.g. "A1" (non-empty string with valid cell name)
457  * @param bCmpColsFirst    if <true> position in columns will be compared first before rows
458  *
459  * @return -1 if cell_range_1 < cell_range_2; 0 if both cell ranges are equal; +1 if cell_range_1 > cell_range_2
460  */
461 int sw_CompareCellRanges(
462         const OUString &rRange1StartCell, const OUString &rRange1EndCell,
463         const OUString &rRange2StartCell, const OUString &rRange2EndCell,
464         bool bCmpColsFirst )
465 {
466     int (*pCompareCells)( const OUString &, const OUString & ) =
467             bCmpColsFirst ? &sw_CompareCellsByColFirst : &sw_CompareCellsByRowFirst;
468 
469     int nCmpResStartCells = pCompareCells( rRange1StartCell, rRange2StartCell );
470     if ((-1 == nCmpResStartCells ) ||
471          ( 0 == nCmpResStartCells &&
472           -1 == pCompareCells( rRange1EndCell, rRange2EndCell ) ))
473         return -1;
474     else if (0 == nCmpResStartCells &&
475              0 == pCompareCells( rRange1EndCell, rRange2EndCell ))
476         return 0;
477     else
478         return +1;
479 }
480 
481 /** get cell name at a specified coordinate
482  *
483  * @param nColumn column index (0-based)
484  * @param nRow row index (0-based)
485  * @return the cell name
486  */
487 OUString sw_GetCellName( sal_Int32 nColumn, sal_Int32 nRow )
488 {
489     if (nColumn < 0 || nRow < 0)
490         return OUString();
491     OUString sCellName;
492     sw_GetTableBoxColStr( static_cast< sal_uInt16 >(nColumn), sCellName );
493     return sCellName + OUString::number( nRow + 1 );
494 }
495 
496 /** Find the top left or bottom right corner box in given table.
497   Consider nested lines when finding the box.
498 
499   @param rTableLines the table
500   @param i_bTopLeft if true, find top left box, otherwise find bottom
501          right box
502  */
503 static const SwTableBox* lcl_FindCornerTableBox(const SwTableLines& rTableLines, const bool i_bTopLeft)
504 {
505     const SwTableLines* pLines(&rTableLines);
506     while(true)
507     {
508         assert(!pLines->empty());
509         if(pLines->empty())
510             return nullptr;
511         const SwTableLine* pLine(i_bTopLeft ? pLines->front() : pLines->back());
512         assert(pLine);
513         const SwTableBoxes& rBoxes(pLine->GetTabBoxes());
514         assert(rBoxes.size() != 0);
515         const SwTableBox* pBox = i_bTopLeft ? rBoxes.front() : rBoxes.back();
516         assert(pBox);
517         if (pBox->GetSttNd())
518             return pBox;
519         pLines = &pBox->GetTabLines();
520     }
521 }
522 
523 /** cleanup order in a range
524  *
525  * Sorts the input to a uniform format. I.e. for the four possible representation
526  *      A1:C5, C5:A1, A5:C1, C1:A5
527  * the result will be always A1:C5.
528  *
529  * @param [IN,OUT] rCell1 cell name (will be modified to upper-left corner), e.g. "A1" (non-empty string with valid cell name)
530  * @param [IN,OUT] rCell2 cell name (will be modified to lower-right corner), e.g. "A1" (non-empty string with valid cell name)
531  */
532 void sw_NormalizeRange(OUString &rCell1, OUString &rCell2)
533 {
534     sal_Int32 nCol1 = -1, nRow1 = -1, nCol2 = -1, nRow2 = -1;
535     SwXTextTable::GetCellPosition( rCell1, nCol1, nRow1 );
536     SwXTextTable::GetCellPosition( rCell2, nCol2, nRow2 );
537     if (nCol2 < nCol1 || nRow2 < nRow1)
538     {
539         rCell1  = sw_GetCellName( std::min(nCol1, nCol2), std::min(nRow1, nRow2) );
540         rCell2  = sw_GetCellName( std::max(nCol1, nCol2), std::max(nRow1, nRow2) );
541     }
542 }
543 
544 void SwRangeDescriptor::Normalize()
545 {
546     if (nTop > nBottom)
547         std::swap(nBottom, nTop);
548     if (nLeft > nRight)
549         std::swap(nLeft, nRight);
550 }
551 
552 static SwXCell* lcl_CreateXCell(SwFrameFormat* pFormat, sal_Int32 nColumn, sal_Int32 nRow)
553 {
554     const OUString sCellName = sw_GetCellName(nColumn, nRow);
555     SwTable* pTable = SwTable::FindTable(pFormat);
556     SwTableBox* pBox = const_cast<SwTableBox*>(pTable->GetTableBox(sCellName));
557     if(!pBox)
558         return nullptr;
559     return SwXCell::CreateXCell(pFormat, pBox, pTable);
560 }
561 
562 static void lcl_InspectLines(SwTableLines& rLines, std::vector<OUString>& rAllNames)
563 {
564     for(auto pLine : rLines)
565     {
566         for(auto pBox : pLine->GetTabBoxes())
567         {
568             if(!pBox->GetName().isEmpty() && pBox->getRowSpan() > 0)
569                 rAllNames.push_back(pBox->GetName());
570             SwTableLines& rBoxLines = pBox->GetTabLines();
571             if(!rBoxLines.empty())
572                 lcl_InspectLines(rBoxLines, rAllNames);
573         }
574     }
575 }
576 
577 static bool lcl_FormatTable(SwFrameFormat const * pTableFormat)
578 {
579     bool bHasFrames = false;
580     SwIterator<SwFrame,SwFormat> aIter( *pTableFormat );
581     for(SwFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
582     {
583         vcl::RenderContext* pRenderContext = pFrame->getRootFrame()->GetCurrShell()->GetOut();
584         // mba: no TYPEINFO for SwTabFrame
585         if(!pFrame->IsTabFrame())
586             continue;
587         DisableCallbackAction a(*pFrame->getRootFrame());
588         SwTabFrame* pTabFrame = static_cast<SwTabFrame*>(pFrame);
589         if(pTabFrame->isFrameAreaDefinitionValid())
590             pTabFrame->InvalidatePos();
591         pTabFrame->SetONECalcLowers();
592         pTabFrame->Calc(pRenderContext);
593         bHasFrames = true;
594     }
595     return bHasFrames;
596 }
597 
598 static void lcl_CursorSelect(SwPaM& rCursor, bool bExpand)
599 {
600     if(bExpand)
601     {
602         if(!rCursor.HasMark())
603             rCursor.SetMark();
604     }
605     else if(rCursor.HasMark())
606         rCursor.DeleteMark();
607 }
608 
609 static void lcl_GetTableSeparators(uno::Any& rRet, SwTable const * pTable, SwTableBox const * pBox, bool bRow)
610 {
611     SwTabCols aCols;
612     aCols.SetLeftMin ( 0 );
613     aCols.SetLeft    ( 0 );
614     aCols.SetRight   ( UNO_TABLE_COLUMN_SUM );
615     aCols.SetRightMax( UNO_TABLE_COLUMN_SUM );
616 
617     pTable->GetTabCols( aCols, pBox, false, bRow );
618 
619     const size_t nSepCount = aCols.Count();
620     uno::Sequence< text::TableColumnSeparator> aColSeq(nSepCount);
621     text::TableColumnSeparator* pArray = aColSeq.getArray();
622     bool bError = false;
623     for(size_t i = 0; i < nSepCount; ++i)
624     {
625         pArray[i].Position = static_cast< sal_Int16 >(aCols[i]);
626         pArray[i].IsVisible = !aCols.IsHidden(i);
627         if(!bRow && !pArray[i].IsVisible)
628         {
629             bError = true;
630             break;
631         }
632     }
633     if(!bError)
634         rRet <<= aColSeq;
635 
636 }
637 
638 static void lcl_SetTableSeparators(const uno::Any& rVal, SwTable* pTable, SwTableBox const * pBox, bool bRow, SwDoc* pDoc)
639 {
640     SwTabCols aOldCols;
641 
642     aOldCols.SetLeftMin ( 0 );
643     aOldCols.SetLeft    ( 0 );
644     aOldCols.SetRight   ( UNO_TABLE_COLUMN_SUM );
645     aOldCols.SetRightMax( UNO_TABLE_COLUMN_SUM );
646 
647     pTable->GetTabCols( aOldCols, pBox, false, bRow );
648     const size_t nOldCount = aOldCols.Count();
649     // there is no use in setting tab cols if there is only one column
650     if( !nOldCount )
651         return;
652 
653     auto pSepSeq =
654                 o3tl::tryAccess<uno::Sequence<text::TableColumnSeparator>>(rVal);
655     if(!pSepSeq || static_cast<size_t>(pSepSeq->getLength()) != nOldCount)
656         return;
657     SwTabCols aCols(aOldCols);
658     const text::TableColumnSeparator* pArray = pSepSeq->getConstArray();
659     long nLastValue = 0;
660     //sal_Int32 nTableWidth = aCols.GetRight() - aCols.GetLeft();
661     for(size_t i = 0; i < nOldCount; ++i)
662     {
663         aCols[i] = pArray[i].Position;
664         if(bool(pArray[i].IsVisible) == aCols.IsHidden(i) ||
665                 (!bRow && aCols.IsHidden(i)) ||
666                 aCols[i] < nLastValue ||
667                 UNO_TABLE_COLUMN_SUM < aCols[i] )
668             return; // probably this should assert()
669         nLastValue = aCols[i];
670     }
671     pDoc->SetTabCols(*pTable, aCols, aOldCols, pBox, bRow );
672 }
673 
674 /* non UNO function call to set string in SwXCell */
675 void sw_setString( SwXCell &rCell, const OUString &rText,
676         bool bKeepNumberFormat = false )
677 {
678     if(rCell.IsValid())
679     {
680         SwFrameFormat* pBoxFormat = rCell.m_pBox->ClaimFrameFormat();
681         pBoxFormat->LockModify();
682         pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMULA );
683         pBoxFormat->ResetFormatAttr( RES_BOXATR_VALUE );
684         if (!bKeepNumberFormat)
685             pBoxFormat->SetFormatAttr( SwTableBoxNumFormat(/*default Text*/) );
686         pBoxFormat->UnlockModify();
687     }
688     rCell.SwXText::setString(rText);
689 }
690 
691 
692 /* non UNO function call to set value in SwXCell */
693 void sw_setValue( SwXCell &rCell, double nVal )
694 {
695     if(!rCell.IsValid())
696         return;
697     // first this text (maybe) needs to be deleted
698     sal_uLong nNdPos = rCell.m_pBox->IsValidNumTextNd();
699     if(ULONG_MAX != nNdPos)
700         sw_setString( rCell, OUString(), true );   // true == keep number format
701     SwDoc* pDoc = rCell.GetDoc();
702     UnoActionContext aAction(pDoc);
703     SwFrameFormat* pBoxFormat = rCell.m_pBox->ClaimFrameFormat();
704     SfxItemSet aSet(pDoc->GetAttrPool(), svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{});
705     const SfxPoolItem* pItem;
706 
707     //!! do we need to set a new number format? Yes, if
708     // - there is no current number format
709     // - the current number format is not a number format according to the number formatter, but rather a text format
710     if(SfxItemState::SET != pBoxFormat->GetAttrSet().GetItemState(RES_BOXATR_FORMAT, true, &pItem)
711         ||  pDoc->GetNumberFormatter()->IsTextFormat(static_cast<const SwTableBoxNumFormat*>(pItem)->GetValue()))
712     {
713         aSet.Put(SwTableBoxNumFormat(0));
714     }
715 
716     SwTableBoxValue aVal(nVal);
717     aSet.Put(aVal);
718     pDoc->SetTableBoxFormulaAttrs( *rCell.m_pBox, aSet );
719     // update table
720     SwTableFormulaUpdate aTableUpdate( SwTable::FindTable( rCell.GetFrameFormat() ));
721     pDoc->getIDocumentFieldsAccess().UpdateTableFields( &aTableUpdate );
722 }
723 
724 
725 SwXCell::SwXCell(SwFrameFormat* pTableFormat, SwTableBox* pBx, size_t const nPos) :
726     SwXText(pTableFormat->GetDoc(), CursorType::TableText),
727     m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TABLE_CELL)),
728     m_pBox(pBx),
729     m_pStartNode(nullptr),
730     m_pTableFormat(pTableFormat),
731     m_nFndPos(nPos)
732 {
733     StartListening(pTableFormat->GetNotifier());
734 }
735 
736 SwXCell::SwXCell(SwFrameFormat* pTableFormat, const SwStartNode& rStartNode) :
737     SwXText(pTableFormat->GetDoc(), CursorType::TableText),
738     m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TABLE_CELL)),
739     m_pBox(nullptr),
740     m_pStartNode(&rStartNode),
741     m_pTableFormat(pTableFormat),
742     m_nFndPos(NOTFOUND)
743 {
744     StartListening(pTableFormat->GetNotifier());
745 }
746 
747 SwXCell::~SwXCell()
748 {
749     SolarMutexGuard aGuard;
750     EndListeningAll();
751 }
752 
753 namespace
754 {
755     class theSwXCellUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXCellUnoTunnelId > {};
756 }
757 
758 const uno::Sequence< sal_Int8 > & SwXCell::getUnoTunnelId()
759 {
760     return theSwXCellUnoTunnelId::get().getSeq();
761 }
762 
763 sal_Int64 SAL_CALL SwXCell::getSomething( const uno::Sequence< sal_Int8 >& rId )
764 {
765     if( isUnoTunnelId<SwXCell>(rId) )
766     {
767         return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) );
768     }
769     else
770         return SwXText::getSomething(rId);
771 }
772 
773 uno::Sequence< uno::Type > SAL_CALL SwXCell::getTypes(  )
774 {
775     return comphelper::concatSequences(
776             SwXCellBaseClass::getTypes(),
777             SwXText::getTypes()
778         );
779 }
780 
781 uno::Sequence< sal_Int8 > SAL_CALL SwXCell::getImplementationId(  )
782 {
783     return css::uno::Sequence<sal_Int8>();
784 }
785 
786 void SAL_CALL SwXCell::acquire(  ) throw()
787 {
788     SwXCellBaseClass::acquire();
789 }
790 
791 void SAL_CALL SwXCell::release(  ) throw()
792 {
793     SolarMutexGuard aGuard;
794 
795     SwXCellBaseClass::release();
796 }
797 
798 uno::Any SAL_CALL SwXCell::queryInterface( const uno::Type& aType )
799 {
800     uno::Any aRet = SwXCellBaseClass::queryInterface(aType);
801     if(aRet.getValueType() == cppu::UnoType<void>::get())
802         aRet = SwXText::queryInterface(aType);
803     return aRet;
804 }
805 
806 const SwStartNode *SwXCell::GetStartNode() const
807 {
808     const SwStartNode* pSttNd = nullptr;
809 
810     if( m_pStartNode || IsValid() )
811         pSttNd = m_pStartNode ? m_pStartNode : m_pBox->GetSttNd();
812 
813     return pSttNd;
814 }
815 
816 uno::Reference< text::XTextCursor >
817 SwXCell::CreateCursor()
818 {
819     return createTextCursor();
820 }
821 
822 bool SwXCell::IsValid() const
823 {
824     // FIXME: this is now a const method, to make SwXText::IsValid invisible
825     // but the const_cast here are still ridiculous. TODO: find a better way.
826     SwFrameFormat* pTableFormat = m_pBox ? GetFrameFormat() : nullptr;
827     if(!pTableFormat)
828     {
829         const_cast<SwXCell*>(this)->m_pBox = nullptr;
830     }
831     else
832     {
833         SwTable* pTable = SwTable::FindTable( pTableFormat );
834         SwTableBox const*const pFoundBox =
835             const_cast<SwXCell*>(this)->FindBox(pTable, m_pBox);
836         if (!pFoundBox)
837         {
838             const_cast<SwXCell*>(this)->m_pBox = nullptr;
839         }
840     }
841     return nullptr != m_pBox;
842 }
843 
844 OUString SwXCell::getFormula()
845 {
846     SolarMutexGuard aGuard;
847     if(!IsValid())
848         return OUString();
849     SwTableBoxFormula aFormula( m_pBox->GetFrameFormat()->GetTableBoxFormula() );
850     SwTable* pTable = SwTable::FindTable( GetFrameFormat() );
851     aFormula.PtrToBoxNm( pTable );
852     return aFormula.GetFormula();
853 }
854 
855 ///@see sw_setValue (TODO: seems to be copy and paste programming here)
856 void SwXCell::setFormula(const OUString& rFormula)
857 {
858     SolarMutexGuard aGuard;
859     if(!IsValid())
860         return;
861     // first this text (maybe) needs to be deleted
862     sal_uInt32 nNdPos = m_pBox->IsValidNumTextNd();
863     if(USHRT_MAX == nNdPos)
864         sw_setString( *this, OUString(), true );
865     OUString sFormula(comphelper::string::stripStart(rFormula, ' '));
866     if( !sFormula.isEmpty() && '=' == sFormula[0] )
867                 sFormula = sFormula.copy( 1 );
868     SwTableBoxFormula aFormula( sFormula );
869     SwDoc* pMyDoc = GetDoc();
870     UnoActionContext aAction(pMyDoc);
871     SfxItemSet aSet(pMyDoc->GetAttrPool(), svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_FORMULA>{});
872     const SfxPoolItem* pItem;
873     SwFrameFormat* pBoxFormat = m_pBox->GetFrameFormat();
874     if(SfxItemState::SET != pBoxFormat->GetAttrSet().GetItemState(RES_BOXATR_FORMAT, true, &pItem)
875         ||  pMyDoc->GetNumberFormatter()->IsTextFormat(static_cast<const SwTableBoxNumFormat*>(pItem)->GetValue()))
876     {
877         aSet.Put(SwTableBoxNumFormat(0));
878     }
879     aSet.Put(aFormula);
880     GetDoc()->SetTableBoxFormulaAttrs( *m_pBox, aSet );
881     // update table
882     SwTableFormulaUpdate aTableUpdate( SwTable::FindTable( GetFrameFormat() ));
883     pMyDoc->getIDocumentFieldsAccess().UpdateTableFields( &aTableUpdate );
884 }
885 
886 double SwXCell::getValue()
887 {
888     SolarMutexGuard aGuard;
889     // #i112652# a table cell may contain NaN as a value, do not filter that
890     double fRet;
891     if(IsValid() && !getString().isEmpty())
892         fRet = m_pBox->GetFrameFormat()->GetTableBoxValue().GetValue();
893     else
894         ::rtl::math::setNan( &fRet );
895     return fRet;
896 }
897 
898 void SwXCell::setValue(double rValue)
899 {
900     SolarMutexGuard aGuard;
901     sw_setValue( *this, rValue );
902 }
903 
904 table::CellContentType SwXCell::getType()
905 {
906     SolarMutexGuard aGuard;
907 
908     table::CellContentType nRes = table::CellContentType_EMPTY;
909     sal_uInt32 nNdPos = m_pBox->IsFormulaOrValueBox();
910     switch (nNdPos)
911     {
912         case 0 :                    nRes = table::CellContentType_TEXT; break;
913         case USHRT_MAX :            nRes = table::CellContentType_EMPTY; break;
914         case RES_BOXATR_VALUE :     nRes = table::CellContentType_VALUE; break;
915         case RES_BOXATR_FORMULA :   nRes = table::CellContentType_FORMULA; break;
916         default :
917             OSL_FAIL( "unexpected case" );
918     }
919     return  nRes;
920 }
921 
922 void SwXCell::setString(const OUString& aString)
923 {
924     SolarMutexGuard aGuard;
925     sw_setString( *this, aString );
926 }
927 
928 sal_Int32 SwXCell::getError()
929 {
930     SolarMutexGuard aGuard;
931     OUString sContent = getString();
932     return sal_Int32(sContent == SwViewShell::GetShellRes()->aCalc_Error);
933 }
934 
935 uno::Reference<text::XTextCursor> SwXCell::createTextCursor()
936 {
937     SolarMutexGuard aGuard;
938     if(!m_pStartNode && !IsValid())
939         throw uno::RuntimeException();
940     const SwStartNode* pSttNd = m_pStartNode ? m_pStartNode : m_pBox->GetSttNd();
941     SwPosition aPos(*pSttNd);
942     SwXTextCursor* const pXCursor =
943         new SwXTextCursor(*GetDoc(), this, CursorType::TableText, aPos);
944     auto& rUnoCursor(pXCursor->GetCursor());
945     rUnoCursor.Move(fnMoveForward, GoInNode);
946     return static_cast<text::XWordCursor*>(pXCursor);
947 }
948 
949 uno::Reference<text::XTextCursor> SwXCell::createTextCursorByRange(const uno::Reference< text::XTextRange > & xTextPosition)
950 {
951     SolarMutexGuard aGuard;
952     SwUnoInternalPaM aPam(*GetDoc());
953     if((!m_pStartNode && !IsValid()) || !::sw::XTextRangeToSwPaM(aPam, xTextPosition))
954         throw uno::RuntimeException();
955     const SwStartNode* pSttNd = m_pStartNode ? m_pStartNode : m_pBox->GetSttNd();
956     // skip sections
957     SwStartNode* p1 = aPam.GetNode().StartOfSectionNode();
958     while(p1->IsSectionNode())
959         p1 = p1->StartOfSectionNode();
960     if( p1 != pSttNd )
961         return nullptr;
962     return static_cast<text::XWordCursor*>(
963         new SwXTextCursor(*GetDoc(), this, CursorType::TableText,
964         *aPam.GetPoint(), aPam.GetMark()));
965 }
966 
967 uno::Reference< beans::XPropertySetInfo >  SwXCell::getPropertySetInfo()
968 {
969     static uno::Reference< beans::XPropertySetInfo >  xRef = m_pPropSet->getPropertySetInfo();
970     return xRef;
971 }
972 
973 // If the current property matches the previous parent's property (i.e. no reason for it to be set),
974 // then it may be a ::DEFAULT value, even if it is marked as ::SET
975 static bool lcl_mayBeDefault( const sal_uInt16 nWhich, sal_uInt8 nMemberId,
976                        const SfxPoolItem* pPrevItem, const SfxPoolItem& rCurrItem,
977                        const bool bDirect )
978 {
979     bool bMayBeDefault = false;
980     // These are the paragraph/character pairs that I found running unit tests.
981     // UNFORTUNATELY there is no way to see if a property has multiple members.
982     // Since valid members can be nMemberId == 0, we can't do something like "if (nMemberId & ~CONVERT_TWIPS) != 0"
983     // Perhaps the full list can be found in editeng/memberids.h???
984     switch ( nWhich )
985     {
986         case RES_BOX:
987         case RES_UL_SPACE:
988         case RES_LR_SPACE:
989         case RES_CHRATR_ESCAPEMENT:
990         case RES_CHRATR_FONT:
991         case RES_CHRATR_CJK_FONT:
992         case RES_CHRATR_CTL_FONT:
993         case RES_CHRATR_FONTSIZE:
994         case RES_CHRATR_CJK_FONTSIZE:
995         case RES_CHRATR_CTL_FONTSIZE:
996         case RES_CHRATR_WEIGHT:
997         case RES_CHRATR_CJK_WEIGHT:
998         case RES_CHRATR_CTL_WEIGHT:
999         case RES_CHRATR_LANGUAGE:
1000         case RES_CHRATR_CJK_LANGUAGE:
1001         case RES_CHRATR_CTL_LANGUAGE:
1002         case RES_CHRATR_POSTURE:
1003         case RES_CHRATR_CJK_POSTURE:
1004         case RES_CHRATR_CTL_POSTURE:
1005         case RES_PARATR_ADJUST:
1006         {
1007             // These properties are paired up, containing multiple properties in one nWhich.
1008             // If one is ::SET, they all report ::SET, even if only initialized with the default value.
1009             // Assume created automatically by another MemberId.
1010             bMayBeDefault = true;
1011             if ( pPrevItem )
1012             {
1013                 uno::Any aPrev;
1014                 uno::Any aCurr;
1015                 (*pPrevItem).QueryValue(aPrev, nMemberId);
1016                 rCurrItem.QueryValue(aCurr, nMemberId);
1017                 // If different, it overrides a parent value, so can't be considered a default.
1018                 bMayBeDefault = aPrev == aCurr;
1019             }
1020             break;
1021         }
1022         default:
1023         {
1024             // Since DocDefaults are copied into root-level stylesheets (tdf#103961),
1025             // identify the duplicated properties as DocDefault values.
1026             // Assume any style information could have been inherited/copied.
1027             if ( !bDirect )
1028                 bMayBeDefault = !pPrevItem || *pPrevItem == rCurrItem;
1029         }
1030     }
1031     return bMayBeDefault;
1032 }
1033 
1034 void SwXCell::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue)
1035 {
1036     SolarMutexGuard aGuard;
1037     if(!IsValid())
1038         return;
1039     // Hack to support hidden property to transfer textDirection
1040     if(rPropertyName == "FRMDirection")
1041     {
1042         SvxFrameDirectionItem aItem(SvxFrameDirection::Environment, RES_FRAMEDIR);
1043         aItem.PutValue(aValue, 0);
1044         m_pBox->GetFrameFormat()->SetFormatAttr(aItem);
1045     }
1046     else if(rPropertyName == "TableRedlineParams")
1047     {
1048         // Get the table row properties
1049         uno::Sequence<beans::PropertyValue> tableCellProperties = aValue.get< uno::Sequence< beans::PropertyValue > >();
1050         comphelper::SequenceAsHashMap aPropMap(tableCellProperties);
1051         OUString sRedlineType;
1052         if(!(aPropMap.getValue("RedlineType") >>= sRedlineType))
1053             throw beans::UnknownPropertyException("No redline type property: ", static_cast<cppu::OWeakObject*>(this));
1054 
1055         // Create a 'Table Cell Redline' object
1056         SwUnoCursorHelper::makeTableCellRedline(*m_pBox, sRedlineType, tableCellProperties);
1057 
1058 
1059     }
1060     else
1061     {
1062         auto pEntry(m_pPropSet->getPropertyMap().getByName(rPropertyName));
1063         if ( !pEntry )
1064         {
1065             // not a table property: if it is a paragraph/character property, consider applying it to the underlying text.
1066             const SfxItemPropertySet& rParaPropSet = *aSwMapProvider.GetPropertySet(PROPERTY_MAP_PARAGRAPH);
1067             pEntry = rParaPropSet.getPropertyMap().getByName(rPropertyName);
1068 
1069             if ( pEntry )
1070             {
1071                 SwNodeIndex aIdx( *GetStartNode(), 1 );
1072                 const SwNode* pEndNd = aIdx.GetNode().EndOfSectionNode();
1073                 while ( &aIdx.GetNode() != pEndNd )
1074                 {
1075                     const SwTextNode* pNd = aIdx.GetNode().GetTextNode();
1076                     ++aIdx;
1077                     if ( !pNd )
1078                         continue;
1079 
1080                     const SfxPoolItem* pPrevItem = nullptr;
1081                     const SfxPoolItem* pCurrItem = nullptr;
1082                     // Table-styles don't override direct formatting
1083                     if ( pNd->HasSwAttrSet() && SfxItemState::SET == pNd->GetSwAttrSet().GetItemState(pEntry->nWID, false, &pCurrItem) )
1084                     {
1085                         // Some WIDs have several MIDs, so perhaps ::SET refers to another MID and this property was copied from parents?
1086                         if ( lcl_mayBeDefault(pEntry->nWID, pEntry->nMemberId, pPrevItem, *pCurrItem, /*bDirect=*/true) )
1087                             pPrevItem = pCurrItem;
1088                         else
1089                             continue; //don't override direct formatting
1090                     }
1091 
1092                     bool bSet = false;
1093                     SwFormat* pFormatColl = pNd->GetFormatColl();
1094                     // Manually walk through the parent properties in order to avoid the default properties.
1095                     // Table-styles don't override paragraph-style formatting.
1096                     //    TODO: ?except for fontsize/justification if compat:overrideTableStyleFontSizeAndJustification?
1097                     while ( pFormatColl )
1098                     {
1099                         if ( SfxItemState::SET == pFormatColl->GetItemState(pEntry->nWID, /*bSrchInParent=*/false, &pCurrItem) )
1100                         {
1101                             if ( lcl_mayBeDefault(pEntry->nWID, pEntry->nMemberId, pPrevItem, *pCurrItem, false) )
1102                             {
1103                                 // if the property matches DocDefaults, then table-style needs to override it
1104                                 pPrevItem = pFormatColl->IsDefault() ? nullptr : pCurrItem;
1105                             }
1106                             else
1107                             {
1108                                 bSet = true; //don't override style formatting
1109                                 break;
1110                             }
1111                         }
1112                         pFormatColl = pFormatColl->DerivedFrom();
1113                     }
1114                     if ( bSet )
1115                         continue;
1116 
1117                     // Check if previous ::SET came from the pool defaults.
1118                     if ( pPrevItem && pNd->GetSwAttrSet().GetPool() )
1119                     {
1120                         pCurrItem = &pNd->GetSwAttrSet().GetPool()->GetDefaultItem(pEntry->nWID);
1121                         if ( !lcl_mayBeDefault(pEntry->nWID, pEntry->nMemberId, pPrevItem, *pCurrItem, false) )
1122                             continue;
1123                     }
1124 
1125                     // Apply table-style property
1126                     // point and mark selecting the whole paragraph
1127                     SwPaM aPaM(*pNd, 0, *pNd, pNd->GetText().getLength());
1128                     // for isCHRATR: change the base/auto SwAttr property, but don't remove the DIRECT hints
1129                     SwUnoCursorHelper::SetPropertyValue(aPaM, rParaPropSet, rPropertyName, aValue, SetAttrMode::DONTREPLACE);
1130                 }
1131                 return;
1132             }
1133         }
1134 
1135         if(!pEntry)
1136             throw beans::UnknownPropertyException(rPropertyName, static_cast<cppu::OWeakObject*>(this));
1137         if(pEntry->nWID != FN_UNO_CELL_ROW_SPAN)
1138         {
1139             SwFrameFormat* pBoxFormat = m_pBox->ClaimFrameFormat();
1140             SwAttrSet aSet(pBoxFormat->GetAttrSet());
1141             m_pPropSet->setPropertyValue(rPropertyName, aValue, aSet);
1142             pBoxFormat->GetDoc()->SetAttr(aSet, *pBoxFormat);
1143         }
1144         else if(aValue.isExtractableTo(cppu::UnoType<sal_Int32>::get()))
1145             m_pBox->setRowSpan(aValue.get<sal_Int32>());
1146     }
1147 }
1148 
1149 uno::Any SwXCell::getPropertyValue(const OUString& rPropertyName)
1150 {
1151     SolarMutexGuard aGuard;
1152     if(!IsValid())
1153         return uno::Any();
1154     auto pEntry(m_pPropSet->getPropertyMap().getByName(rPropertyName));
1155     if(!pEntry)
1156         throw beans::UnknownPropertyException(rPropertyName, static_cast<cppu::OWeakObject*>(this));
1157     switch(pEntry->nWID)
1158     {
1159         case FN_UNO_CELL_ROW_SPAN:
1160             return uno::makeAny(m_pBox->getRowSpan());
1161         break;
1162         case FN_UNO_TEXT_SECTION:
1163         {
1164             SwFrameFormat* pTableFormat = GetFrameFormat();
1165             SwTable* pTable = SwTable::FindTable(pTableFormat);
1166             SwTableNode* pTableNode = pTable->GetTableNode();
1167             SwSectionNode* pSectionNode = pTableNode->FindSectionNode();
1168             if(!pSectionNode)
1169                 return uno::Any();
1170             SwSection& rSect = pSectionNode->GetSection();
1171             return uno::makeAny(SwXTextSections::GetObject(*rSect.GetFormat()));
1172         }
1173         break;
1174         case FN_UNO_CELL_NAME:
1175             return uno::makeAny(m_pBox->GetName());
1176         break;
1177         case FN_UNO_REDLINE_NODE_START:
1178         case FN_UNO_REDLINE_NODE_END:
1179         {
1180             //redline can only be returned if it's a living object
1181             return SwXText::getPropertyValue(rPropertyName);
1182         }
1183         break;
1184         case FN_UNO_PARENT_TEXT:
1185         {
1186             if (!m_xParentText.is())
1187             {
1188                 const SwStartNode* pSttNd = m_pBox->GetSttNd();
1189                 if (!pSttNd)
1190                     return uno::Any();
1191 
1192                 const SwTableNode* pTableNode = pSttNd->FindTableNode();
1193                 if (!pTableNode)
1194                     return uno::Any();
1195 
1196                 SwPosition aPos(*pTableNode);
1197                 SwDoc* pDoc = aPos.GetDoc();
1198                 if (!pDoc)
1199                     return uno::Any();
1200 
1201                 m_xParentText = sw::CreateParentXText(*pDoc, aPos);
1202             }
1203 
1204             return uno::makeAny(m_xParentText);
1205         }
1206         break;
1207         default:
1208         {
1209             const SwAttrSet& rSet = m_pBox->GetFrameFormat()->GetAttrSet();
1210             uno::Any aResult;
1211             m_pPropSet->getPropertyValue(rPropertyName, rSet, aResult);
1212             return aResult;
1213         }
1214     }
1215 }
1216 
1217 void SwXCell::addPropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
1218     { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
1219 
1220 void SwXCell::removePropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
1221     { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
1222 
1223 void SwXCell::addVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
1224     { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
1225 
1226 void SwXCell::removeVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
1227     { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
1228 
1229 uno::Reference<container::XEnumeration> SwXCell::createEnumeration()
1230 {
1231     SolarMutexGuard aGuard;
1232     if(!IsValid())
1233         return uno::Reference<container::XEnumeration>();
1234     const SwStartNode* pSttNd = m_pBox->GetSttNd();
1235     SwPosition aPos(*pSttNd);
1236     auto pUnoCursor(GetDoc()->CreateUnoCursor(aPos));
1237     pUnoCursor->Move(fnMoveForward, GoInNode);
1238     // remember table and start node for later travelling
1239     // (used in export of tables in tables)
1240     SwTable const*const pTable(&pSttNd->FindTableNode()->GetTable());
1241     return SwXParagraphEnumeration::Create(this, pUnoCursor, CursorType::TableText, pSttNd, pTable);
1242 }
1243 
1244 uno::Type SAL_CALL SwXCell::getElementType()
1245 {
1246     return cppu::UnoType<text::XTextRange>::get();
1247 }
1248 
1249 sal_Bool SwXCell::hasElements()
1250 {
1251     return true;
1252 }
1253 
1254 void SwXCell::Notify(const SfxHint& rHint)
1255 {
1256     if(rHint.GetId() == SfxHintId::Dying)
1257     {
1258         m_pTableFormat = nullptr;
1259     }
1260     else if(auto pFindHint = dynamic_cast<const FindUnoInstanceHint<SwTableBox, SwXCell>*>(&rHint))
1261     {
1262         if(!pFindHint->m_pResult && pFindHint->m_pCore == GetTableBox())
1263             pFindHint->m_pResult = this;
1264     }
1265 }
1266 
1267 SwXCell* SwXCell::CreateXCell(SwFrameFormat* pTableFormat, SwTableBox* pBox, SwTable *pTable )
1268 {
1269     if(!pTableFormat || !pBox)
1270         return nullptr;
1271     if(!pTable)
1272         pTable = SwTable::FindTable(pTableFormat);
1273     SwTableSortBoxes::const_iterator it = pTable->GetTabSortBoxes().find(pBox);
1274     if(it == pTable->GetTabSortBoxes().end())
1275         return nullptr;
1276     size_t const nPos = it - pTable->GetTabSortBoxes().begin();
1277     FindUnoInstanceHint<SwTableBox, SwXCell> aHint{pBox};
1278     pTableFormat->GetNotifier().Broadcast(aHint);
1279     return aHint.m_pResult ? aHint.m_pResult : new SwXCell(pTableFormat, pBox, nPos);
1280 }
1281 
1282 /** search if a box exists in a table
1283  *
1284  * @param pTable the table to search in
1285  * @param pBox2 box model to find
1286  * @return the box if existent in pTable, 0 (!!!) if not found
1287  */
1288 SwTableBox* SwXCell::FindBox(SwTable* pTable, SwTableBox* pBox2)
1289 {
1290     // check if nFndPos happens to point to the right table box
1291     if( m_nFndPos < pTable->GetTabSortBoxes().size() &&
1292         pBox2 == pTable->GetTabSortBoxes()[ m_nFndPos ] )
1293         return pBox2;
1294 
1295     // if not, seek the entry (and return, if successful)
1296     SwTableSortBoxes::const_iterator it = pTable->GetTabSortBoxes().find( pBox2 );
1297     if( it != pTable->GetTabSortBoxes().end() )
1298     {
1299         m_nFndPos = it - pTable->GetTabSortBoxes().begin();
1300         return pBox2;
1301     }
1302 
1303     // box not found: reset nFndPos pointer
1304     m_nFndPos = NOTFOUND;
1305     return nullptr;
1306 }
1307 
1308 double SwXCell::GetForcedNumericalValue() const
1309 {
1310     if(table::CellContentType_TEXT != const_cast<SwXCell*>(this)->getType())
1311         return getValue();
1312     // now we'll try to get a useful numerical value
1313     // from the text in the cell...
1314     sal_uInt32 nFIndex;
1315     SvNumberFormatter* pNumFormatter(const_cast<SvNumberFormatter*>(GetDoc()->GetNumberFormatter()));
1316     // look for SwTableBoxNumFormat value in parents as well
1317     const SfxPoolItem* pItem;
1318     auto pBoxFormat(GetTableBox()->GetFrameFormat());
1319     SfxItemState eState = pBoxFormat->GetAttrSet().GetItemState(RES_BOXATR_FORMAT, true, &pItem);
1320 
1321     if (eState == SfxItemState::SET)
1322     {
1323         // please note that the language of the numberformat
1324         // is implicitly coded into the below value as well
1325         nFIndex = static_cast<const SwTableBoxNumFormat*>(pItem)->GetValue();
1326 
1327         // since the current value indicates a text format but the call
1328         // to 'IsNumberFormat' below won't work for text formats
1329         // we need to get rid of the part that indicates the text format.
1330         // According to ER this can be done like this:
1331         nFIndex -= (nFIndex % SV_COUNTRY_LANGUAGE_OFFSET);
1332     }
1333     else
1334     {
1335         // system language is probably not the best possible choice
1336         // but since we have to guess anyway (because the language of at
1337         // the text is NOT the one used for the number format!)
1338         // it is at least conform to what is used in
1339         // SwTableShell::Execute when
1340         // SID_ATTR_NUMBERFORMAT_VALUE is set...
1341         LanguageType eLang = LANGUAGE_SYSTEM;
1342         nFIndex = pNumFormatter->GetStandardIndex( eLang );
1343     }
1344     double fTmp;
1345     if (!const_cast<SwDoc*>(GetDoc())->IsNumberFormat(const_cast<SwXCell*>(this)->getString(), nFIndex, fTmp))
1346         ::rtl::math::setNan(&fTmp);
1347     return fTmp;
1348 }
1349 
1350 uno::Any SwXCell::GetAny() const
1351 {
1352     if(!m_pBox)
1353         throw uno::RuntimeException();
1354     // check if table box value item is set
1355     auto pBoxFormat(m_pBox->GetFrameFormat());
1356     const bool bIsNum = pBoxFormat->GetItemState(RES_BOXATR_VALUE, false) == SfxItemState::SET;
1357     return bIsNum ? uno::makeAny(getValue()) : uno::makeAny(const_cast<SwXCell*>(this)->getString());
1358 }
1359 
1360 OUString SwXCell::getImplementationName()
1361     { return "SwXCell"; }
1362 
1363 sal_Bool SwXCell::supportsService(const OUString& rServiceName)
1364     { return cppu::supportsService(this, rServiceName); }
1365 
1366 uno::Sequence< OUString > SwXCell::getSupportedServiceNames()
1367     { return {"com.sun.star.text.CellProperties"}; }
1368 
1369 OUString SwXTextTableRow::getImplementationName()
1370     { return "SwXTextTableRow"; }
1371 
1372 sal_Bool SwXTextTableRow::supportsService(const OUString& rServiceName)
1373     { return cppu::supportsService(this, rServiceName); }
1374 
1375 uno::Sequence< OUString > SwXTextTableRow::getSupportedServiceNames()
1376     { return {"com.sun.star.text.TextTableRow"}; }
1377 
1378 
1379 SwXTextTableRow::SwXTextTableRow(SwFrameFormat* pFormat, SwTableLine* pLn) :
1380     m_pFormat(pFormat),
1381     pLine(pLn),
1382     m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_TABLE_ROW))
1383 {
1384     StartListening(m_pFormat->GetNotifier());
1385 }
1386 
1387 SwXTextTableRow::~SwXTextTableRow()
1388 {
1389     SolarMutexGuard aGuard;
1390     EndListeningAll();
1391 }
1392 
1393 uno::Reference< beans::XPropertySetInfo > SwXTextTableRow::getPropertySetInfo()
1394 {
1395     static uno::Reference<beans::XPropertySetInfo> xRef = m_pPropSet->getPropertySetInfo();
1396     return xRef;
1397 }
1398 
1399 void SwXTextTableRow::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue)
1400 {
1401     SolarMutexGuard aGuard;
1402     SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
1403     SwTable* pTable = SwTable::FindTable( pFormat );
1404     SwTableLine* pLn = SwXTextTableRow::FindLine(pTable, pLine);
1405     if(pLn)
1406     {
1407         // Check for a specific property
1408         if  ( rPropertyName == "TableRedlineParams" )
1409         {
1410             // Get the table row properties
1411             uno::Sequence< beans::PropertyValue > tableRowProperties = aValue.get< uno::Sequence< beans::PropertyValue > >();
1412             comphelper::SequenceAsHashMap aPropMap( tableRowProperties );
1413             OUString sRedlineType;
1414             if( !(aPropMap.getValue("RedlineType") >>= sRedlineType) )
1415             {
1416                 throw beans::UnknownPropertyException("No redline type property: ", static_cast < cppu::OWeakObject * > ( this ) );
1417             }
1418 
1419             // Create a 'Table Row Redline' object
1420             SwUnoCursorHelper::makeTableRowRedline( *pLn, sRedlineType, tableRowProperties);
1421 
1422         }
1423         else
1424         {
1425             const SfxItemPropertySimpleEntry* pEntry =
1426                 m_pPropSet->getPropertyMap().getByName(rPropertyName);
1427             SwDoc* pDoc = pFormat->GetDoc();
1428             if (!pEntry)
1429                 throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
1430             if ( pEntry->nFlags & beans::PropertyAttribute::READONLY)
1431                 throw beans::PropertyVetoException("Property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
1432 
1433             switch(pEntry->nWID)
1434             {
1435                 case FN_UNO_ROW_HEIGHT:
1436                 case FN_UNO_ROW_AUTO_HEIGHT:
1437                 {
1438                     SwFormatFrameSize aFrameSize(pLn->GetFrameFormat()->GetFrameSize());
1439                     if(FN_UNO_ROW_AUTO_HEIGHT== pEntry->nWID)
1440                     {
1441                         bool bSet = *o3tl::doAccess<bool>(aValue);
1442                         aFrameSize.SetHeightSizeType(bSet ? SwFrameSize::Variable : SwFrameSize::Fixed);
1443                     }
1444                     else
1445                     {
1446                         sal_Int32 nHeight = 0;
1447                         aValue >>= nHeight;
1448                         Size aSz(aFrameSize.GetSize());
1449                         aSz.setHeight( convertMm100ToTwip(nHeight) );
1450                         aFrameSize.SetSize(aSz);
1451                     }
1452                     pDoc->SetAttr(aFrameSize, *pLn->ClaimFrameFormat());
1453                 }
1454                 break;
1455 
1456                 case FN_UNO_TABLE_COLUMN_SEPARATORS:
1457                 {
1458                     UnoActionContext aContext(pDoc);
1459                     SwTable* pTable2 = SwTable::FindTable( pFormat );
1460                     lcl_SetTableSeparators(aValue, pTable2, pLine->GetTabBoxes()[0], true, pDoc);
1461                 }
1462                 break;
1463 
1464                 default:
1465                 {
1466                     SwFrameFormat* pLnFormat = pLn->ClaimFrameFormat();
1467                     SwAttrSet aSet(pLnFormat->GetAttrSet());
1468                     m_pPropSet->setPropertyValue(*pEntry, aValue, aSet);
1469                     pDoc->SetAttr(aSet, *pLnFormat);
1470                 }
1471             }
1472         }
1473     }
1474 }
1475 
1476 uno::Any SwXTextTableRow::getPropertyValue(const OUString& rPropertyName)
1477 {
1478     SolarMutexGuard aGuard;
1479     uno::Any aRet;
1480     SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
1481     SwTable* pTable = SwTable::FindTable( pFormat );
1482     SwTableLine* pLn = SwXTextTableRow::FindLine(pTable, pLine);
1483     if(pLn)
1484     {
1485         const SfxItemPropertySimpleEntry* pEntry =
1486                                 m_pPropSet->getPropertyMap().getByName(rPropertyName);
1487         if (!pEntry)
1488             throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
1489 
1490         switch(pEntry->nWID)
1491         {
1492             case FN_UNO_ROW_HEIGHT:
1493             case FN_UNO_ROW_AUTO_HEIGHT:
1494             {
1495                 const SwFormatFrameSize& rSize = pLn->GetFrameFormat()->GetFrameSize();
1496                 if(FN_UNO_ROW_AUTO_HEIGHT== pEntry->nWID)
1497                 {
1498                     aRet <<= SwFrameSize::Variable == rSize.GetHeightSizeType();
1499                 }
1500                 else
1501                     aRet <<= static_cast<sal_Int32>(convertTwipToMm100(rSize.GetSize().Height()));
1502             }
1503             break;
1504 
1505             case FN_UNO_TABLE_COLUMN_SEPARATORS:
1506             {
1507                 lcl_GetTableSeparators(aRet, pTable, pLine->GetTabBoxes()[0], true);
1508             }
1509             break;
1510 
1511             default:
1512             {
1513                 const SwAttrSet& rSet = pLn->GetFrameFormat()->GetAttrSet();
1514                 m_pPropSet->getPropertyValue(*pEntry, rSet, aRet);
1515             }
1516         }
1517     }
1518     return aRet;
1519 }
1520 
1521 void SwXTextTableRow::addPropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
1522     { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
1523 
1524 void SwXTextTableRow::removePropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
1525     { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
1526 
1527 void SwXTextTableRow::addVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
1528     { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
1529 
1530 void SwXTextTableRow::removeVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
1531     { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
1532 
1533 void SwXTextTableRow::Notify(const SfxHint& rHint)
1534 {
1535     if(rHint.GetId() == SfxHintId::Dying)
1536     {
1537         m_pFormat = nullptr;
1538     } else if(auto pFindHint = dynamic_cast<const FindUnoInstanceHint<SwTableLine, SwXTextTableRow>*>(&rHint))
1539     {
1540         if(!pFindHint->m_pCore && pFindHint->m_pCore == pLine)
1541             pFindHint->m_pResult = this;
1542     }
1543 }
1544 
1545 SwTableLine* SwXTextTableRow::FindLine(SwTable* pTable, SwTableLine const * pLine)
1546 {
1547     for(const auto& pCurrentLine : pTable->GetTabLines())
1548         if(pCurrentLine == pLine)
1549             return pCurrentLine;
1550     return nullptr;
1551 }
1552 
1553 // SwXTextTableCursor
1554 
1555 OUString SwXTextTableCursor::getImplementationName()
1556     { return "SwXTextTableCursor"; }
1557 
1558 sal_Bool SwXTextTableCursor::supportsService(const OUString& rServiceName)
1559     { return cppu::supportsService(this, rServiceName); }
1560 
1561 void SwXTextTableCursor::acquire() throw()
1562 {
1563     SwXTextTableCursor_Base::acquire();
1564 }
1565 
1566 void SwXTextTableCursor::release() throw()
1567 {
1568     SolarMutexGuard aGuard;
1569     SwXTextTableCursor_Base::release();
1570 }
1571 
1572 css::uno::Any SAL_CALL
1573 SwXTextTableCursor::queryInterface( const css::uno::Type& _rType )
1574 {
1575     css::uno::Any aReturn = SwXTextTableCursor_Base::queryInterface( _rType );
1576     if ( !aReturn.hasValue() )
1577         aReturn = OTextCursorHelper::queryInterface( _rType );
1578     return aReturn;
1579 }
1580 
1581 const SwPaM*        SwXTextTableCursor::GetPaM() const  { return &GetCursor(); }
1582 SwPaM*              SwXTextTableCursor::GetPaM()        { return &GetCursor(); }
1583 const SwDoc*        SwXTextTableCursor::GetDoc() const  { return GetFrameFormat()->GetDoc(); }
1584 SwDoc*              SwXTextTableCursor::GetDoc()        { return GetFrameFormat()->GetDoc(); }
1585 const SwUnoCursor&    SwXTextTableCursor::GetCursor() const { return *m_pUnoCursor; }
1586 SwUnoCursor&          SwXTextTableCursor::GetCursor()       { return *m_pUnoCursor; }
1587 
1588 uno::Sequence<OUString> SwXTextTableCursor::getSupportedServiceNames()
1589     { return {"com.sun.star.text.TextTableCursor"}; }
1590 
1591 SwXTextTableCursor::SwXTextTableCursor(SwFrameFormat* pFrameFormat, SwTableBox const* pBox)
1592     : m_pFrameFormat(pFrameFormat)
1593     , m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_TABLE_CURSOR))
1594 {
1595     StartListening(m_pFrameFormat->GetNotifier());
1596     SwDoc* pDoc = m_pFrameFormat->GetDoc();
1597     const SwStartNode* pSttNd = pBox->GetSttNd();
1598     SwPosition aPos(*pSttNd);
1599     m_pUnoCursor = pDoc->CreateUnoCursor(aPos, true);
1600     m_pUnoCursor->Move( fnMoveForward, GoInNode );
1601     SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(*m_pUnoCursor);
1602     rTableCursor.MakeBoxSels();
1603 }
1604 
1605 SwXTextTableCursor::SwXTextTableCursor(SwFrameFormat& rTableFormat, const SwTableCursor* pTableSelection)
1606     : m_pFrameFormat(&rTableFormat)
1607     , m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_TABLE_CURSOR))
1608 {
1609     StartListening(m_pFrameFormat->GetNotifier());
1610     m_pUnoCursor = pTableSelection->GetDoc()->CreateUnoCursor(*pTableSelection->GetPoint(), true);
1611     if(pTableSelection->HasMark())
1612     {
1613         m_pUnoCursor->SetMark();
1614         *m_pUnoCursor->GetMark() = *pTableSelection->GetMark();
1615     }
1616     const SwSelBoxes& rBoxes = pTableSelection->GetSelectedBoxes();
1617     SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(*m_pUnoCursor);
1618     for(auto pBox : rBoxes)
1619         rTableCursor.InsertBox(*pBox);
1620     rTableCursor.MakeBoxSels();
1621 }
1622 
1623 OUString SwXTextTableCursor::getRangeName()
1624 {
1625     SolarMutexGuard aGuard;
1626     SwUnoCursor& rUnoCursor = GetCursor();
1627     SwUnoTableCursor* pTableCursor = dynamic_cast<SwUnoTableCursor*>(&rUnoCursor);
1628     //!! see also SwChartDataSequence::getSourceRangeRepresentation
1629     if(!pTableCursor)
1630         return OUString();
1631     pTableCursor->MakeBoxSels();
1632     const SwStartNode* pNode = pTableCursor->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
1633     const SwTable* pTable = SwTable::FindTable(GetFrameFormat());
1634     const SwTableBox* pEndBox = pTable->GetTableBox(pNode->GetIndex());
1635     if(pTableCursor->HasMark())
1636     {
1637         pNode = pTableCursor->GetMark()->nNode.GetNode().FindTableBoxStartNode();
1638         const SwTableBox* pStartBox = pTable->GetTableBox(pNode->GetIndex());
1639         if(pEndBox != pStartBox)
1640         {
1641             // need to switch start and end?
1642             if(*pTableCursor->GetPoint() < *pTableCursor->GetMark())
1643                 std::swap(pStartBox, pEndBox);
1644             return pStartBox->GetName() + ":" + pEndBox->GetName();
1645         }
1646     }
1647     return pEndBox->GetName();
1648 }
1649 
1650 sal_Bool SwXTextTableCursor::gotoCellByName(const OUString& sCellName, sal_Bool bExpand)
1651 {
1652     SolarMutexGuard aGuard;
1653     SwUnoCursor& rUnoCursor = GetCursor();
1654     auto& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
1655     lcl_CursorSelect(rTableCursor, bExpand);
1656     return rTableCursor.GotoTableBox(sCellName);
1657 }
1658 
1659 sal_Bool SwXTextTableCursor::goLeft(sal_Int16 Count, sal_Bool bExpand)
1660 {
1661     SolarMutexGuard aGuard;
1662     SwUnoCursor& rUnoCursor = GetCursor();
1663     SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
1664     lcl_CursorSelect(rTableCursor, bExpand);
1665     return rTableCursor.Left(Count);
1666 }
1667 
1668 sal_Bool SwXTextTableCursor::goRight(sal_Int16 Count, sal_Bool bExpand)
1669 {
1670     SolarMutexGuard aGuard;
1671     SwUnoCursor& rUnoCursor = GetCursor();
1672     SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
1673     lcl_CursorSelect(rTableCursor, bExpand);
1674     return rTableCursor.Right(Count);
1675 }
1676 
1677 sal_Bool SwXTextTableCursor::goUp(sal_Int16 Count, sal_Bool bExpand)
1678 {
1679     SolarMutexGuard aGuard;
1680     SwUnoCursor& rUnoCursor = GetCursor();
1681     SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
1682     lcl_CursorSelect(rTableCursor, bExpand);
1683     return rTableCursor.UpDown(true, Count, nullptr, 0,
1684         *rUnoCursor.GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout());
1685 }
1686 
1687 sal_Bool SwXTextTableCursor::goDown(sal_Int16 Count, sal_Bool bExpand)
1688 {
1689     SolarMutexGuard aGuard;
1690     SwUnoCursor& rUnoCursor = GetCursor();
1691     SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
1692     lcl_CursorSelect(rTableCursor, bExpand);
1693     return rTableCursor.UpDown(false, Count, nullptr, 0,
1694         *rUnoCursor.GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout());
1695 }
1696 
1697 void SwXTextTableCursor::gotoStart(sal_Bool bExpand)
1698 {
1699     SolarMutexGuard aGuard;
1700     SwUnoCursor& rUnoCursor = GetCursor();
1701     SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
1702     lcl_CursorSelect(rTableCursor, bExpand);
1703     rTableCursor.MoveTable(GotoCurrTable, fnTableStart);
1704 }
1705 
1706 void SwXTextTableCursor::gotoEnd(sal_Bool bExpand)
1707 {
1708     SolarMutexGuard aGuard;
1709     SwUnoCursor& rUnoCursor = GetCursor();
1710     SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
1711     lcl_CursorSelect(rTableCursor, bExpand);
1712     rTableCursor.MoveTable(GotoCurrTable, fnTableEnd);
1713 }
1714 
1715 sal_Bool SwXTextTableCursor::mergeRange()
1716 {
1717     SolarMutexGuard aGuard;
1718     SwUnoCursor& rUnoCursor = GetCursor();
1719 
1720     SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
1721     {
1722         // HACK: remove pending actions for selecting old style tables
1723         UnoActionRemoveContext aRemoveContext(rTableCursor);
1724     }
1725     rTableCursor.MakeBoxSels();
1726     bool bResult;
1727     {
1728         UnoActionContext aContext(rUnoCursor.GetDoc());
1729         bResult = TableMergeErr::Ok == rTableCursor.GetDoc()->MergeTable(rTableCursor);
1730     }
1731     if(bResult)
1732     {
1733         size_t nCount = rTableCursor.GetSelectedBoxesCount();
1734         while (nCount--)
1735             rTableCursor.DeleteBox(nCount);
1736     }
1737     rTableCursor.MakeBoxSels();
1738     return bResult;
1739 }
1740 
1741 sal_Bool SwXTextTableCursor::splitRange(sal_Int16 Count, sal_Bool Horizontal)
1742 {
1743     SolarMutexGuard aGuard;
1744     if (Count <= 0)
1745         throw uno::RuntimeException("Illegal first argument: needs to be > 0", static_cast<cppu::OWeakObject*>(this));
1746     SwUnoCursor& rUnoCursor = GetCursor();
1747     SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
1748     {
1749         // HACK: remove pending actions for selecting old style tables
1750         UnoActionRemoveContext aRemoveContext(rTableCursor);
1751     }
1752     rTableCursor.MakeBoxSels();
1753     bool bResult;
1754     {
1755         UnoActionContext aContext(rUnoCursor.GetDoc());
1756         bResult = rTableCursor.GetDoc()->SplitTable(rTableCursor.GetSelectedBoxes(), !Horizontal, Count);
1757     }
1758     rTableCursor.MakeBoxSels();
1759     return bResult;
1760 }
1761 
1762 uno::Reference< beans::XPropertySetInfo >  SwXTextTableCursor::getPropertySetInfo()
1763 {
1764     static uno::Reference< beans::XPropertySetInfo >  xRef = m_pPropSet->getPropertySetInfo();
1765     return xRef;
1766 }
1767 
1768 void SwXTextTableCursor::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue)
1769 {
1770     SolarMutexGuard aGuard;
1771     SwUnoCursor& rUnoCursor = GetCursor();
1772     auto pEntry(m_pPropSet->getPropertyMap().getByName(rPropertyName));
1773     if(!pEntry)
1774         throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast<cppu::OWeakObject*>(this));
1775     if(pEntry->nFlags & beans::PropertyAttribute::READONLY)
1776         throw beans::PropertyVetoException("Property is read-only: " + rPropertyName, static_cast<cppu::OWeakObject*>(this));
1777     {
1778         auto pSttNode = rUnoCursor.GetNode().StartOfSectionNode();
1779         const SwTableNode* pTableNode = pSttNode->FindTableNode();
1780         lcl_FormatTable(pTableNode->GetTable().GetFrameFormat());
1781     }
1782     auto& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
1783     rTableCursor.MakeBoxSels();
1784     SwDoc* pDoc = rUnoCursor.GetDoc();
1785     switch(pEntry->nWID)
1786     {
1787         case FN_UNO_TABLE_CELL_BACKGROUND:
1788         {
1789             std::shared_ptr<SfxPoolItem> aBrush(std::make_shared<SvxBrushItem>(RES_BACKGROUND));
1790             SwDoc::GetBoxAttr(rUnoCursor, aBrush);
1791             aBrush->PutValue(aValue, pEntry->nMemberId);
1792             pDoc->SetBoxAttr(rUnoCursor, *aBrush);
1793 
1794         }
1795         break;
1796         case RES_BOXATR_FORMAT:
1797         {
1798             SfxUInt32Item aNumberFormat(RES_BOXATR_FORMAT);
1799             aNumberFormat.PutValue(aValue, 0);
1800             pDoc->SetBoxAttr(rUnoCursor, aNumberFormat);
1801         }
1802         break;
1803         case FN_UNO_PARA_STYLE:
1804             SwUnoCursorHelper::SetTextFormatColl(aValue, rUnoCursor);
1805         break;
1806         default:
1807         {
1808             SfxItemSet aItemSet(pDoc->GetAttrPool(), {{pEntry->nWID, pEntry->nWID}});
1809             SwUnoCursorHelper::GetCursorAttr(rTableCursor.GetSelRing(),
1810                     aItemSet);
1811 
1812             if (!SwUnoCursorHelper::SetCursorPropertyValue(
1813                     *pEntry, aValue, rTableCursor.GetSelRing(), aItemSet))
1814             {
1815                 m_pPropSet->setPropertyValue(*pEntry, aValue, aItemSet);
1816             }
1817             SwUnoCursorHelper::SetCursorAttr(rTableCursor.GetSelRing(),
1818                     aItemSet, SetAttrMode::DEFAULT, true);
1819         }
1820     }
1821 }
1822 
1823 uno::Any SwXTextTableCursor::getPropertyValue(const OUString& rPropertyName)
1824 {
1825     SolarMutexGuard aGuard;
1826     SwUnoCursor& rUnoCursor = GetCursor();
1827     {
1828         auto pSttNode = rUnoCursor.GetNode().StartOfSectionNode();
1829         const SwTableNode* pTableNode = pSttNode->FindTableNode();
1830         lcl_FormatTable(pTableNode->GetTable().GetFrameFormat());
1831     }
1832     SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
1833     auto pEntry(m_pPropSet->getPropertyMap().getByName(rPropertyName));
1834     if(!pEntry)
1835         throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast<cppu::OWeakObject*>(this));
1836     rTableCursor.MakeBoxSels();
1837     uno::Any aResult;
1838     switch(pEntry->nWID)
1839     {
1840         case FN_UNO_TABLE_CELL_BACKGROUND:
1841         {
1842             std::shared_ptr<SfxPoolItem> aBrush(std::make_shared<SvxBrushItem>(RES_BACKGROUND));
1843             if (SwDoc::GetBoxAttr(rUnoCursor, aBrush))
1844                 aBrush->QueryValue(aResult, pEntry->nMemberId);
1845         }
1846         break;
1847         case RES_BOXATR_FORMAT:
1848             // TODO: GetAttr for table selections in a Doc is missing
1849             throw uno::RuntimeException("Unknown property: " + rPropertyName, static_cast<cppu::OWeakObject*>(this));
1850         break;
1851         case FN_UNO_PARA_STYLE:
1852         {
1853             auto pFormat(SwUnoCursorHelper::GetCurTextFormatColl(rUnoCursor, false));
1854             if(pFormat)
1855                 aResult <<= pFormat->GetName();
1856         }
1857         break;
1858         default:
1859         {
1860             SfxItemSet aSet(rTableCursor.GetDoc()->GetAttrPool(),
1861                 svl::Items<RES_CHRATR_BEGIN, RES_FRMATR_END-1,
1862                 RES_UNKNOWNATR_CONTAINER, RES_UNKNOWNATR_CONTAINER>{});
1863             SwUnoCursorHelper::GetCursorAttr(rTableCursor.GetSelRing(), aSet);
1864             m_pPropSet->getPropertyValue(*pEntry, aSet, aResult);
1865         }
1866     }
1867     return aResult;
1868 }
1869 
1870 void SwXTextTableCursor::addPropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
1871     { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
1872 
1873 void SwXTextTableCursor::removePropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
1874     { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
1875 
1876 void SwXTextTableCursor::addVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
1877     { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
1878 
1879 void SwXTextTableCursor::removeVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
1880     { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
1881 
1882 void SwXTextTableCursor::Notify( const SfxHint& rHint )
1883 {
1884     if(rHint.GetId() == SfxHintId::Dying)
1885         m_pFrameFormat = nullptr;
1886 }
1887 
1888 
1889 // SwXTextTable ===========================================================
1890 
1891 namespace {
1892 
1893 class SwTableProperties_Impl
1894 {
1895     SwUnoCursorHelper::SwAnyMapHelper aAnyMap;
1896 public:
1897     SwTableProperties_Impl();
1898 
1899     void SetProperty(sal_uInt16 nWhichId, sal_uInt16 nMemberId, const uno::Any& aVal);
1900     bool GetProperty(sal_uInt16 nWhichId, sal_uInt16 nMemberId, const uno::Any*& rpAny);
1901     template<typename Tpoolitem>
1902     inline void AddItemToSet(SfxItemSet& rSet, std::function<Tpoolitem()> aItemFactory, sal_uInt16 nWhich, std::initializer_list<sal_uInt16> vMember, bool bAddTwips = false);
1903 
1904     void ApplyTableAttr(const SwTable& rTable, SwDoc& rDoc);
1905 };
1906 
1907 }
1908 
1909 SwTableProperties_Impl::SwTableProperties_Impl()
1910     { }
1911 
1912 void SwTableProperties_Impl::SetProperty(sal_uInt16 nWhichId, sal_uInt16 nMemberId, const uno::Any& rVal)
1913     { aAnyMap.SetValue( nWhichId, nMemberId, rVal ); }
1914 
1915 bool SwTableProperties_Impl::GetProperty(sal_uInt16 nWhichId, sal_uInt16 nMemberId, const uno::Any*& rpAny )
1916     { return aAnyMap.FillValue( nWhichId, nMemberId, rpAny ); }
1917 
1918 template<typename Tpoolitem>
1919 void SwTableProperties_Impl::AddItemToSet(SfxItemSet& rSet, std::function<Tpoolitem()> aItemFactory, sal_uInt16 nWhich, std::initializer_list<sal_uInt16> vMember, bool bAddTwips)
1920 {
1921     std::vector< std::pair<sal_uInt16, const uno::Any* > > vMemberAndAny;
1922     for(sal_uInt16 nMember : vMember)
1923     {
1924         const uno::Any* pAny = nullptr;
1925         GetProperty(nWhich, nMember, pAny);
1926         if(pAny)
1927             vMemberAndAny.emplace_back(nMember, pAny);
1928     }
1929     if(!vMemberAndAny.empty())
1930     {
1931         Tpoolitem aItem = aItemFactory();
1932         for(const auto& aMemberAndAny : vMemberAndAny)
1933             aItem->PutValue(*aMemberAndAny.second, aMemberAndAny.first | (bAddTwips ? CONVERT_TWIPS : 0) );
1934         rSet.Put(*aItem);
1935     }
1936 }
1937 void SwTableProperties_Impl::ApplyTableAttr(const SwTable& rTable, SwDoc& rDoc)
1938 {
1939     SfxItemSet aSet(
1940         rDoc.GetAttrPool(),
1941         svl::Items<
1942             RES_FRM_SIZE, RES_BREAK,
1943             RES_HORI_ORIENT, RES_HORI_ORIENT,
1944             RES_BACKGROUND, RES_BACKGROUND,
1945             RES_SHADOW, RES_SHADOW,
1946             RES_KEEP, RES_KEEP,
1947             RES_LAYOUT_SPLIT, RES_LAYOUT_SPLIT>{});
1948     const uno::Any* pRepHead;
1949     const SwFrameFormat &rFrameFormat = *rTable.GetFrameFormat();
1950     if(GetProperty(FN_TABLE_HEADLINE_REPEAT, 0xff, pRepHead ))
1951     {
1952         bool bVal(pRepHead->get<bool>());
1953         const_cast<SwTable&>(rTable).SetRowsToRepeat( bVal ? 1 : 0 );  // TODO: MULTIHEADER
1954     }
1955 
1956     AddItemToSet<std::shared_ptr<SvxBrushItem>>(aSet, [&rFrameFormat]() { return rFrameFormat.makeBackgroundBrushItem(); }, RES_BACKGROUND, {
1957         MID_BACK_COLOR,
1958         MID_GRAPHIC_TRANSPARENT,
1959         MID_GRAPHIC_POSITION,
1960         MID_GRAPHIC,
1961         MID_GRAPHIC_FILTER });
1962 
1963     bool bPutBreak = true;
1964     const uno::Any* pPage;
1965     if(GetProperty(FN_UNO_PAGE_STYLE, 0, pPage) || GetProperty(RES_PAGEDESC, 0xff, pPage))
1966     {
1967         OUString sPageStyle = pPage->get<OUString>();
1968         if(!sPageStyle.isEmpty())
1969         {
1970             SwStyleNameMapper::FillUIName(sPageStyle, sPageStyle, SwGetPoolIdFromName::PageDesc);
1971             const SwPageDesc* pDesc = SwPageDesc::GetByName(rDoc, sPageStyle);
1972             if(pDesc)
1973             {
1974                 SwFormatPageDesc aDesc(pDesc);
1975                 const uno::Any* pPgNo;
1976                 if(GetProperty(RES_PAGEDESC, MID_PAGEDESC_PAGENUMOFFSET, pPgNo))
1977                 {
1978                     aDesc.SetNumOffset(pPgNo->get<sal_Int16>());
1979                 }
1980                 aSet.Put(aDesc);
1981                 bPutBreak = false;
1982             }
1983 
1984         }
1985     }
1986 
1987     if(bPutBreak)
1988         AddItemToSet<std::shared_ptr<SvxFormatBreakItem>>(aSet, [&rFrameFormat]() { return std::shared_ptr<SvxFormatBreakItem>(rFrameFormat.GetBreak().Clone()); }, RES_BREAK, {0});
1989     AddItemToSet<std::shared_ptr<SvxShadowItem>>(aSet, [&rFrameFormat]() { return std::shared_ptr<SvxShadowItem>(rFrameFormat.GetShadow().Clone()); }, RES_SHADOW, {0}, true);
1990     AddItemToSet<std::shared_ptr<SvxFormatKeepItem>>(aSet, [&rFrameFormat]() { return std::shared_ptr<SvxFormatKeepItem>(rFrameFormat.GetKeep().Clone()); }, RES_KEEP, {0});
1991     AddItemToSet<std::shared_ptr<SwFormatHoriOrient>>(aSet, [&rFrameFormat]() { return std::shared_ptr<SwFormatHoriOrient>(rFrameFormat.GetHoriOrient().Clone()); }, RES_HORI_ORIENT, {MID_HORIORIENT_ORIENT}, true);
1992 
1993     const uno::Any* pSzRel(nullptr);
1994     GetProperty(FN_TABLE_IS_RELATIVE_WIDTH, 0xff, pSzRel);
1995     const uno::Any* pRelWidth(nullptr);
1996     GetProperty(FN_TABLE_RELATIVE_WIDTH, 0xff, pRelWidth);
1997     const uno::Any* pWidth(nullptr);
1998     GetProperty(FN_TABLE_WIDTH, 0xff, pWidth);
1999 
2000     bool bPutSize = pWidth != nullptr;
2001     SwFormatFrameSize aSz(SwFrameSize::Variable);
2002     if(pWidth)
2003     {
2004         aSz.PutValue(*pWidth, MID_FRMSIZE_WIDTH);
2005         bPutSize = true;
2006     }
2007     if(pSzRel && pSzRel->get<bool>() && pRelWidth)
2008     {
2009         aSz.PutValue(*pRelWidth, MID_FRMSIZE_REL_WIDTH|CONVERT_TWIPS);
2010         bPutSize = true;
2011     }
2012     if(bPutSize)
2013     {
2014         if(!aSz.GetWidth())
2015             aSz.SetWidth(MINLAY);
2016         aSet.Put(aSz);
2017     }
2018     AddItemToSet<std::shared_ptr<SvxLRSpaceItem>>(aSet, [&rFrameFormat]() { return std::shared_ptr<SvxLRSpaceItem>(rFrameFormat.GetLRSpace().Clone()); }, RES_LR_SPACE, {
2019         MID_L_MARGIN|CONVERT_TWIPS,
2020         MID_R_MARGIN|CONVERT_TWIPS });
2021     AddItemToSet<std::shared_ptr<SvxULSpaceItem>>(aSet, [&rFrameFormat]() { return std::shared_ptr<SvxULSpaceItem>(rFrameFormat.GetULSpace().Clone()); }, RES_UL_SPACE, {
2022         MID_UP_MARGIN|CONVERT_TWIPS,
2023         MID_LO_MARGIN|CONVERT_TWIPS });
2024     const::uno::Any* pSplit(nullptr);
2025     if(GetProperty(RES_LAYOUT_SPLIT, 0, pSplit))
2026     {
2027         SwFormatLayoutSplit aSp(pSplit->get<bool>());
2028         aSet.Put(aSp);
2029     }
2030     if(aSet.Count())
2031     {
2032         rDoc.SetAttr(aSet, *rTable.GetFrameFormat());
2033     }
2034 }
2035 
2036 class SwXTextTable::Impl
2037     : public SvtListener
2038 {
2039 private:
2040     SwFrameFormat* m_pFrameFormat;
2041     ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2
2042 
2043 public:
2044     uno::WeakReference<uno::XInterface> m_wThis;
2045     ::cppu::OMultiTypeInterfaceContainerHelper m_Listeners;
2046 
2047     const SfxItemPropertySet * m_pPropSet;
2048 
2049     css::uno::WeakReference<css::table::XTableRows> m_xRows;
2050     css::uno::WeakReference<css::table::XTableColumns> m_xColumns;
2051 
2052     bool m_bFirstRowAsLabel;
2053     bool m_bFirstColumnAsLabel;
2054 
2055     // Descriptor-interface
2056     std::unique_ptr<SwTableProperties_Impl> m_pTableProps;
2057     OUString       m_sTableName;
2058     unsigned short m_nRows;
2059     unsigned short m_nColumns;
2060 
2061     explicit Impl(SwFrameFormat* const pFrameFormat)
2062         : m_pFrameFormat(pFrameFormat)
2063         , m_Listeners(m_Mutex)
2064         , m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_TABLE))
2065         , m_bFirstRowAsLabel(false)
2066         , m_bFirstColumnAsLabel(false)
2067         , m_pTableProps(pFrameFormat ? nullptr : new SwTableProperties_Impl)
2068         , m_nRows(pFrameFormat ? 0 : 2)
2069         , m_nColumns(pFrameFormat ? 0 : 2)
2070     {
2071         if(m_pFrameFormat)
2072             StartListening(m_pFrameFormat->GetNotifier());
2073     }
2074 
2075     SwFrameFormat* GetFrameFormat() { return m_pFrameFormat; }
2076     void SetFrameFormat(SwFrameFormat& rFrameFormat)
2077     {
2078         EndListeningAll();
2079         m_pFrameFormat = &rFrameFormat;
2080         StartListening(m_pFrameFormat->GetNotifier());
2081     }
2082 
2083     bool IsDescriptor() const { return m_pTableProps != nullptr; }
2084 
2085     // note: lock mutex before calling this to avoid concurrent update
2086     static std::pair<sal_uInt16, sal_uInt16> ThrowIfComplex(SwXTextTable &rThis)
2087     {
2088         sal_uInt16 const nRowCount(rThis.m_pImpl->GetRowCount());
2089         sal_uInt16 const nColCount(rThis.m_pImpl->GetColumnCount());
2090         if (!nRowCount || !nColCount)
2091         {
2092             throw uno::RuntimeException("Table too complex",
2093                     static_cast<cppu::OWeakObject*>(&rThis));
2094         }
2095         return std::make_pair(nRowCount, nColCount);
2096     }
2097 
2098     sal_uInt16 GetRowCount();
2099     sal_uInt16 GetColumnCount();
2100 
2101     virtual void Notify(const SfxHint&) override;
2102 
2103 };
2104 
2105 namespace
2106 {
2107     class theSwXTextTableUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextTableUnoTunnelId > {};
2108 }
2109 
2110 const uno::Sequence< sal_Int8 > & SwXTextTable::getUnoTunnelId()
2111     { return theSwXTextTableUnoTunnelId::get().getSeq(); }
2112 
2113 sal_Int64 SAL_CALL SwXTextTable::getSomething( const uno::Sequence< sal_Int8 >& rId )
2114 {
2115     if(isUnoTunnelId<SwXTextTable>(rId))
2116     {
2117         return sal::static_int_cast<sal_Int64>(reinterpret_cast<sal_IntPtr>(this));
2118     }
2119     return 0;
2120 }
2121 
2122 
2123 SwXTextTable::SwXTextTable()
2124     : m_pImpl(new Impl(nullptr))
2125 {
2126 }
2127 
2128 SwXTextTable::SwXTextTable(SwFrameFormat& rFrameFormat)
2129     : m_pImpl(new Impl(&rFrameFormat))
2130 {
2131 }
2132 
2133 SwXTextTable::~SwXTextTable()
2134 {
2135 }
2136 
2137 uno::Reference<text::XTextTable> SwXTextTable::CreateXTextTable(SwFrameFormat* const pFrameFormat)
2138 {
2139     uno::Reference<text::XTextTable> xTable;
2140     if(pFrameFormat)
2141         xTable.set(pFrameFormat->GetXObject(), uno::UNO_QUERY); // cached?
2142     if(xTable.is())
2143         return xTable;
2144     SwXTextTable* const pNew( pFrameFormat ? new SwXTextTable(*pFrameFormat) : new SwXTextTable());
2145     xTable.set(pNew);
2146     if(pFrameFormat)
2147         pFrameFormat->SetXObject(xTable);
2148     // need a permanent Reference to initialize m_wThis
2149     pNew->m_pImpl->m_wThis = xTable;
2150     return xTable;
2151 }
2152 
2153 SwFrameFormat* SwXTextTable::GetFrameFormat()
2154 {
2155     return m_pImpl->GetFrameFormat();
2156 }
2157 
2158 void SwXTextTable::initialize(sal_Int32 nR, sal_Int32 nC)
2159 {
2160     if (!m_pImpl->IsDescriptor() || nR <= 0 || nC <= 0 || nR >= SAL_MAX_UINT16 || nC >= SAL_MAX_UINT16)
2161         throw uno::RuntimeException();
2162     m_pImpl->m_nRows = static_cast<sal_uInt16>(nR);
2163     m_pImpl->m_nColumns = static_cast<sal_uInt16>(nC);
2164 }
2165 
2166 uno::Reference<table::XTableRows> SAL_CALL SwXTextTable::getRows()
2167 {
2168     SolarMutexGuard aGuard;
2169     uno::Reference<table::XTableRows> xResult(m_pImpl->m_xRows);
2170     if(xResult.is())
2171         return xResult;
2172     if(SwFrameFormat* pFormat = GetFrameFormat())
2173         m_pImpl->m_xRows = xResult = new SwXTableRows(*pFormat);
2174     if(!xResult.is())
2175         throw uno::RuntimeException();
2176     return xResult;
2177 }
2178 
2179 uno::Reference<table::XTableColumns> SAL_CALL SwXTextTable::getColumns()
2180 {
2181     SolarMutexGuard aGuard;
2182     uno::Reference<table::XTableColumns> xResult(m_pImpl->m_xColumns);
2183     if(xResult.is())
2184         return xResult;
2185     if(SwFrameFormat* pFormat = GetFrameFormat())
2186         m_pImpl->m_xColumns = xResult = new SwXTableColumns(*pFormat);
2187     if(!xResult.is())
2188         throw uno::RuntimeException();
2189     return xResult;
2190 }
2191 
2192 uno::Reference<table::XCell> SwXTextTable::getCellByName(const OUString& sCellName)
2193 {
2194     SolarMutexGuard aGuard;
2195     SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
2196     SwTable* pTable = SwTable::FindTable(pFormat);
2197     SwTableBox* pBox = const_cast<SwTableBox*>(pTable->GetTableBox(sCellName));
2198     if(!pBox)
2199         return nullptr;
2200     return SwXCell::CreateXCell(pFormat, pBox);
2201 }
2202 
2203 uno::Sequence<OUString> SwXTextTable::getCellNames()
2204 {
2205     SolarMutexGuard aGuard;
2206     SwFrameFormat* pFormat(GetFrameFormat());
2207     if(!pFormat)
2208         return {};
2209     SwTable* pTable = SwTable::FindTable(pFormat);
2210     // exists at the table and at all boxes
2211     SwTableLines& rTableLines = pTable->GetTabLines();
2212     std::vector<OUString> aAllNames;
2213     lcl_InspectLines(rTableLines, aAllNames);
2214     return comphelper::containerToSequence(aAllNames);
2215 }
2216 
2217 uno::Reference<text::XTextTableCursor> SwXTextTable::createCursorByCellName(const OUString& sCellName)
2218 {
2219     SolarMutexGuard aGuard;
2220     SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
2221     SwTable* pTable = SwTable::FindTable(pFormat);
2222     SwTableBox* pBox = const_cast<SwTableBox*>(pTable->GetTableBox(sCellName));
2223     if(!pBox || pBox->getRowSpan() == 0)
2224         throw uno::RuntimeException();
2225     return new SwXTextTableCursor(pFormat, pBox);
2226 }
2227 
2228 void SAL_CALL
2229 SwXTextTable::attach(const uno::Reference<text::XTextRange> & xTextRange)
2230 {
2231     SolarMutexGuard aGuard;
2232 
2233     // attach() must only be called once
2234     if (!m_pImpl->IsDescriptor())  /* already attached ? */
2235         throw uno::RuntimeException("SwXTextTable: already attached to range.", static_cast<cppu::OWeakObject*>(this));
2236 
2237     uno::Reference<XUnoTunnel> xRangeTunnel(xTextRange, uno::UNO_QUERY);
2238     SwXTextRange* pRange(nullptr);
2239     OTextCursorHelper* pCursor(nullptr);
2240     if(xRangeTunnel.is())
2241     {
2242         pRange  = reinterpret_cast<SwXTextRange*>(
2243                 sal::static_int_cast<sal_IntPtr>(xRangeTunnel->getSomething(SwXTextRange::getUnoTunnelId())));
2244         pCursor = reinterpret_cast<OTextCursorHelper*>(
2245                 sal::static_int_cast<sal_IntPtr>(xRangeTunnel->getSomething(OTextCursorHelper::getUnoTunnelId())));
2246     }
2247     SwDoc* pDoc = pRange ? &pRange->GetDoc() : pCursor ? pCursor->GetDoc() : nullptr;
2248     if (!pDoc || !m_pImpl->m_nRows || !m_pImpl->m_nColumns)
2249         throw lang::IllegalArgumentException();
2250     SwUnoInternalPaM aPam(*pDoc);
2251     // this now needs to return TRUE
2252     ::sw::XTextRangeToSwPaM(aPam, xTextRange);
2253     {
2254         UnoActionContext aCont(pDoc);
2255 
2256         pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
2257         const SwTable* pTable(nullptr);
2258         if( 0 != aPam.Start()->nContent.GetIndex() )
2259         {
2260             pDoc->getIDocumentContentOperations().SplitNode(*aPam.Start(), false);
2261         }
2262         //TODO: if it is the last paragraph than add another one!
2263         if(aPam.HasMark())
2264         {
2265             pDoc->getIDocumentContentOperations().DeleteAndJoin(aPam);
2266             aPam.DeleteMark();
2267         }
2268         pTable = pDoc->InsertTable(SwInsertTableOptions( SwInsertTableFlags::Headline | SwInsertTableFlags::DefaultBorder | SwInsertTableFlags::SplitLayout, 0 ),
2269                 *aPam.GetPoint(),
2270                 m_pImpl->m_nRows,
2271                 m_pImpl->m_nColumns,
2272                 text::HoriOrientation::FULL);
2273         if(pTable)
2274         {
2275             // here, the properties of the descriptor need to be analyzed
2276             m_pImpl->m_pTableProps->ApplyTableAttr(*pTable, *pDoc);
2277             SwFrameFormat* pTableFormat(pTable->GetFrameFormat());
2278             lcl_FormatTable(pTableFormat);
2279 
2280             m_pImpl->SetFrameFormat(*pTableFormat);
2281 
2282             if (!m_pImpl->m_sTableName.isEmpty())
2283             {
2284                 sal_uInt16 nIndex = 1;
2285                 OUString sTmpNameIndex(m_pImpl->m_sTableName);
2286                 while(pDoc->FindTableFormatByName(sTmpNameIndex, true) && nIndex < USHRT_MAX)
2287                 {
2288                     sTmpNameIndex = m_pImpl->m_sTableName + OUString::number(nIndex++);
2289                 }
2290                 pDoc->SetTableName( *pTableFormat, sTmpNameIndex);
2291             }
2292 
2293             const::uno::Any* pName;
2294             if (m_pImpl->m_pTableProps->GetProperty(FN_UNO_TABLE_NAME, 0, pName))
2295                 setName(pName->get<OUString>());
2296             m_pImpl->m_pTableProps.reset();
2297         }
2298         pDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
2299     }
2300 }
2301 
2302 uno::Reference<text::XTextRange>  SwXTextTable::getAnchor()
2303 {
2304     SolarMutexGuard aGuard;
2305     SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
2306     return new SwXTextRange(*pFormat);
2307 }
2308 
2309 void SwXTextTable::dispose()
2310 {
2311     SolarMutexGuard aGuard;
2312     SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
2313     SwTable* pTable = SwTable::FindTable(pFormat);
2314     SwSelBoxes aSelBoxes;
2315     for(auto& rBox : pTable->GetTabSortBoxes() )
2316         aSelBoxes.insert(rBox);
2317     pFormat->GetDoc()->DeleteRowCol(aSelBoxes);
2318 }
2319 
2320 void SAL_CALL SwXTextTable::addEventListener(
2321         const uno::Reference<lang::XEventListener> & xListener)
2322 {
2323     // no need to lock here as m_pImpl is const and container threadsafe
2324     m_pImpl->m_Listeners.addInterface(
2325             cppu::UnoType<lang::XEventListener>::get(), xListener);
2326 }
2327 
2328 void SAL_CALL SwXTextTable::removeEventListener(
2329         const uno::Reference< lang::XEventListener > & xListener)
2330 {
2331     // no need to lock here as m_pImpl is const and container threadsafe
2332     m_pImpl->m_Listeners.removeInterface(
2333             cppu::UnoType<lang::XEventListener>::get(), xListener);
2334 }
2335 
2336 uno::Reference<table::XCell>  SwXTextTable::getCellByPosition(sal_Int32 nColumn, sal_Int32 nRow)
2337 {
2338     SolarMutexGuard aGuard;
2339     SwFrameFormat* pFormat(GetFrameFormat());
2340     // sheet is unimportant
2341     if(nColumn >= 0 && nRow >= 0 && pFormat)
2342     {
2343         auto pXCell = lcl_CreateXCell(pFormat, nColumn, nRow);
2344         if(pXCell)
2345             return pXCell;
2346     }
2347     throw lang::IndexOutOfBoundsException();
2348 }
2349 
2350 namespace {
2351 
2352 uno::Reference<table::XCellRange> GetRangeByName(
2353         SwFrameFormat* pFormat, SwTable const * pTable,
2354         const OUString& rTLName, const OUString& rBRName,
2355         SwRangeDescriptor const & rDesc)
2356 {
2357     const SwTableBox* pTLBox = pTable->GetTableBox(rTLName);
2358     if(!pTLBox)
2359         return nullptr;
2360     const SwStartNode* pSttNd = pTLBox->GetSttNd();
2361     SwPosition aPos(*pSttNd);
2362     // set cursor to the upper-left cell of the range
2363     auto pUnoCursor(pFormat->GetDoc()->CreateUnoCursor(aPos, true));
2364     pUnoCursor->Move(fnMoveForward, GoInNode);
2365     pUnoCursor->SetRemainInSection(false);
2366     const SwTableBox* pBRBox(pTable->GetTableBox(rBRName));
2367     if(!pBRBox)
2368         return nullptr;
2369     pUnoCursor->SetMark();
2370     pUnoCursor->GetPoint()->nNode = *pBRBox->GetSttNd();
2371     pUnoCursor->Move( fnMoveForward, GoInNode );
2372     SwUnoTableCursor& rCursor = dynamic_cast<SwUnoTableCursor&>(*pUnoCursor);
2373     // HACK: remove pending actions for selecting old style tables
2374     UnoActionRemoveContext aRemoveContext(rCursor);
2375     rCursor.MakeBoxSels();
2376     // pUnoCursor will be provided and will not be deleted
2377     return SwXCellRange::CreateXCellRange(pUnoCursor, *pFormat, rDesc).get();
2378 }
2379 
2380 } // namespace
2381 
2382 uno::Reference<table::XCellRange>  SwXTextTable::getCellRangeByPosition(sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom)
2383 {
2384     SolarMutexGuard aGuard;
2385     SwFrameFormat* pFormat(GetFrameFormat());
2386     if(pFormat &&
2387             nLeft <= nRight && nTop <= nBottom &&
2388             nLeft >= 0 && nRight >= 0 && nTop >= 0 && nBottom >= 0 )
2389     {
2390         SwTable* pTable = SwTable::FindTable(pFormat);
2391         if(!pTable->IsTableComplex())
2392         {
2393             SwRangeDescriptor aDesc;
2394             aDesc.nTop    = nTop;
2395             aDesc.nBottom = nBottom;
2396             aDesc.nLeft   = nLeft;
2397             aDesc.nRight  = nRight;
2398             const OUString sTLName = sw_GetCellName(aDesc.nLeft, aDesc.nTop);
2399             const OUString sBRName = sw_GetCellName(aDesc.nRight, aDesc.nBottom);
2400             // please note that according to the 'if' statement at the begin
2401             // sTLName:sBRName already denotes the normalized range string
2402             return GetRangeByName(pFormat, pTable, sTLName, sBRName, aDesc);
2403         }
2404     }
2405     throw lang::IndexOutOfBoundsException();
2406 }
2407 
2408 uno::Reference<table::XCellRange> SwXTextTable::getCellRangeByName(const OUString& sRange)
2409 {
2410     SolarMutexGuard aGuard;
2411     SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
2412     SwTable* pTable = lcl_EnsureTableNotComplex(SwTable::FindTable(pFormat), static_cast<cppu::OWeakObject*>(this));
2413     sal_Int32 nPos = 0;
2414     const OUString sTLName(sRange.getToken(0, ':', nPos));
2415     const OUString sBRName(sRange.getToken(0, ':', nPos));
2416     if(sTLName.isEmpty() || sBRName.isEmpty())
2417         throw uno::RuntimeException();
2418     SwRangeDescriptor aDesc;
2419     aDesc.nTop = aDesc.nLeft = aDesc.nBottom = aDesc.nRight = -1;
2420     SwXTextTable::GetCellPosition(sTLName, aDesc.nLeft, aDesc.nTop );
2421     SwXTextTable::GetCellPosition(sBRName, aDesc.nRight, aDesc.nBottom );
2422 
2423     // we should normalize the range now (e.g. A5:C1 will become A1:C5)
2424     // since (depending on what is done later) it will be troublesome
2425     // elsewhere when the cursor in the implementation does not
2426     // point to the top-left and bottom-right cells
2427     aDesc.Normalize();
2428     return GetRangeByName(pFormat, pTable, sTLName, sBRName, aDesc);
2429 }
2430 
2431 uno::Sequence< uno::Sequence< uno::Any > > SAL_CALL SwXTextTable::getDataArray()
2432 {
2433     SolarMutexGuard aGuard;
2434     std::pair<sal_uInt16, sal_uInt16> const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this));
2435     uno::Reference<sheet::XCellRangeData> const xAllRange(
2436         getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1),
2437         uno::UNO_QUERY_THROW);
2438     return xAllRange->getDataArray();
2439 }
2440 
2441 void SAL_CALL SwXTextTable::setDataArray(const uno::Sequence< uno::Sequence< uno::Any > >& rArray)
2442 {
2443     SolarMutexGuard aGuard;
2444     std::pair<sal_uInt16, sal_uInt16> const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this));
2445     uno::Reference<sheet::XCellRangeData> const xAllRange(
2446         getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1),
2447         uno::UNO_QUERY_THROW);
2448     return xAllRange->setDataArray(rArray);
2449 }
2450 
2451 uno::Sequence< uno::Sequence< double > > SwXTextTable::getData()
2452 {
2453     SolarMutexGuard aGuard;
2454     std::pair<sal_uInt16, sal_uInt16> const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this));
2455     uno::Reference<chart::XChartDataArray> const xAllRange(
2456         getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1),
2457         uno::UNO_QUERY_THROW);
2458     static_cast<SwXCellRange*>(xAllRange.get())->SetLabels(
2459             m_pImpl->m_bFirstRowAsLabel, m_pImpl->m_bFirstColumnAsLabel);
2460     return xAllRange->getData();
2461 }
2462 
2463 void SwXTextTable::setData(const uno::Sequence< uno::Sequence< double > >& rData)
2464 {
2465     SolarMutexGuard aGuard;
2466     std::pair<sal_uInt16, sal_uInt16> const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this));
2467     uno::Reference<chart::XChartDataArray> const xAllRange(
2468         getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1),
2469         uno::UNO_QUERY_THROW);
2470     static_cast<SwXCellRange*>(xAllRange.get())->SetLabels(
2471             m_pImpl->m_bFirstRowAsLabel, m_pImpl->m_bFirstColumnAsLabel);
2472     xAllRange->setData(rData);
2473     // this is rather inconsistent: setData on XTextTable sends events, but e.g. CellRanges do not
2474     lcl_SendChartEvent(*this, m_pImpl->m_Listeners);
2475 }
2476 
2477 uno::Sequence<OUString> SwXTextTable::getRowDescriptions()
2478 {
2479     SolarMutexGuard aGuard;
2480     std::pair<sal_uInt16, sal_uInt16> const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this));
2481     uno::Reference<chart::XChartDataArray> const xAllRange(
2482         getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1),
2483         uno::UNO_QUERY_THROW);
2484     static_cast<SwXCellRange*>(xAllRange.get())->SetLabels(
2485             m_pImpl->m_bFirstRowAsLabel, m_pImpl->m_bFirstColumnAsLabel);
2486     return xAllRange->getRowDescriptions();
2487 }
2488 
2489 void SwXTextTable::setRowDescriptions(const uno::Sequence<OUString>& rRowDesc)
2490 {
2491     SolarMutexGuard aGuard;
2492     std::pair<sal_uInt16, sal_uInt16> const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this));
2493     uno::Reference<chart::XChartDataArray> const xAllRange(
2494         getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1),
2495         uno::UNO_QUERY_THROW);
2496     static_cast<SwXCellRange*>(xAllRange.get())->SetLabels(
2497             m_pImpl->m_bFirstRowAsLabel, m_pImpl->m_bFirstColumnAsLabel);
2498     xAllRange->setRowDescriptions(rRowDesc);
2499 }
2500 
2501 uno::Sequence<OUString> SwXTextTable::getColumnDescriptions()
2502 {
2503     SolarMutexGuard aGuard;
2504     std::pair<sal_uInt16, sal_uInt16> const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this));
2505     uno::Reference<chart::XChartDataArray> const xAllRange(
2506         getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1),
2507         uno::UNO_QUERY_THROW);
2508     static_cast<SwXCellRange*>(xAllRange.get())->SetLabels(
2509             m_pImpl->m_bFirstRowAsLabel, m_pImpl->m_bFirstColumnAsLabel);
2510     return xAllRange->getColumnDescriptions();
2511 }
2512 
2513 void SwXTextTable::setColumnDescriptions(const uno::Sequence<OUString>& rColumnDesc)
2514 {
2515     SolarMutexGuard aGuard;
2516     std::pair<sal_uInt16, sal_uInt16> const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this));
2517     uno::Reference<chart::XChartDataArray> const xAllRange(
2518         getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1),
2519         uno::UNO_QUERY_THROW);
2520     static_cast<SwXCellRange*>(xAllRange.get())->SetLabels(
2521             m_pImpl->m_bFirstRowAsLabel, m_pImpl->m_bFirstColumnAsLabel);
2522     return xAllRange->setColumnDescriptions(rColumnDesc);
2523 }
2524 
2525 void SAL_CALL SwXTextTable::addChartDataChangeEventListener(
2526     const uno::Reference<chart::XChartDataChangeEventListener> & xListener)
2527 {
2528     // no need to lock here as m_pImpl is const and container threadsafe
2529     m_pImpl->m_Listeners.addInterface(
2530             cppu::UnoType<chart::XChartDataChangeEventListener>::get(), xListener);
2531 }
2532 
2533 void SAL_CALL SwXTextTable::removeChartDataChangeEventListener(
2534     const uno::Reference<chart::XChartDataChangeEventListener> & xListener)
2535 {
2536     // no need to lock here as m_pImpl is const and container threadsafe
2537     m_pImpl->m_Listeners.removeInterface(
2538             cppu::UnoType<chart::XChartDataChangeEventListener>::get(), xListener);
2539 }
2540 
2541 sal_Bool SwXTextTable::isNotANumber(double nNumber)
2542 {
2543     // We use DBL_MIN because starcalc does (which uses it because chart
2544     // wants it that way!)
2545     return ( nNumber == DBL_MIN );
2546 }
2547 
2548 double SwXTextTable::getNotANumber()
2549 {
2550     // We use DBL_MIN because starcalc does (which uses it because chart
2551     // wants it that way!)
2552     return DBL_MIN;
2553 }
2554 
2555 uno::Sequence< beans::PropertyValue > SwXTextTable::createSortDescriptor()
2556 {
2557     SolarMutexGuard aGuard;
2558 
2559     return SwUnoCursorHelper::CreateSortDescriptor(true);
2560 }
2561 
2562 void SwXTextTable::sort(const uno::Sequence< beans::PropertyValue >& rDescriptor)
2563 {
2564     SolarMutexGuard aGuard;
2565     SwSortOptions aSortOpt;
2566     SwFrameFormat* pFormat = GetFrameFormat();
2567     if(pFormat &&
2568         SwUnoCursorHelper::ConvertSortProperties(rDescriptor, aSortOpt))
2569     {
2570         SwTable* pTable = SwTable::FindTable( pFormat );
2571         SwSelBoxes aBoxes;
2572         const SwTableSortBoxes& rTBoxes = pTable->GetTabSortBoxes();
2573         for (size_t n = 0; n < rTBoxes.size(); ++n)
2574         {
2575             SwTableBox* pBox = rTBoxes[ n ];
2576             aBoxes.insert( pBox );
2577         }
2578         UnoActionContext aContext( pFormat->GetDoc() );
2579         pFormat->GetDoc()->SortTable(aBoxes, aSortOpt);
2580     }
2581 }
2582 
2583 void SwXTextTable::autoFormat(const OUString& sAutoFormatName)
2584 {
2585     SolarMutexGuard aGuard;
2586     SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
2587     SwTable* pTable = lcl_EnsureTableNotComplex(SwTable::FindTable(pFormat), static_cast<cppu::OWeakObject*>(this));
2588     SwTableAutoFormatTable aAutoFormatTable;
2589     aAutoFormatTable.Load();
2590     for (size_t i = aAutoFormatTable.size(); i;)
2591         if( sAutoFormatName == aAutoFormatTable[ --i ].GetName() )
2592         {
2593             SwSelBoxes aBoxes;
2594             const SwTableSortBoxes& rTBoxes = pTable->GetTabSortBoxes();
2595             for (size_t n = 0; n < rTBoxes.size(); ++n)
2596             {
2597                 SwTableBox* pBox = rTBoxes[ n ];
2598                 aBoxes.insert( pBox );
2599             }
2600             UnoActionContext aContext( pFormat->GetDoc() );
2601             pFormat->GetDoc()->SetTableAutoFormat( aBoxes, aAutoFormatTable[i] );
2602             break;
2603         }
2604 }
2605 
2606 uno::Reference< beans::XPropertySetInfo >  SwXTextTable::getPropertySetInfo()
2607 {
2608     static uno::Reference<beans::XPropertySetInfo> xRef = m_pImpl->m_pPropSet->getPropertySetInfo();
2609     return xRef;
2610 }
2611 
2612 void SwXTextTable::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue)
2613 {
2614     SolarMutexGuard aGuard;
2615     SwFrameFormat* pFormat = GetFrameFormat();
2616     if(!aValue.hasValue())
2617         throw lang::IllegalArgumentException();
2618     const SfxItemPropertySimpleEntry* pEntry =
2619             m_pImpl->m_pPropSet->getPropertyMap().getByName(rPropertyName);
2620     if( !pEntry )
2621         throw lang::IllegalArgumentException();
2622     if(pFormat)
2623     {
2624         if ( pEntry->nFlags & beans::PropertyAttribute::READONLY)
2625             throw beans::PropertyVetoException("Property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
2626 
2627         if(0xBF == pEntry->nMemberId)
2628         {
2629             lcl_SetSpecialProperty(pFormat, pEntry, aValue);
2630         }
2631         else
2632         {
2633             switch(pEntry->nWID)
2634             {
2635                 case FN_UNO_TABLE_NAME :
2636                 {
2637                     OUString sName;
2638                     aValue >>= sName;
2639                     setName( sName );
2640                 }
2641                 break;
2642 
2643                 case FN_UNO_RANGE_ROW_LABEL:
2644                 {
2645                     bool bTmp = *o3tl::doAccess<bool>(aValue);
2646                     if (m_pImpl->m_bFirstRowAsLabel != bTmp)
2647                     {
2648                         lcl_SendChartEvent(*this, m_pImpl->m_Listeners);
2649                         m_pImpl->m_bFirstRowAsLabel = bTmp;
2650                     }
2651                 }
2652                 break;
2653 
2654                 case FN_UNO_RANGE_COL_LABEL:
2655                 {
2656                     bool bTmp = *o3tl::doAccess<bool>(aValue);
2657                     if (m_pImpl->m_bFirstColumnAsLabel != bTmp)
2658                     {
2659                         lcl_SendChartEvent(*this, m_pImpl->m_Listeners);
2660                         m_pImpl->m_bFirstColumnAsLabel = bTmp;
2661                     }
2662                 }
2663                 break;
2664 
2665                 case FN_UNO_TABLE_BORDER:
2666                 case FN_UNO_TABLE_BORDER2:
2667                 {
2668                     table::TableBorder oldBorder;
2669                     table::TableBorder2 aBorder;
2670                     SvxBorderLine aTopLine;
2671                     SvxBorderLine aBottomLine;
2672                     SvxBorderLine aLeftLine;
2673                     SvxBorderLine aRightLine;
2674                     SvxBorderLine aHoriLine;
2675                     SvxBorderLine aVertLine;
2676                     if (aValue >>= oldBorder)
2677                     {
2678                         aBorder.IsTopLineValid = oldBorder.IsTopLineValid;
2679                         aBorder.IsBottomLineValid = oldBorder.IsBottomLineValid;
2680                         aBorder.IsLeftLineValid = oldBorder.IsLeftLineValid;
2681                         aBorder.IsRightLineValid = oldBorder.IsRightLineValid;
2682                         aBorder.IsHorizontalLineValid = oldBorder.IsHorizontalLineValid;
2683                         aBorder.IsVerticalLineValid = oldBorder.IsVerticalLineValid;
2684                         aBorder.Distance = oldBorder.Distance;
2685                         aBorder.IsDistanceValid = oldBorder.IsDistanceValid;
2686                         lcl_LineToSvxLine(
2687                                 oldBorder.TopLine, aTopLine);
2688                         lcl_LineToSvxLine(
2689                                 oldBorder.BottomLine, aBottomLine);
2690                         lcl_LineToSvxLine(
2691                                 oldBorder.LeftLine, aLeftLine);
2692                         lcl_LineToSvxLine(
2693                                 oldBorder.RightLine, aRightLine);
2694                         lcl_LineToSvxLine(
2695                                 oldBorder.HorizontalLine, aHoriLine);
2696                         lcl_LineToSvxLine(
2697                                 oldBorder.VerticalLine, aVertLine);
2698                     }
2699                     else if (aValue >>= aBorder)
2700                     {
2701                         SvxBoxItem::LineToSvxLine(
2702                                 aBorder.TopLine, aTopLine, true);
2703                         SvxBoxItem::LineToSvxLine(
2704                                 aBorder.BottomLine, aBottomLine, true);
2705                         SvxBoxItem::LineToSvxLine(
2706                                 aBorder.LeftLine, aLeftLine, true);
2707                         SvxBoxItem::LineToSvxLine(
2708                                 aBorder.RightLine, aRightLine, true);
2709                         SvxBoxItem::LineToSvxLine(
2710                                 aBorder.HorizontalLine, aHoriLine, true);
2711                         SvxBoxItem::LineToSvxLine(
2712                                 aBorder.VerticalLine, aVertLine, true);
2713                     }
2714                     else
2715                     {
2716                         break; // something else
2717                     }
2718                     SwDoc* pDoc = pFormat->GetDoc();
2719                     if(!lcl_FormatTable(pFormat))
2720                         break;
2721                     SwTable* pTable = SwTable::FindTable( pFormat );
2722                     SwTableLines &rLines = pTable->GetTabLines();
2723 
2724                     const SwTableBox* pTLBox = lcl_FindCornerTableBox(rLines, true);
2725                     const SwStartNode* pSttNd = pTLBox->GetSttNd();
2726                     SwPosition aPos(*pSttNd);
2727                     // set cursor to top left cell
2728                     auto pUnoCursor(pDoc->CreateUnoCursor(aPos, true));
2729                     pUnoCursor->Move( fnMoveForward, GoInNode );
2730                     pUnoCursor->SetRemainInSection( false );
2731 
2732                     const SwTableBox* pBRBox = lcl_FindCornerTableBox(rLines, false);
2733                     pUnoCursor->SetMark();
2734                     pUnoCursor->GetPoint()->nNode = *pBRBox->GetSttNd();
2735                     pUnoCursor->Move( fnMoveForward, GoInNode );
2736                     SwUnoTableCursor& rCursor = dynamic_cast<SwUnoTableCursor&>(*pUnoCursor);
2737                     // HACK: remove pending actions for selecting old style tables
2738                     UnoActionRemoveContext aRemoveContext(rCursor);
2739                     rCursor.MakeBoxSels();
2740 
2741                     SfxItemSet aSet(pDoc->GetAttrPool(),
2742                                     svl::Items<RES_BOX, RES_BOX,
2743                                     SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>{});
2744 
2745                     SvxBoxItem aBox( RES_BOX );
2746                     SvxBoxInfoItem aBoxInfo( SID_ATTR_BORDER_INNER );
2747 
2748                     aBox.SetLine(aTopLine.isEmpty() ? nullptr : &aTopLine, SvxBoxItemLine::TOP);
2749                     aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::TOP, aBorder.IsTopLineValid);
2750 
2751                     aBox.SetLine(aBottomLine.isEmpty() ? nullptr : &aBottomLine, SvxBoxItemLine::BOTTOM);
2752                     aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::BOTTOM, aBorder.IsBottomLineValid);
2753 
2754                     aBox.SetLine(aLeftLine.isEmpty() ? nullptr : &aLeftLine, SvxBoxItemLine::LEFT);
2755                     aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::LEFT, aBorder.IsLeftLineValid);
2756 
2757                     aBox.SetLine(aRightLine.isEmpty() ? nullptr : &aRightLine, SvxBoxItemLine::RIGHT);
2758                     aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::RIGHT, aBorder.IsRightLineValid);
2759 
2760                     aBoxInfo.SetLine(aHoriLine.isEmpty() ? nullptr : &aHoriLine, SvxBoxInfoItemLine::HORI);
2761                     aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI, aBorder.IsHorizontalLineValid);
2762 
2763                     aBoxInfo.SetLine(aVertLine.isEmpty() ? nullptr : &aVertLine, SvxBoxInfoItemLine::VERT);
2764                     aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::VERT, aBorder.IsVerticalLineValid);
2765 
2766                     aBox.SetAllDistances(static_cast<sal_uInt16>(convertMm100ToTwip(aBorder.Distance)));
2767                     aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::DISTANCE, aBorder.IsDistanceValid);
2768 
2769                     aSet.Put(aBox);
2770                     aSet.Put(aBoxInfo);
2771 
2772                     pDoc->SetTabBorders(rCursor, aSet);
2773                 }
2774                 break;
2775 
2776                 case FN_UNO_TABLE_BORDER_DISTANCES:
2777                 {
2778                     table::TableBorderDistances aTableBorderDistances;
2779                     if( !(aValue >>= aTableBorderDistances) ||
2780                         (!aTableBorderDistances.IsLeftDistanceValid &&
2781                         !aTableBorderDistances.IsRightDistanceValid &&
2782                         !aTableBorderDistances.IsTopDistanceValid &&
2783                         !aTableBorderDistances.IsBottomDistanceValid ))
2784                         break;
2785 
2786                     const sal_uInt16 nLeftDistance =   convertMm100ToTwip(aTableBorderDistances.LeftDistance);
2787                     const sal_uInt16 nRightDistance =  convertMm100ToTwip(aTableBorderDistances.RightDistance);
2788                     const sal_uInt16 nTopDistance =    convertMm100ToTwip(aTableBorderDistances.TopDistance);
2789                     const sal_uInt16 nBottomDistance = convertMm100ToTwip(aTableBorderDistances.BottomDistance);
2790                     SwDoc* pDoc = pFormat->GetDoc();
2791                     SwTable* pTable = SwTable::FindTable( pFormat );
2792                     SwTableLines &rLines = pTable->GetTabLines();
2793                     pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::START, nullptr);
2794                     for(size_t i = 0; i < rLines.size(); ++i)
2795                     {
2796                         SwTableLine* pLine = rLines[i];
2797                         SwTableBoxes& rBoxes = pLine->GetTabBoxes();
2798                         for(size_t k = 0; k < rBoxes.size(); ++k)
2799                         {
2800                             SwTableBox* pBox = rBoxes[k];
2801                             const SwFrameFormat* pBoxFormat = pBox->GetFrameFormat();
2802                             const SvxBoxItem& rBox = pBoxFormat->GetBox();
2803                             if(
2804                                 (aTableBorderDistances.IsLeftDistanceValid && nLeftDistance !=   rBox.GetDistance( SvxBoxItemLine::LEFT )) ||
2805                                 (aTableBorderDistances.IsRightDistanceValid && nRightDistance !=  rBox.GetDistance( SvxBoxItemLine::RIGHT )) ||
2806                                 (aTableBorderDistances.IsTopDistanceValid && nTopDistance !=    rBox.GetDistance( SvxBoxItemLine::TOP )) ||
2807                                 (aTableBorderDistances.IsBottomDistanceValid && nBottomDistance != rBox.GetDistance( SvxBoxItemLine::BOTTOM )))
2808                             {
2809                                 SvxBoxItem aSetBox( rBox );
2810                                 SwFrameFormat* pSetBoxFormat = pBox->ClaimFrameFormat();
2811                                 if( aTableBorderDistances.IsLeftDistanceValid )
2812                                     aSetBox.SetDistance( nLeftDistance, SvxBoxItemLine::LEFT );
2813                                 if( aTableBorderDistances.IsRightDistanceValid )
2814                                     aSetBox.SetDistance( nRightDistance, SvxBoxItemLine::RIGHT );
2815                                 if( aTableBorderDistances.IsTopDistanceValid )
2816                                     aSetBox.SetDistance( nTopDistance, SvxBoxItemLine::TOP );
2817                                 if( aTableBorderDistances.IsBottomDistanceValid )
2818                                     aSetBox.SetDistance( nBottomDistance, SvxBoxItemLine::BOTTOM );
2819                                 pDoc->SetAttr( aSetBox, *pSetBoxFormat );
2820                             }
2821                         }
2822                     }
2823                     pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
2824                 }
2825                 break;
2826 
2827                 case FN_UNO_TABLE_COLUMN_SEPARATORS:
2828                 {
2829                     UnoActionContext aContext(pFormat->GetDoc());
2830                     SwTable* pTable = SwTable::FindTable( pFormat );
2831                     lcl_SetTableSeparators(aValue, pTable, pTable->GetTabLines()[0]->GetTabBoxes()[0], false, pFormat->GetDoc());
2832                 }
2833                 break;
2834 
2835                 case FN_UNO_TABLE_COLUMN_RELATIVE_SUM:/*_readonly_*/ break;
2836 
2837                 case FN_UNO_TABLE_TEMPLATE_NAME:
2838                 {
2839                     SwTable* pTable = SwTable::FindTable(pFormat);
2840                     OUString sName;
2841                     if (!(aValue >>= sName))
2842                         break;
2843                     pTable->SetTableStyleName(sName);
2844                     SwDoc* pDoc = pFormat->GetDoc();
2845                     pDoc->GetDocShell()->GetFEShell()->UpdateTableStyleFormatting(pTable->GetTableNode());
2846                 }
2847                 break;
2848 
2849                 default:
2850                 {
2851                     SwAttrSet aSet(pFormat->GetAttrSet());
2852                     m_pImpl->m_pPropSet->setPropertyValue(*pEntry, aValue, aSet);
2853                     pFormat->GetDoc()->SetAttr(aSet, *pFormat);
2854                 }
2855             }
2856         }
2857     }
2858     else if (m_pImpl->IsDescriptor())
2859     {
2860         m_pImpl->m_pTableProps->SetProperty(pEntry->nWID, pEntry->nMemberId, aValue);
2861     }
2862     else
2863         throw uno::RuntimeException();
2864 }
2865 
2866 uno::Any SwXTextTable::getPropertyValue(const OUString& rPropertyName)
2867 {
2868     SolarMutexGuard aGuard;
2869     uno::Any aRet;
2870     SwFrameFormat* pFormat = GetFrameFormat();
2871     const SfxItemPropertySimpleEntry* pEntry =
2872             m_pImpl->m_pPropSet->getPropertyMap().getByName(rPropertyName);
2873 
2874     if (!pEntry)
2875         throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
2876 
2877     if(pFormat)
2878     {
2879         if(0xBF == pEntry->nMemberId)
2880         {
2881             aRet = lcl_GetSpecialProperty(pFormat, pEntry );
2882         }
2883         else
2884         {
2885             switch(pEntry->nWID)
2886             {
2887                 case FN_UNO_TABLE_NAME:
2888                 {
2889                     aRet <<= getName();
2890                 }
2891                 break;
2892 
2893                 case  FN_UNO_ANCHOR_TYPES:
2894                 case  FN_UNO_TEXT_WRAP:
2895                 case  FN_UNO_ANCHOR_TYPE:
2896                     ::sw::GetDefaultTextContentValue(
2897                             aRet, OUString(), pEntry->nWID);
2898                 break;
2899 
2900                 case FN_UNO_RANGE_ROW_LABEL:
2901                 {
2902                     aRet <<= m_pImpl->m_bFirstRowAsLabel;
2903                 }
2904                 break;
2905 
2906                 case FN_UNO_RANGE_COL_LABEL:
2907                     aRet <<= m_pImpl->m_bFirstColumnAsLabel;
2908                 break;
2909 
2910                 case FN_UNO_TABLE_BORDER:
2911                 case FN_UNO_TABLE_BORDER2:
2912                 {
2913                     SwDoc* pDoc = pFormat->GetDoc();
2914                     // tables without layout (invisible header/footer?)
2915                     if(!lcl_FormatTable(pFormat))
2916                         break;
2917                     SwTable* pTable = SwTable::FindTable( pFormat );
2918                     SwTableLines &rLines = pTable->GetTabLines();
2919 
2920                     const SwTableBox* pTLBox = lcl_FindCornerTableBox(rLines, true);
2921                     const SwStartNode* pSttNd = pTLBox->GetSttNd();
2922                     SwPosition aPos(*pSttNd);
2923                     // set cursor to top left cell
2924                     auto pUnoCursor(pDoc->CreateUnoCursor(aPos, true));
2925                     pUnoCursor->Move( fnMoveForward, GoInNode );
2926                     pUnoCursor->SetRemainInSection( false );
2927 
2928                     const SwTableBox* pBRBox = lcl_FindCornerTableBox(rLines, false);
2929                     pUnoCursor->SetMark();
2930                     const SwStartNode* pLastNd = pBRBox->GetSttNd();
2931                     pUnoCursor->GetPoint()->nNode = *pLastNd;
2932 
2933                     pUnoCursor->Move( fnMoveForward, GoInNode );
2934                     SwUnoTableCursor& rCursor = dynamic_cast<SwUnoTableCursor&>(*pUnoCursor);
2935                     // HACK: remove pending actions for selecting old style tables
2936                     UnoActionRemoveContext aRemoveContext(rCursor);
2937                     rCursor.MakeBoxSels();
2938 
2939                     SfxItemSet aSet(pDoc->GetAttrPool(),
2940                                     svl::Items<RES_BOX, RES_BOX,
2941                                     SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>{});
2942                     aSet.Put(SvxBoxInfoItem( SID_ATTR_BORDER_INNER ));
2943                     SwDoc::GetTabBorders(rCursor, aSet);
2944                     const SvxBoxInfoItem& rBoxInfoItem = aSet.Get(SID_ATTR_BORDER_INNER);
2945                     const SvxBoxItem& rBox = aSet.Get(RES_BOX);
2946 
2947                     if (FN_UNO_TABLE_BORDER == pEntry->nWID)
2948                     {
2949                         table::TableBorder aTableBorder;
2950                         aTableBorder.TopLine                = SvxBoxItem::SvxLineToLine(rBox.GetTop(), true);
2951                         aTableBorder.IsTopLineValid         = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::TOP);
2952                         aTableBorder.BottomLine             = SvxBoxItem::SvxLineToLine(rBox.GetBottom(), true);
2953                         aTableBorder.IsBottomLineValid      = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::BOTTOM);
2954                         aTableBorder.LeftLine               = SvxBoxItem::SvxLineToLine(rBox.GetLeft(), true);
2955                         aTableBorder.IsLeftLineValid        = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::LEFT);
2956                         aTableBorder.RightLine              = SvxBoxItem::SvxLineToLine(rBox.GetRight(), true);
2957                         aTableBorder.IsRightLineValid       = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::RIGHT );
2958                         aTableBorder.HorizontalLine         = SvxBoxItem::SvxLineToLine(rBoxInfoItem.GetHori(), true);
2959                         aTableBorder.IsHorizontalLineValid  = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::HORI);
2960                         aTableBorder.VerticalLine           = SvxBoxItem::SvxLineToLine(rBoxInfoItem.GetVert(), true);
2961                         aTableBorder.IsVerticalLineValid    = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::VERT);
2962                         aTableBorder.Distance               = convertTwipToMm100(rBox.GetSmallestDistance());
2963                         aTableBorder.IsDistanceValid        = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::DISTANCE);
2964                         aRet <<= aTableBorder;
2965                     }
2966                     else
2967                     {
2968                         table::TableBorder2 aTableBorder;
2969                         aTableBorder.TopLine                = SvxBoxItem::SvxLineToLine(rBox.GetTop(), true);
2970                         aTableBorder.IsTopLineValid         = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::TOP);
2971                         aTableBorder.BottomLine             = SvxBoxItem::SvxLineToLine(rBox.GetBottom(), true);
2972                         aTableBorder.IsBottomLineValid      = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::BOTTOM);
2973                         aTableBorder.LeftLine               = SvxBoxItem::SvxLineToLine(rBox.GetLeft(), true);
2974                         aTableBorder.IsLeftLineValid        = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::LEFT);
2975                         aTableBorder.RightLine              = SvxBoxItem::SvxLineToLine(rBox.GetRight(), true);
2976                         aTableBorder.IsRightLineValid       = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::RIGHT );
2977                         aTableBorder.HorizontalLine         = SvxBoxItem::SvxLineToLine(rBoxInfoItem.GetHori(), true);
2978                         aTableBorder.IsHorizontalLineValid  = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::HORI);
2979                         aTableBorder.VerticalLine           = SvxBoxItem::SvxLineToLine(rBoxInfoItem.GetVert(), true);
2980                         aTableBorder.IsVerticalLineValid    = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::VERT);
2981                         aTableBorder.Distance               = convertTwipToMm100(rBox.GetSmallestDistance());
2982                         aTableBorder.IsDistanceValid        = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::DISTANCE);
2983                         aRet <<= aTableBorder;
2984                     }
2985                 }
2986                 break;
2987 
2988                 case FN_UNO_TABLE_BORDER_DISTANCES :
2989                 {
2990                     table::TableBorderDistances aTableBorderDistances( 0, true, 0, true, 0, true, 0, true ) ;
2991                     SwTable* pTable = SwTable::FindTable( pFormat );
2992                     const SwTableLines &rLines = pTable->GetTabLines();
2993                     bool bFirst = true;
2994                     sal_uInt16 nLeftDistance = 0;
2995                     sal_uInt16 nRightDistance = 0;
2996                     sal_uInt16 nTopDistance = 0;
2997                     sal_uInt16 nBottomDistance = 0;
2998 
2999                     for(size_t i = 0; i < rLines.size(); ++i)
3000                     {
3001                         const SwTableLine* pLine = rLines[i];
3002                         const SwTableBoxes& rBoxes = pLine->GetTabBoxes();
3003                         for(size_t k = 0; k < rBoxes.size(); ++k)
3004                         {
3005                             const SwTableBox* pBox = rBoxes[k];
3006                             SwFrameFormat* pBoxFormat = pBox->GetFrameFormat();
3007                             const SvxBoxItem& rBox = pBoxFormat->GetBox();
3008                             if( bFirst )
3009                             {
3010                                 nLeftDistance =     convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::LEFT   ));
3011                                 nRightDistance =    convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::RIGHT  ));
3012                                 nTopDistance =      convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::TOP    ));
3013                                 nBottomDistance =   convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::BOTTOM ));
3014                                 bFirst = false;
3015                             }
3016                             else
3017                             {
3018                                 if( aTableBorderDistances.IsLeftDistanceValid &&
3019                                     nLeftDistance != convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::LEFT   )))
3020                                     aTableBorderDistances.IsLeftDistanceValid = false;
3021                                 if( aTableBorderDistances.IsRightDistanceValid &&
3022                                     nRightDistance != convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::RIGHT   )))
3023                                     aTableBorderDistances.IsRightDistanceValid = false;
3024                                 if( aTableBorderDistances.IsTopDistanceValid &&
3025                                     nTopDistance != convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::TOP   )))
3026                                     aTableBorderDistances.IsTopDistanceValid = false;
3027                                 if( aTableBorderDistances.IsBottomDistanceValid &&
3028                                     nBottomDistance != convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::BOTTOM   )))
3029                                     aTableBorderDistances.IsBottomDistanceValid = false;
3030                             }
3031 
3032                         }
3033                         if( !aTableBorderDistances.IsLeftDistanceValid &&
3034                                 !aTableBorderDistances.IsRightDistanceValid &&
3035                                 !aTableBorderDistances.IsTopDistanceValid &&
3036                                 !aTableBorderDistances.IsBottomDistanceValid )
3037                             break;
3038                     }
3039                     if( aTableBorderDistances.IsLeftDistanceValid)
3040                         aTableBorderDistances.LeftDistance = nLeftDistance;
3041                     if( aTableBorderDistances.IsRightDistanceValid)
3042                         aTableBorderDistances.RightDistance  = nRightDistance;
3043                     if( aTableBorderDistances.IsTopDistanceValid)
3044                         aTableBorderDistances.TopDistance    = nTopDistance;
3045                     if( aTableBorderDistances.IsBottomDistanceValid)
3046                         aTableBorderDistances.BottomDistance = nBottomDistance;
3047 
3048                     aRet <<= aTableBorderDistances;
3049                 }
3050                 break;
3051 
3052                 case FN_UNO_TABLE_COLUMN_SEPARATORS:
3053                 {
3054                     SwTable* pTable = SwTable::FindTable( pFormat );
3055                     lcl_GetTableSeparators(aRet, pTable, pTable->GetTabLines()[0]->GetTabBoxes()[0], false);
3056                 }
3057                 break;
3058 
3059                 case FN_UNO_TABLE_COLUMN_RELATIVE_SUM:
3060                     aRet <<= sal_Int16(UNO_TABLE_COLUMN_SUM);
3061                 break;
3062 
3063                 case RES_ANCHOR:
3064                     // AnchorType is readonly and might be void (no return value)
3065                 break;
3066 
3067                 case FN_UNO_TEXT_SECTION:
3068                 {
3069                     SwTable* pTable = SwTable::FindTable( pFormat );
3070                     SwTableNode* pTableNode = pTable->GetTableNode();
3071                     SwSectionNode* pSectionNode =  pTableNode->FindSectionNode();
3072                     if(pSectionNode)
3073                     {
3074                         SwSection& rSect = pSectionNode->GetSection();
3075                         uno::Reference< text::XTextSection >  xSect =
3076                                         SwXTextSections::GetObject( *rSect.GetFormat() );
3077                         aRet <<= xSect;
3078                     }
3079                 }
3080                 break;
3081 
3082                 case FN_UNO_TABLE_TEMPLATE_NAME:
3083                 {
3084                     SwTable* pTable = SwTable::FindTable(pFormat);
3085                     aRet <<= pTable->GetTableStyleName();
3086                 }
3087                 break;
3088 
3089                 default:
3090                 {
3091                     const SwAttrSet& rSet = pFormat->GetAttrSet();
3092                     m_pImpl->m_pPropSet->getPropertyValue(*pEntry, rSet, aRet);
3093                 }
3094             }
3095         }
3096     }
3097     else if (m_pImpl->IsDescriptor())
3098     {
3099         const uno::Any* pAny = nullptr;
3100         if (!m_pImpl->m_pTableProps->GetProperty(pEntry->nWID, pEntry->nMemberId, pAny))
3101             throw lang::IllegalArgumentException();
3102         else if(pAny)
3103             aRet = *pAny;
3104     }
3105     else
3106         throw uno::RuntimeException();
3107     return aRet;
3108 }
3109 
3110 void SwXTextTable::addPropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
3111     { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
3112 
3113 void SwXTextTable::removePropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
3114     { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
3115 
3116 void SwXTextTable::addVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
3117     { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
3118 
3119 void SwXTextTable::removeVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
3120     { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
3121 
3122 OUString SwXTextTable::getName()
3123 {
3124     SolarMutexGuard aGuard;
3125     SwFrameFormat* pFormat = GetFrameFormat();
3126     if (!pFormat && !m_pImpl->IsDescriptor())
3127         throw uno::RuntimeException();
3128     if(pFormat)
3129     {
3130         return pFormat->GetName();
3131     }
3132     return m_pImpl->m_sTableName;
3133 }
3134 
3135 void SwXTextTable::setName(const OUString& rName)
3136 {
3137     SolarMutexGuard aGuard;
3138     SwFrameFormat* pFormat = GetFrameFormat();
3139     if ((!pFormat && !m_pImpl->IsDescriptor()) ||
3140        rName.isEmpty() ||
3141        rName.indexOf('.')>=0 ||
3142        rName.indexOf(' ')>=0 )
3143         throw uno::RuntimeException();
3144 
3145     if(pFormat)
3146     {
3147         const OUString aOldName( pFormat->GetName() );
3148         const SwFrameFormats* pFrameFormats = pFormat->GetDoc()->GetTableFrameFormats();
3149         for (size_t i = pFrameFormats->size(); i;)
3150         {
3151             const SwFrameFormat* pTmpFormat = (*pFrameFormats)[--i];
3152             if( !pTmpFormat->IsDefault() &&
3153                 pTmpFormat->GetName() == rName &&
3154                             pFormat->GetDoc()->IsUsed( *pTmpFormat ))
3155             {
3156                 throw uno::RuntimeException();
3157             }
3158         }
3159 
3160         pFormat->SetName( rName );
3161 
3162         SwStartNode *pStNd;
3163         SwNodeIndex aIdx( *pFormat->GetDoc()->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
3164         while ( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) )
3165         {
3166             ++aIdx;
3167             SwNode *const pNd = & aIdx.GetNode();
3168             if ( pNd->IsOLENode() &&
3169                 aOldName == static_cast<const SwOLENode*>(pNd)->GetChartTableName() )
3170             {
3171                 static_cast<SwOLENode*>(pNd)->SetChartTableName( rName );
3172 
3173                 SwTable* pTable = SwTable::FindTable( pFormat );
3174                 //TL_CHART2: chart needs to be notfied about name changes
3175                 pFormat->GetDoc()->UpdateCharts( pTable->GetFrameFormat()->GetName() );
3176             }
3177             aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
3178         }
3179         pFormat->GetDoc()->getIDocumentState().SetModified();
3180     }
3181     else
3182         m_pImpl->m_sTableName = rName;
3183 }
3184 
3185 sal_uInt16 SwXTextTable::Impl::GetRowCount()
3186 {
3187     sal_uInt16 nRet = 0;
3188     SwFrameFormat* pFormat = GetFrameFormat();
3189     if(pFormat)
3190     {
3191         SwTable* pTable = SwTable::FindTable( pFormat );
3192         if(!pTable->IsTableComplex())
3193         {
3194             nRet = pTable->GetTabLines().size();
3195         }
3196     }
3197     return nRet;
3198 }
3199 
3200 sal_uInt16 SwXTextTable::Impl::GetColumnCount()
3201 {
3202     SwFrameFormat* pFormat = GetFrameFormat();
3203     sal_uInt16 nRet = 0;
3204     if(pFormat)
3205     {
3206         SwTable* pTable = SwTable::FindTable( pFormat );
3207         if(!pTable->IsTableComplex())
3208         {
3209             SwTableLines& rLines = pTable->GetTabLines();
3210             SwTableLine* pLine = rLines.front();
3211             nRet = pLine->GetTabBoxes().size();
3212         }
3213     }
3214     return nRet;
3215 }
3216 
3217 void SwXTextTable::Impl::Notify(const SfxHint& rHint)
3218 {
3219     if(rHint.GetId() == SfxHintId::Dying)
3220     {
3221         m_pFrameFormat = nullptr;
3222         EndListeningAll();
3223     }
3224     uno::Reference<uno::XInterface> const xThis(m_wThis);
3225     if (xThis.is())
3226     {   // fdo#72695: if UNO object is already dead, don't revive it with event
3227         if(!m_pFrameFormat)
3228         {
3229             lang::EventObject const ev(xThis);
3230             m_Listeners.disposeAndClear(ev);
3231         }
3232         else
3233         {
3234             lcl_SendChartEvent(xThis.get(), m_Listeners);
3235         }
3236     }
3237 }
3238 
3239 OUString SAL_CALL SwXTextTable::getImplementationName()
3240     { return "SwXTextTable"; }
3241 
3242 sal_Bool SwXTextTable::supportsService(const OUString& rServiceName)
3243     { return cppu::supportsService(this, rServiceName); }
3244 
3245 uno::Sequence<OUString> SwXTextTable::getSupportedServiceNames()
3246 {
3247     return {
3248         "com.sun.star.document.LinkTarget",
3249         "com.sun.star.text.TextTable",
3250         "com.sun.star.text.TextContent",
3251         "com.sun.star.text.TextSortable" };
3252 }
3253 
3254 
3255 class SwXCellRange::Impl
3256     : public SvtListener
3257 {
3258 private:
3259     ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2
3260     SwFrameFormat* m_pFrameFormat;
3261 
3262 public:
3263     uno::WeakReference<uno::XInterface> m_wThis;
3264     ::comphelper::OInterfaceContainerHelper2 m_ChartListeners;
3265 
3266     sw::UnoCursorPointer m_pTableCursor;
3267 
3268     SwRangeDescriptor           m_RangeDescriptor;
3269     const SfxItemPropertySet*   m_pPropSet;
3270 
3271     bool m_bFirstRowAsLabel;
3272     bool m_bFirstColumnAsLabel;
3273 
3274     Impl(sw::UnoCursorPointer const& pCursor, SwFrameFormat& rFrameFormat, SwRangeDescriptor const& rDesc)
3275         : m_pFrameFormat(&rFrameFormat)
3276         , m_ChartListeners(m_Mutex)
3277         , m_pTableCursor(pCursor)
3278         , m_RangeDescriptor(rDesc)
3279         , m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TABLE_RANGE))
3280         , m_bFirstRowAsLabel(false)
3281         , m_bFirstColumnAsLabel(false)
3282     {
3283         StartListening(rFrameFormat.GetNotifier());
3284         m_RangeDescriptor.Normalize();
3285     }
3286 
3287     SwFrameFormat* GetFrameFormat()
3288     {
3289         return m_pFrameFormat;
3290     }
3291 
3292     std::tuple<sal_uInt32, sal_uInt32, sal_uInt32, sal_uInt32> GetLabelCoordinates(bool bRow);
3293 
3294     uno::Sequence<OUString> GetLabelDescriptions(SwXCellRange & rThis, bool bRow);
3295 
3296     void SetLabelDescriptions(SwXCellRange & rThis,
3297             const css::uno::Sequence<OUString>& rDesc, bool bRow);
3298 
3299     sal_Int32 GetRowCount() const;
3300     sal_Int32 GetColumnCount() const;
3301 
3302     virtual void Notify(const SfxHint& ) override;
3303 
3304 };
3305 
3306 namespace
3307 {
3308     class theSwXCellRangeUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXCellRangeUnoTunnelId > {};
3309 }
3310 
3311 const uno::Sequence< sal_Int8 > & SwXCellRange::getUnoTunnelId()
3312 {
3313     return theSwXCellRangeUnoTunnelId::get().getSeq();
3314 }
3315 
3316 sal_Int64 SAL_CALL SwXCellRange::getSomething( const uno::Sequence< sal_Int8 >& rId )
3317 {
3318     if( isUnoTunnelId<SwXCellRange>(rId) )
3319     {
3320         return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) );
3321     }
3322     return 0;
3323 }
3324 
3325 
3326 OUString SwXCellRange::getImplementationName()
3327     { return "SwXCellRange"; }
3328 
3329 sal_Bool SwXCellRange::supportsService(const OUString& rServiceName)
3330     { return cppu::supportsService(this, rServiceName); }
3331 
3332 uno::Sequence<OUString> SwXCellRange::getSupportedServiceNames()
3333 {
3334     return {
3335         "com.sun.star.text.CellRange",
3336         "com.sun.star.style.CharacterProperties",
3337         "com.sun.star.style.CharacterPropertiesAsian",
3338         "com.sun.star.style.CharacterPropertiesComplex",
3339         "com.sun.star.style.ParagraphProperties",
3340         "com.sun.star.style.ParagraphPropertiesAsian",
3341         "com.sun.star.style.ParagraphPropertiesComplex" };
3342 }
3343 
3344 SwXCellRange::SwXCellRange(sw::UnoCursorPointer const& pCursor,
3345         SwFrameFormat& rFrameFormat, SwRangeDescriptor const & rDesc)
3346     : m_pImpl(new Impl(pCursor, rFrameFormat, rDesc))
3347 {
3348 }
3349 
3350 SwXCellRange::~SwXCellRange()
3351 {
3352 }
3353 
3354 rtl::Reference<SwXCellRange> SwXCellRange::CreateXCellRange(
3355         sw::UnoCursorPointer const& pCursor, SwFrameFormat& rFrameFormat,
3356         SwRangeDescriptor const & rDesc)
3357 {
3358     SwXCellRange *const pCellRange(new SwXCellRange(pCursor, rFrameFormat, rDesc));
3359     uno::Reference<table::XCellRange> xCellRange(pCellRange);
3360     // need a permanent Reference to initialize m_wThis
3361     pCellRange->m_pImpl->m_wThis = xCellRange;
3362     return pCellRange;
3363 }
3364 
3365 void SwXCellRange::SetLabels(bool bFirstRowAsLabel, bool bFirstColumnAsLabel)
3366 {
3367     m_pImpl->m_bFirstRowAsLabel = bFirstRowAsLabel;
3368     m_pImpl->m_bFirstColumnAsLabel = bFirstColumnAsLabel;
3369 }
3370 
3371 std::vector< uno::Reference< table::XCell > > SwXCellRange::GetCells()
3372 {
3373     SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat();
3374     const sal_Int32 nRowCount(m_pImpl->GetRowCount());
3375     const sal_Int32 nColCount(m_pImpl->GetColumnCount());
3376     std::vector< uno::Reference< table::XCell > > vResult;
3377     vResult.reserve(static_cast<size_t>(nRowCount)*static_cast<size_t>(nColCount));
3378     for(sal_Int32 nRow = 0; nRow < nRowCount; ++nRow)
3379         for(sal_Int32 nCol = 0; nCol < nColCount; ++nCol)
3380             vResult.emplace_back(lcl_CreateXCell(pFormat, m_pImpl->m_RangeDescriptor.nLeft + nCol, m_pImpl->m_RangeDescriptor.nTop + nRow));
3381     return vResult;
3382 }
3383 
3384 uno::Reference<table::XCell> SAL_CALL
3385 SwXCellRange::getCellByPosition(sal_Int32 nColumn, sal_Int32 nRow)
3386 {
3387     SolarMutexGuard aGuard;
3388     uno::Reference< table::XCell >  aRet;
3389     SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat();
3390     if(pFormat)
3391     {
3392         if(nColumn >= 0 && nRow >= 0 &&
3393              m_pImpl->GetColumnCount() > nColumn && m_pImpl->GetRowCount() > nRow )
3394         {
3395             SwXCell* pXCell = lcl_CreateXCell(pFormat,
3396                     m_pImpl->m_RangeDescriptor.nLeft + nColumn,
3397                     m_pImpl->m_RangeDescriptor.nTop + nRow);
3398             if(pXCell)
3399                 aRet = pXCell;
3400         }
3401     }
3402     if(!aRet.is())
3403         throw lang::IndexOutOfBoundsException();
3404     return aRet;
3405 }
3406 
3407 uno::Reference<table::XCellRange> SAL_CALL
3408 SwXCellRange::getCellRangeByPosition(
3409         sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom)
3410 {
3411     SolarMutexGuard aGuard;
3412     uno::Reference< table::XCellRange >  aRet;
3413     SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat();
3414     if  (pFormat && m_pImpl->GetColumnCount() > nRight
3415         && m_pImpl->GetRowCount() > nBottom &&
3416         nLeft <= nRight && nTop <= nBottom
3417         && nLeft >= 0 && nRight >= 0 && nTop >= 0 && nBottom >= 0 )
3418     {
3419         SwTable* pTable = SwTable::FindTable( pFormat );
3420         if(!pTable->IsTableComplex())
3421         {
3422             SwRangeDescriptor aNewDesc;
3423             aNewDesc.nTop    = nTop + m_pImpl->m_RangeDescriptor.nTop;
3424             aNewDesc.nBottom = nBottom + m_pImpl->m_RangeDescriptor.nTop;
3425             aNewDesc.nLeft   = nLeft + m_pImpl->m_RangeDescriptor.nLeft;
3426             aNewDesc.nRight  = nRight + m_pImpl->m_RangeDescriptor.nLeft;
3427             aNewDesc.Normalize();
3428             const OUString sTLName = sw_GetCellName(aNewDesc.nLeft, aNewDesc.nTop);
3429             const OUString sBRName = sw_GetCellName(aNewDesc.nRight, aNewDesc.nBottom);
3430             const SwTableBox* pTLBox = pTable->GetTableBox( sTLName );
3431             if(pTLBox)
3432             {
3433                 const SwStartNode* pSttNd = pTLBox->GetSttNd();
3434                 SwPosition aPos(*pSttNd);
3435                 // set cursor in the upper-left cell of the range
3436                 auto pUnoCursor(pFormat->GetDoc()->CreateUnoCursor(aPos, true));
3437                 pUnoCursor->Move( fnMoveForward, GoInNode );
3438                 pUnoCursor->SetRemainInSection( false );
3439                 const SwTableBox* pBRBox = pTable->GetTableBox( sBRName );
3440                 if(pBRBox)
3441                 {
3442                     pUnoCursor->SetMark();
3443                     pUnoCursor->GetPoint()->nNode = *pBRBox->GetSttNd();
3444                     pUnoCursor->Move( fnMoveForward, GoInNode );
3445                     SwUnoTableCursor& rCursor = dynamic_cast<SwUnoTableCursor&>(*pUnoCursor);
3446                     // HACK: remove pending actions for selecting old style tables
3447                     UnoActionRemoveContext aRemoveContext(rCursor);
3448                     rCursor.MakeBoxSels();
3449                     // pUnoCursor will be provided and will not be deleted
3450                     aRet = SwXCellRange::CreateXCellRange(pUnoCursor, *pFormat, aNewDesc).get();
3451                 }
3452             }
3453         }
3454     }
3455     if(!aRet.is())
3456         throw lang::IndexOutOfBoundsException();
3457     return aRet;
3458 }
3459 
3460 uno::Reference<table::XCellRange> SAL_CALL
3461 SwXCellRange::getCellRangeByName(const OUString& rRange)
3462 {
3463     SolarMutexGuard aGuard;
3464     sal_Int32 nPos = 0;
3465     const OUString sTLName(rRange.getToken(0, ':', nPos));
3466     const OUString sBRName(rRange.getToken(0, ':', nPos));
3467     if(sTLName.isEmpty() || sBRName.isEmpty())
3468         throw uno::RuntimeException();
3469     SwRangeDescriptor aDesc;
3470     aDesc.nTop = aDesc.nLeft = aDesc.nBottom = aDesc.nRight = -1;
3471     SwXTextTable::GetCellPosition( sTLName, aDesc.nLeft, aDesc.nTop );
3472     SwXTextTable::GetCellPosition( sBRName, aDesc.nRight, aDesc.nBottom );
3473     aDesc.Normalize();
3474     return getCellRangeByPosition(
3475                 aDesc.nLeft - m_pImpl->m_RangeDescriptor.nLeft,
3476                 aDesc.nTop - m_pImpl->m_RangeDescriptor.nTop,
3477                 aDesc.nRight - m_pImpl->m_RangeDescriptor.nLeft,
3478                 aDesc.nBottom - m_pImpl->m_RangeDescriptor.nTop);
3479 }
3480 
3481 uno::Reference< beans::XPropertySetInfo >  SwXCellRange::getPropertySetInfo()
3482 {
3483     static uno::Reference<beans::XPropertySetInfo> xRef = m_pImpl->m_pPropSet->getPropertySetInfo();
3484     return xRef;
3485 }
3486 
3487 void SAL_CALL
3488 SwXCellRange::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue)
3489 {
3490     SolarMutexGuard aGuard;
3491     SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat();
3492     if(pFormat)
3493     {
3494         const SfxItemPropertySimpleEntry *const pEntry =
3495                 m_pImpl->m_pPropSet->getPropertyMap().getByName(rPropertyName);
3496         if(!pEntry)
3497             throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
3498 
3499         if ( pEntry->nFlags & beans::PropertyAttribute::READONLY)
3500             throw beans::PropertyVetoException("Property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
3501 
3502         SwDoc *const pDoc = m_pImpl->m_pTableCursor->GetDoc();
3503         SwUnoTableCursor& rCursor(dynamic_cast<SwUnoTableCursor&>(*m_pImpl->m_pTableCursor));
3504         {
3505             // HACK: remove pending actions for selecting old style tables
3506             UnoActionRemoveContext aRemoveContext(rCursor);
3507         }
3508         rCursor.MakeBoxSels();
3509         switch(pEntry->nWID )
3510         {
3511             case FN_UNO_TABLE_CELL_BACKGROUND:
3512             {
3513                 std::shared_ptr<SfxPoolItem> aBrush(std::make_shared<SvxBrushItem>(RES_BACKGROUND));
3514                 SwDoc::GetBoxAttr(*m_pImpl->m_pTableCursor, aBrush);
3515                 aBrush->PutValue(aValue, pEntry->nMemberId);
3516                 pDoc->SetBoxAttr(*m_pImpl->m_pTableCursor, *aBrush);
3517 
3518             }
3519             break;
3520             case RES_BOX :
3521             {
3522                 SfxItemSet aSet(pDoc->GetAttrPool(),
3523                                 svl::Items<RES_BOX, RES_BOX,
3524                                 SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>{});
3525                 SvxBoxInfoItem aBoxInfo( SID_ATTR_BORDER_INNER );
3526                 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::ALL, false);
3527                 SvxBoxInfoItemValidFlags nValid = SvxBoxInfoItemValidFlags::NONE;
3528                 switch(pEntry->nMemberId & ~CONVERT_TWIPS)
3529                 {
3530                     case  LEFT_BORDER :             nValid = SvxBoxInfoItemValidFlags::LEFT; break;
3531                     case  RIGHT_BORDER:             nValid = SvxBoxInfoItemValidFlags::RIGHT; break;
3532                     case  TOP_BORDER  :             nValid = SvxBoxInfoItemValidFlags::TOP; break;
3533                     case  BOTTOM_BORDER:            nValid = SvxBoxInfoItemValidFlags::BOTTOM; break;
3534                     case  LEFT_BORDER_DISTANCE :
3535                     case  RIGHT_BORDER_DISTANCE:
3536                     case  TOP_BORDER_DISTANCE  :
3537                     case  BOTTOM_BORDER_DISTANCE:
3538                         nValid = SvxBoxInfoItemValidFlags::DISTANCE;
3539                     break;
3540                 }
3541                 aBoxInfo.SetValid(nValid);
3542 
3543                 aSet.Put(aBoxInfo);
3544                 SwDoc::GetTabBorders(rCursor, aSet);
3545 
3546                 aSet.Put(aBoxInfo);
3547                 SvxBoxItem aBoxItem(aSet.Get(RES_BOX));
3548                 static_cast<SfxPoolItem&>(aBoxItem).PutValue(aValue, pEntry->nMemberId);
3549                 aSet.Put(aBoxItem);
3550                 pDoc->SetTabBorders(*m_pImpl->m_pTableCursor, aSet);
3551             }
3552             break;
3553             case RES_BOXATR_FORMAT:
3554             {
3555                 SfxUInt32Item aNumberFormat(RES_BOXATR_FORMAT);
3556                 static_cast<SfxPoolItem&>(aNumberFormat).PutValue(aValue, 0);
3557                 pDoc->SetBoxAttr(rCursor, aNumberFormat);
3558             }
3559             break;
3560             case FN_UNO_RANGE_ROW_LABEL:
3561             {
3562                 bool bTmp = *o3tl::doAccess<bool>(aValue);
3563                 if (m_pImpl->m_bFirstRowAsLabel != bTmp)
3564                 {
3565                     lcl_SendChartEvent(*this, m_pImpl->m_ChartListeners);
3566                     m_pImpl->m_bFirstRowAsLabel = bTmp;
3567                 }
3568             }
3569             break;
3570             case FN_UNO_RANGE_COL_LABEL:
3571             {
3572                 bool bTmp = *o3tl::doAccess<bool>(aValue);
3573                 if (m_pImpl->m_bFirstColumnAsLabel != bTmp)
3574                 {
3575                     lcl_SendChartEvent(*this, m_pImpl->m_ChartListeners);
3576                     m_pImpl->m_bFirstColumnAsLabel = bTmp;
3577                 }
3578             }
3579             break;
3580             case RES_VERT_ORIENT:
3581             {
3582                 sal_Int16 nAlign = -1;
3583                 aValue >>= nAlign;
3584                 if( nAlign >= text::VertOrientation::NONE && nAlign <= text::VertOrientation::BOTTOM)
3585                     pDoc->SetBoxAlign( rCursor, nAlign );
3586             }
3587             break;
3588             default:
3589             {
3590                 SfxItemSet aItemSet( pDoc->GetAttrPool(), {{pEntry->nWID, pEntry->nWID}} );
3591                 SwUnoCursorHelper::GetCursorAttr(rCursor.GetSelRing(),
3592                         aItemSet);
3593 
3594                 if (!SwUnoCursorHelper::SetCursorPropertyValue(
3595                         *pEntry, aValue, rCursor.GetSelRing(), aItemSet))
3596                 {
3597                     m_pImpl->m_pPropSet->setPropertyValue(*pEntry, aValue, aItemSet);
3598                 }
3599                 SwUnoCursorHelper::SetCursorAttr(rCursor.GetSelRing(),
3600                         aItemSet, SetAttrMode::DEFAULT, true);
3601             }
3602         }
3603 
3604     }
3605 }
3606 
3607 uno::Any SAL_CALL SwXCellRange::getPropertyValue(const OUString& rPropertyName)
3608 {
3609     SolarMutexGuard aGuard;
3610     uno::Any aRet;
3611     SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat();
3612     if(pFormat)
3613     {
3614         const SfxItemPropertySimpleEntry *const pEntry =
3615             m_pImpl->m_pPropSet->getPropertyMap().getByName(rPropertyName);
3616         if(!pEntry)
3617            throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
3618 
3619         switch(pEntry->nWID )
3620         {
3621             case FN_UNO_TABLE_CELL_BACKGROUND:
3622             {
3623                 std::shared_ptr<SfxPoolItem> aBrush(std::make_shared<SvxBrushItem>(RES_BACKGROUND));
3624                 if (SwDoc::GetBoxAttr(*m_pImpl->m_pTableCursor, aBrush))
3625                     aBrush->QueryValue(aRet, pEntry->nMemberId);
3626 
3627             }
3628             break;
3629             case RES_BOX :
3630             {
3631                 SwDoc *const pDoc = m_pImpl->m_pTableCursor->GetDoc();
3632                 SfxItemSet aSet(pDoc->GetAttrPool(),
3633                                 svl::Items<RES_BOX, RES_BOX,
3634                                 SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>{});
3635                 aSet.Put(SvxBoxInfoItem( SID_ATTR_BORDER_INNER ));
3636                 SwDoc::GetTabBorders(*m_pImpl->m_pTableCursor, aSet);
3637                 const SvxBoxItem& rBoxItem = aSet.Get(RES_BOX);
3638                 rBoxItem.QueryValue(aRet, pEntry->nMemberId);
3639             }
3640             break;
3641             case RES_BOXATR_FORMAT:
3642                 OSL_FAIL("not implemented");
3643             break;
3644             case FN_UNO_PARA_STYLE:
3645             {
3646                 SwFormatColl *const pTmpFormat =
3647                     SwUnoCursorHelper::GetCurTextFormatColl(*m_pImpl->m_pTableCursor, false);
3648                 OUString sRet;
3649                 if (pTmpFormat)
3650                     sRet = pTmpFormat->GetName();
3651                 aRet <<= sRet;
3652             }
3653             break;
3654             case FN_UNO_RANGE_ROW_LABEL:
3655                 aRet <<= m_pImpl->m_bFirstRowAsLabel;
3656             break;
3657             case FN_UNO_RANGE_COL_LABEL:
3658                 aRet <<= m_pImpl->m_bFirstColumnAsLabel;
3659             break;
3660             case RES_VERT_ORIENT:
3661             {
3662                 std::shared_ptr<SfxPoolItem> aVertOrient;
3663                 if (SwDoc::GetBoxAttr(*m_pImpl->m_pTableCursor, aVertOrient))
3664                 {
3665                     aVertOrient->QueryValue( aRet, pEntry->nMemberId );
3666                 }
3667             }
3668             break;
3669             default:
3670             {
3671                 SfxItemSet aSet(
3672                     m_pImpl->m_pTableCursor->GetDoc()->GetAttrPool(),
3673                     svl::Items<
3674                         RES_CHRATR_BEGIN, RES_FRMATR_END - 1,
3675                         RES_UNKNOWNATR_CONTAINER,
3676                             RES_UNKNOWNATR_CONTAINER>{});
3677                 // first look at the attributes of the cursor
3678                 SwUnoTableCursor *const pCursor =
3679                     dynamic_cast<SwUnoTableCursor*>(&(*m_pImpl->m_pTableCursor));
3680                 SwUnoCursorHelper::GetCursorAttr(pCursor->GetSelRing(), aSet);
3681                 m_pImpl->m_pPropSet->getPropertyValue(*pEntry, aSet, aRet);
3682             }
3683         }
3684 
3685     }
3686     return aRet;
3687 }
3688 
3689 void SwXCellRange::addPropertyChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/)
3690     { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
3691 
3692 void SwXCellRange::removePropertyChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/)
3693     { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
3694 
3695 void SwXCellRange::addVetoableChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
3696     { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
3697 
3698 void SwXCellRange::removeVetoableChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
3699     { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
3700 
3701 ///@see SwXCellRange::getData
3702 uno::Sequence<uno::Sequence<uno::Any>> SAL_CALL SwXCellRange::getDataArray()
3703 {
3704     SolarMutexGuard aGuard;
3705     const sal_Int32 nRowCount = m_pImpl->GetRowCount();
3706     const sal_Int32 nColCount = m_pImpl->GetColumnCount();
3707     if(!nRowCount || !nColCount)
3708         throw uno::RuntimeException("Table too complex", static_cast<cppu::OWeakObject*>(this));
3709     lcl_EnsureCoreConnected(m_pImpl->GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
3710     uno::Sequence< uno::Sequence< uno::Any > > aRowSeq(nRowCount);
3711     auto vCells(GetCells());
3712     auto pCurrentCell(vCells.begin());
3713     for(auto& rRow : aRowSeq)
3714     {
3715         rRow = uno::Sequence< uno::Any >(nColCount);
3716         for(auto& rCellAny : rRow)
3717         {
3718             auto pCell(static_cast<SwXCell*>(pCurrentCell->get()));
3719             if(!pCell)
3720                 throw uno::RuntimeException("Table too complex", static_cast<cppu::OWeakObject*>(this));
3721             rCellAny = pCell->GetAny();
3722             ++pCurrentCell;
3723         }
3724     }
3725     return aRowSeq;
3726 }
3727 
3728 ///@see SwXCellRange::setData
3729 void SAL_CALL SwXCellRange::setDataArray(const uno::Sequence< uno::Sequence< uno::Any > >& rArray)
3730 {
3731     SolarMutexGuard aGuard;
3732     const sal_Int32 nRowCount = m_pImpl->GetRowCount();
3733     const sal_Int32 nColCount = m_pImpl->GetColumnCount();
3734     if(!nRowCount || !nColCount)
3735         throw uno::RuntimeException("Table too complex", static_cast<cppu::OWeakObject*>(this));
3736     SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat();
3737     if(!pFormat)
3738         return;
3739     if(rArray.getLength() != nRowCount)
3740         throw uno::RuntimeException("Row count mismatch. expected: " + OUString::number(nRowCount) + " got: " + OUString::number(rArray.getLength()), static_cast<cppu::OWeakObject*>(this));
3741     auto vCells(GetCells());
3742     auto pCurrentCell(vCells.begin());
3743     for(const auto& rColSeq : rArray)
3744     {
3745         if(rColSeq.getLength() != nColCount)
3746             throw uno::RuntimeException("Column count mismatch. expected: " + OUString::number(nColCount) + " got: " + OUString::number(rColSeq.getLength()), static_cast<cppu::OWeakObject*>(this));
3747         for(const auto& aValue : rColSeq)
3748         {
3749             auto pCell(static_cast<SwXCell*>(pCurrentCell->get()));
3750             if(!pCell || !pCell->GetTableBox())
3751                 throw uno::RuntimeException("Box for cell missing", static_cast<cppu::OWeakObject*>(this));
3752             if(aValue.isExtractableTo(cppu::UnoType<OUString>::get()))
3753                 sw_setString(*pCell, aValue.get<OUString>());
3754             else if(aValue.isExtractableTo(cppu::UnoType<double>::get()))
3755                 sw_setValue(*pCell, aValue.get<double>());
3756             else
3757                 sw_setString(*pCell, OUString(), true);
3758             ++pCurrentCell;
3759         }
3760     }
3761 }
3762 
3763 uno::Sequence<uno::Sequence<double>> SAL_CALL
3764 SwXCellRange::getData()
3765 {
3766     SolarMutexGuard aGuard;
3767     const sal_Int32 nRowCount = m_pImpl->GetRowCount();
3768     const sal_Int32 nColCount = m_pImpl->GetColumnCount();
3769     if(!nRowCount || !nColCount)
3770         throw uno::RuntimeException("Table too complex", static_cast<cppu::OWeakObject*>(this));
3771     if (m_pImpl->m_bFirstColumnAsLabel || m_pImpl->m_bFirstRowAsLabel)
3772     {
3773         uno::Reference<chart::XChartDataArray> const xDataRange(
3774                 getCellRangeByPosition((m_pImpl->m_bFirstColumnAsLabel) ? 1 : 0,
3775                                        (m_pImpl->m_bFirstRowAsLabel) ? 1 : 0,
3776             nColCount-1, nRowCount-1), uno::UNO_QUERY_THROW);
3777         return xDataRange->getData();
3778     }
3779     uno::Sequence< uno::Sequence< double > > vRows(nRowCount);
3780     auto vCells(GetCells());
3781     auto pCurrentCell(vCells.begin());
3782     for(auto& rRow : vRows)
3783     {
3784         rRow = uno::Sequence<double>(nColCount);
3785         for(auto& rValue : rRow)
3786         {
3787             if(!(*pCurrentCell))
3788                 throw uno::RuntimeException("Table too complex", static_cast<cppu::OWeakObject*>(this));
3789             rValue = (*pCurrentCell)->getValue();
3790             ++pCurrentCell;
3791         }
3792     }
3793     return vRows;
3794 }
3795 
3796 void SAL_CALL
3797 SwXCellRange::setData(const uno::Sequence< uno::Sequence<double> >& rData)
3798 {
3799     SolarMutexGuard aGuard;
3800     const sal_Int32 nRowCount = m_pImpl->GetRowCount();
3801     const sal_Int32 nColCount = m_pImpl->GetColumnCount();
3802     if(!nRowCount || !nColCount)
3803         throw uno::RuntimeException("Table too complex", static_cast<cppu::OWeakObject*>(this));
3804     if (m_pImpl->m_bFirstColumnAsLabel || m_pImpl->m_bFirstRowAsLabel)
3805     {
3806         uno::Reference<chart::XChartDataArray> const xDataRange(
3807                 getCellRangeByPosition((m_pImpl->m_bFirstColumnAsLabel) ? 1 : 0,
3808                                        (m_pImpl->m_bFirstRowAsLabel) ? 1 : 0,
3809             nColCount-1, nRowCount-1), uno::UNO_QUERY_THROW);
3810         return xDataRange->setData(rData);
3811     }
3812     lcl_EnsureCoreConnected(m_pImpl->GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
3813     if(rData.getLength() != nRowCount)
3814         throw uno::RuntimeException("Row count mismatch. expected: " + OUString::number(nRowCount) + " got: " + OUString::number(rData.getLength()), static_cast<cppu::OWeakObject*>(this));
3815     auto vCells(GetCells());
3816     auto pCurrentCell(vCells.begin());
3817     for(const auto& rRow : rData)
3818     {
3819         if(rRow.getLength() != nColCount)
3820             throw uno::RuntimeException("Column count mismatch. expected: " + OUString::number(nColCount) + " got: " + OUString::number(rRow.getLength()), static_cast<cppu::OWeakObject*>(this));
3821         for(const auto& rValue : rRow)
3822         {
3823             uno::Reference<table::XCell>(*pCurrentCell, uno::UNO_SET_THROW)->setValue(rValue);
3824             ++pCurrentCell;
3825         }
3826     }
3827 }
3828 
3829 std::tuple<sal_uInt32, sal_uInt32, sal_uInt32, sal_uInt32>
3830 SwXCellRange::Impl::GetLabelCoordinates(bool bRow)
3831 {
3832     sal_uInt32 nLeft, nTop, nRight, nBottom;
3833     nLeft = nTop = nRight = nBottom = 0;
3834     if(bRow)
3835     {
3836         nTop = m_bFirstRowAsLabel ? 1 : 0;
3837         nBottom = GetRowCount() - 1;
3838     }
3839     else
3840     {
3841         nLeft = m_bFirstColumnAsLabel ? 1 : 0;
3842         nRight = GetColumnCount() - 1;
3843     }
3844     return std::make_tuple(nLeft, nTop, nRight, nBottom);
3845 }
3846 
3847 uno::Sequence<OUString>
3848 SwXCellRange::Impl::GetLabelDescriptions(SwXCellRange & rThis, bool bRow)
3849 {
3850     SolarMutexGuard aGuard;
3851     sal_uInt32 nLeft, nTop, nRight, nBottom;
3852     std::tie(nLeft, nTop, nRight, nBottom) = GetLabelCoordinates(bRow);
3853     if(!nRight && !nBottom)
3854         throw uno::RuntimeException("Table too complex", static_cast<cppu::OWeakObject*>(&rThis));
3855     lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(&rThis));
3856     if (!(bRow ? m_bFirstColumnAsLabel : m_bFirstRowAsLabel))
3857         return {};  // without labels we have no descriptions
3858     auto xLabelRange(rThis.getCellRangeByPosition(nLeft, nTop, nRight, nBottom));
3859     auto vCells(static_cast<SwXCellRange*>(xLabelRange.get())->GetCells());
3860     uno::Sequence<OUString> vResult(vCells.size());
3861     std::transform(vCells.begin(), vCells.end(), vResult.begin(),
3862         [](uno::Reference<table::XCell> xCell) -> OUString { return uno::Reference<text::XText>(xCell, uno::UNO_QUERY_THROW)->getString(); });
3863     return vResult;
3864 }
3865 
3866 uno::Sequence<OUString> SAL_CALL SwXCellRange::getRowDescriptions()
3867 {
3868     return m_pImpl->GetLabelDescriptions(*this, true);
3869 }
3870 
3871 uno::Sequence<OUString> SAL_CALL SwXCellRange::getColumnDescriptions()
3872 {
3873     return m_pImpl->GetLabelDescriptions(*this, false);
3874 }
3875 
3876 void SwXCellRange::Impl::SetLabelDescriptions(SwXCellRange & rThis,
3877         const uno::Sequence<OUString>& rDesc, bool bRow)
3878 {
3879     SolarMutexGuard aGuard;
3880     lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(&rThis));
3881     if (!(bRow ? m_bFirstColumnAsLabel : m_bFirstRowAsLabel))
3882         return; // if there are no labels we cannot set descriptions
3883     sal_uInt32 nLeft, nTop, nRight, nBottom;
3884     std::tie(nLeft, nTop, nRight, nBottom) = GetLabelCoordinates(bRow);
3885     if(!nRight && !nBottom)
3886         throw uno::RuntimeException("Table too complex", static_cast<cppu::OWeakObject*>(&rThis));
3887     auto xLabelRange(rThis.getCellRangeByPosition(nLeft, nTop, nRight, nBottom));
3888     if (!xLabelRange.is())
3889         throw uno::RuntimeException("Missing Cell Range", static_cast<cppu::OWeakObject*>(&rThis));
3890     auto vCells(static_cast<SwXCellRange*>(xLabelRange.get())->GetCells());
3891     if (sal::static_int_cast<sal_uInt32>(rDesc.getLength()) != vCells.size())
3892         throw uno::RuntimeException("Too few or too many descriptions", static_cast<cppu::OWeakObject*>(&rThis));
3893     auto pDescIterator(rDesc.begin());
3894     for(auto& xCell : vCells)
3895         uno::Reference<text::XText>(xCell, uno::UNO_QUERY_THROW)->setString(*pDescIterator++);
3896 }
3897 
3898 void SAL_CALL SwXCellRange::setRowDescriptions(
3899         const uno::Sequence<OUString>& rRowDesc)
3900 {
3901     m_pImpl->SetLabelDescriptions(*this, rRowDesc, true);
3902 }
3903 
3904 void SAL_CALL SwXCellRange::setColumnDescriptions(
3905         const uno::Sequence<OUString>& rColumnDesc)
3906 {
3907     m_pImpl->SetLabelDescriptions(*this, rColumnDesc, false);
3908 }
3909 
3910 void SAL_CALL SwXCellRange::addChartDataChangeEventListener(
3911         const uno::Reference<chart::XChartDataChangeEventListener> & xListener)
3912 {
3913     // no need to lock here as m_pImpl is const and container threadsafe
3914     m_pImpl->m_ChartListeners.addInterface(xListener);
3915 }
3916 
3917 void SAL_CALL SwXCellRange::removeChartDataChangeEventListener(
3918         const uno::Reference<chart::XChartDataChangeEventListener> & xListener)
3919 {
3920     // no need to lock here as m_pImpl is const and container threadsafe
3921     m_pImpl->m_ChartListeners.removeInterface(xListener);
3922 }
3923 
3924 sal_Bool SwXCellRange::isNotANumber(double /*fNumber*/)
3925     { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
3926 
3927 double SwXCellRange::getNotANumber()
3928     { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
3929 
3930 uno::Sequence< beans::PropertyValue > SwXCellRange::createSortDescriptor()
3931 {
3932     SolarMutexGuard aGuard;
3933     return SwUnoCursorHelper::CreateSortDescriptor(true);
3934 }
3935 
3936 void SAL_CALL SwXCellRange::sort(const uno::Sequence< beans::PropertyValue >& rDescriptor)
3937 {
3938     SolarMutexGuard aGuard;
3939     SwSortOptions aSortOpt;
3940     SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat();
3941     if(pFormat && SwUnoCursorHelper::ConvertSortProperties(rDescriptor, aSortOpt))
3942     {
3943         SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(*m_pImpl->m_pTableCursor);
3944         rTableCursor.MakeBoxSels();
3945         UnoActionContext aContext(pFormat->GetDoc());
3946         pFormat->GetDoc()->SortTable(rTableCursor.GetSelectedBoxes(), aSortOpt);
3947     }
3948 }
3949 
3950 sal_Int32 SwXCellRange::Impl::GetColumnCount() const
3951 {
3952     return m_RangeDescriptor.nRight - m_RangeDescriptor.nLeft + 1;
3953 }
3954 
3955 sal_Int32 SwXCellRange::Impl::GetRowCount() const
3956 {
3957     return m_RangeDescriptor.nBottom - m_RangeDescriptor.nTop + 1;
3958 }
3959 
3960 const SwUnoCursor* SwXCellRange::GetTableCursor() const
3961 {
3962     SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat();
3963     return pFormat ? &(*m_pImpl->m_pTableCursor) : nullptr;
3964 }
3965 
3966 void SwXCellRange::Impl::Notify( const SfxHint& rHint )
3967 {
3968     uno::Reference<uno::XInterface> const xThis(m_wThis);
3969     if(rHint.GetId() == SfxHintId::Dying)
3970     {
3971         m_pFrameFormat = nullptr;
3972         m_pTableCursor.reset(nullptr);
3973     }
3974     if (xThis.is())
3975     {   // fdo#72695: if UNO object is already dead, don't revive it with event
3976         if(m_pFrameFormat)
3977             lcl_SendChartEvent(xThis.get(), m_ChartListeners);
3978         else
3979             m_ChartListeners.disposeAndClear(lang::EventObject(xThis));
3980     }
3981 }
3982 
3983 class SwXTableRows::Impl : public SvtListener
3984 {
3985 private:
3986     SwFrameFormat* m_pFrameFormat;
3987 
3988 public:
3989     explicit Impl(SwFrameFormat& rFrameFormat) : m_pFrameFormat(&rFrameFormat)
3990     {
3991         StartListening(rFrameFormat.GetNotifier());
3992     }
3993     SwFrameFormat* GetFrameFormat() { return m_pFrameFormat; }
3994     virtual void Notify(const SfxHint&) override;
3995 };
3996 
3997 //  SwXTableRows
3998 
3999 OUString SwXTableRows::getImplementationName()
4000     { return "SwXTableRows"; }
4001 
4002 sal_Bool SwXTableRows::supportsService(const OUString& rServiceName)
4003     { return cppu::supportsService(this, rServiceName); }
4004 
4005 uno::Sequence< OUString > SwXTableRows::getSupportedServiceNames()
4006     { return { "com.sun.star.text.TableRows" }; }
4007 
4008 
4009 SwXTableRows::SwXTableRows(SwFrameFormat& rFrameFormat) :
4010     m_pImpl(new SwXTableRows::Impl(rFrameFormat))
4011 { }
4012 
4013 SwXTableRows::~SwXTableRows()
4014 { }
4015 
4016 SwFrameFormat* SwXTableRows::GetFrameFormat()
4017 {
4018     return m_pImpl->GetFrameFormat();
4019 }
4020 
4021 sal_Int32 SwXTableRows::getCount()
4022 {
4023     SolarMutexGuard aGuard;
4024     SwFrameFormat* pFrameFormat = GetFrameFormat();
4025     if(!pFrameFormat)
4026         throw uno::RuntimeException();
4027     SwTable* pTable = SwTable::FindTable(pFrameFormat);
4028     return pTable->GetTabLines().size();
4029 }
4030 
4031 ///@see SwXCell::CreateXCell (TODO: seems to be copy and paste programming here)
4032 uno::Any SwXTableRows::getByIndex(sal_Int32 nIndex)
4033 {
4034     SolarMutexGuard aGuard;
4035     SwFrameFormat* pFrameFormat(lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this)));
4036     if(nIndex < 0)
4037         throw lang::IndexOutOfBoundsException();
4038     SwTable* pTable = SwTable::FindTable( pFrameFormat );
4039     if(static_cast<size_t>(nIndex) >= pTable->GetTabLines().size())
4040         throw lang::IndexOutOfBoundsException();
4041     SwTableLine* pLine = pTable->GetTabLines()[nIndex];
4042     FindUnoInstanceHint<SwTableLine,SwXTextTableRow> aHint{pLine};
4043     pFrameFormat->GetNotifier().Broadcast(aHint);
4044     if(!aHint.m_pResult)
4045         aHint.m_pResult = new SwXTextTableRow(pFrameFormat, pLine);
4046     uno::Reference<beans::XPropertySet> xRet = static_cast<beans::XPropertySet*>(aHint.m_pResult);
4047     return uno::makeAny(xRet);
4048 }
4049 
4050 uno::Type SAL_CALL SwXTableRows::getElementType()
4051 {
4052     return cppu::UnoType<beans::XPropertySet>::get();
4053 }
4054 
4055 sal_Bool SwXTableRows::hasElements()
4056 {
4057     SolarMutexGuard aGuard;
4058     SwFrameFormat* pFrameFormat = GetFrameFormat();
4059     if(!pFrameFormat)
4060         throw uno::RuntimeException();
4061     // a table always has rows
4062     return true;
4063 }
4064 
4065 void SwXTableRows::insertByIndex(sal_Int32 nIndex, sal_Int32 nCount)
4066 {
4067     SolarMutexGuard aGuard;
4068     if (nCount == 0)
4069         return;
4070     SwFrameFormat* pFrameFormat(lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this)));
4071     SwTable* pTable = lcl_EnsureTableNotComplex(SwTable::FindTable(pFrameFormat), static_cast<cppu::OWeakObject*>(this));
4072     const size_t nRowCount = pTable->GetTabLines().size();
4073     if (nCount <= 0 || !(0 <= nIndex && static_cast<size_t>(nIndex) <= nRowCount))
4074         throw uno::RuntimeException("Illegal arguments", static_cast<cppu::OWeakObject*>(this));
4075     const OUString sTLName = sw_GetCellName(0, nIndex);
4076     const SwTableBox* pTLBox = pTable->GetTableBox(sTLName);
4077     bool bAppend = false;
4078     if(!pTLBox)
4079     {
4080         bAppend = true;
4081         // to append at the end the cursor must be in the last line
4082         SwTableLines& rLines = pTable->GetTabLines();
4083         SwTableLine* pLine = rLines.back();
4084         SwTableBoxes& rBoxes = pLine->GetTabBoxes();
4085         pTLBox = rBoxes.front();
4086     }
4087     if(!pTLBox)
4088         throw uno::RuntimeException("Illegal arguments", static_cast<cppu::OWeakObject*>(this));
4089     const SwStartNode* pSttNd = pTLBox->GetSttNd();
4090     SwPosition aPos(*pSttNd);
4091     // set cursor to the upper-left cell of the range
4092     UnoActionContext aAction(pFrameFormat->GetDoc());
4093     std::shared_ptr<SwUnoTableCursor> const pUnoCursor(
4094             std::dynamic_pointer_cast<SwUnoTableCursor>(
4095                 pFrameFormat->GetDoc()->CreateUnoCursor(aPos, true)));
4096     pUnoCursor->Move( fnMoveForward, GoInNode );
4097     {
4098         // remove actions - TODO: why?
4099         UnoActionRemoveContext aRemoveContext(pUnoCursor->GetDoc());
4100     }
4101     pFrameFormat->GetDoc()->InsertRow(*pUnoCursor, static_cast<sal_uInt16>(nCount), bAppend);
4102 }
4103 
4104 void SwXTableRows::removeByIndex(sal_Int32 nIndex, sal_Int32 nCount)
4105 {
4106     SolarMutexGuard aGuard;
4107     if (nCount == 0)
4108         return;
4109     SwFrameFormat* pFrameFormat(lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this)));
4110     if(nIndex < 0 || nCount <=0 )
4111         throw uno::RuntimeException();
4112     SwTable* pTable = lcl_EnsureTableNotComplex(SwTable::FindTable(pFrameFormat), static_cast<cppu::OWeakObject*>(this));
4113     OUString sTLName = sw_GetCellName(0, nIndex);
4114     const SwTableBox* pTLBox = pTable->GetTableBox(sTLName);
4115     if(!pTLBox)
4116         throw uno::RuntimeException("Illegal arguments", static_cast<cppu::OWeakObject*>(this));
4117     const SwStartNode* pSttNd = pTLBox->GetSttNd();
4118     SwPosition aPos(*pSttNd);
4119     // set cursor to the upper-left cell of the range
4120     auto pUnoCursor(pFrameFormat->GetDoc()->CreateUnoCursor(aPos, true));
4121     pUnoCursor->Move(fnMoveForward, GoInNode);
4122     pUnoCursor->SetRemainInSection( false );
4123     const OUString sBLName = sw_GetCellName(0, nIndex + nCount - 1);
4124     const SwTableBox* pBLBox = pTable->GetTableBox( sBLName );
4125     if(!pBLBox)
4126         throw uno::RuntimeException("Illegal arguments", static_cast<cppu::OWeakObject*>(this));
4127     pUnoCursor->SetMark();
4128     pUnoCursor->GetPoint()->nNode = *pBLBox->GetSttNd();
4129     pUnoCursor->Move(fnMoveForward, GoInNode);
4130     SwUnoTableCursor& rCursor = dynamic_cast<SwUnoTableCursor&>(*pUnoCursor);
4131     {
4132         // HACK: remove pending actions for selecting old style tables
4133         UnoActionRemoveContext aRemoveContext(rCursor);
4134     }
4135     rCursor.MakeBoxSels();
4136     {   // these braces are important
4137         UnoActionContext aAction(pFrameFormat->GetDoc());
4138         pFrameFormat->GetDoc()->DeleteRow(*pUnoCursor);
4139         pUnoCursor.reset();
4140     }
4141     {
4142         // invalidate all actions - TODO: why?
4143         UnoActionRemoveContext aRemoveContext(pFrameFormat->GetDoc());
4144     }
4145 }
4146 
4147 void SwXTableRows::Impl::Notify( const SfxHint& rHint)
4148 {
4149     if(rHint.GetId() == SfxHintId::Dying)
4150         m_pFrameFormat = nullptr;
4151 }
4152 
4153 // SwXTableColumns
4154 
4155 class SwXTableColumns::Impl : public SvtListener
4156 {
4157     SwFrameFormat* m_pFrameFormat;
4158     public:
4159         explicit Impl(SwFrameFormat& rFrameFormat) : m_pFrameFormat(&rFrameFormat)
4160         {
4161             StartListening(rFrameFormat.GetNotifier());
4162         }
4163         SwFrameFormat* GetFrameFormat() { return m_pFrameFormat; }
4164         virtual void Notify(const SfxHint&) override;
4165 };
4166 
4167 OUString SwXTableColumns::getImplementationName()
4168     { return "SwXTableColumns"; }
4169 
4170 sal_Bool SwXTableColumns::supportsService(const OUString& rServiceName)
4171     { return cppu::supportsService(this, rServiceName); }
4172 
4173 uno::Sequence< OUString > SwXTableColumns::getSupportedServiceNames()
4174     { return { "com.sun.star.text.TableColumns"}; }
4175 
4176 
4177 SwXTableColumns::SwXTableColumns(SwFrameFormat& rFrameFormat) :
4178     m_pImpl(new SwXTableColumns::Impl(rFrameFormat))
4179 { }
4180 
4181 SwXTableColumns::~SwXTableColumns()
4182 { }
4183 
4184 SwFrameFormat* SwXTableColumns::GetFrameFormat() const
4185 {
4186     return m_pImpl->GetFrameFormat();
4187 }
4188 
4189 sal_Int32 SwXTableColumns::getCount()
4190 {
4191     SolarMutexGuard aGuard;
4192     SwFrameFormat* pFrameFormat(lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this)));
4193     SwTable* pTable = SwTable::FindTable( pFrameFormat );
4194 //    if(!pTable->IsTableComplex())
4195 //        throw uno::RuntimeException("Table too complex", static_cast<cppu::OWeakObject*>(this));
4196     SwTableLines& rLines = pTable->GetTabLines();
4197     SwTableLine* pLine = rLines.front();
4198     return pLine->GetTabBoxes().size();
4199 }
4200 
4201 uno::Any SwXTableColumns::getByIndex(sal_Int32 nIndex)
4202 {
4203     SolarMutexGuard aGuard;
4204     if(nIndex < 0 || getCount() <= nIndex)
4205         throw lang::IndexOutOfBoundsException();
4206     return uno::makeAny(uno::Reference<uno::XInterface>()); // i#21699 not supported
4207 }
4208 
4209 uno::Type SAL_CALL SwXTableColumns::getElementType()
4210 {
4211     return cppu::UnoType<uno::XInterface>::get();
4212 }
4213 
4214 sal_Bool SwXTableColumns::hasElements()
4215 {
4216     SolarMutexGuard aGuard;
4217     lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
4218     return true;
4219 }
4220 
4221 ///@see SwXTableRows::insertByIndex (TODO: seems to be copy and paste programming here)
4222 void SwXTableColumns::insertByIndex(sal_Int32 nIndex, sal_Int32 nCount)
4223 {
4224     SolarMutexGuard aGuard;
4225     if (nCount == 0)
4226         return;
4227     SwFrameFormat* pFrameFormat(lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this)));
4228     SwTable* pTable = lcl_EnsureTableNotComplex(SwTable::FindTable(pFrameFormat), static_cast<cppu::OWeakObject*>(this));
4229     SwTableLines& rLines = pTable->GetTabLines();
4230     SwTableLine* pLine = rLines.front();
4231     const size_t nColCount = pLine->GetTabBoxes().size();
4232     if (nCount <= 0 || !(0 <= nIndex && static_cast<size_t>(nIndex) <= nColCount))
4233         throw uno::RuntimeException("Illegal arguments", static_cast<cppu::OWeakObject*>(this));
4234     const OUString sTLName = sw_GetCellName(nIndex, 0);
4235     const SwTableBox* pTLBox = pTable->GetTableBox( sTLName );
4236     bool bAppend = false;
4237     if(!pTLBox)
4238     {
4239         bAppend = true;
4240         // to append at the end the cursor must be in the last line
4241         SwTableBoxes& rBoxes = pLine->GetTabBoxes();
4242         pTLBox = rBoxes.back();
4243     }
4244     if(!pTLBox)
4245         throw uno::RuntimeException("Illegal arguments", static_cast<cppu::OWeakObject*>(this));
4246     const SwStartNode* pSttNd = pTLBox->GetSttNd();
4247     SwPosition aPos(*pSttNd);
4248     UnoActionContext aAction(pFrameFormat->GetDoc());
4249     auto pUnoCursor(pFrameFormat->GetDoc()->CreateUnoCursor(aPos, true));
4250     pUnoCursor->Move(fnMoveForward, GoInNode);
4251 
4252     {
4253         // remove actions - TODO: why?
4254         UnoActionRemoveContext aRemoveContext(pUnoCursor->GetDoc());
4255     }
4256 
4257     pFrameFormat->GetDoc()->InsertCol(*pUnoCursor, static_cast<sal_uInt16>(nCount), bAppend);
4258 }
4259 
4260 ///@see SwXTableRows::removeByIndex (TODO: seems to be copy and paste programming here)
4261 void SwXTableColumns::removeByIndex(sal_Int32 nIndex, sal_Int32 nCount)
4262 {
4263     SolarMutexGuard aGuard;
4264     if (nCount == 0)
4265         return;
4266     SwFrameFormat* pFrameFormat(lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this)));
4267     if(nIndex < 0 || nCount <=0 )
4268         throw uno::RuntimeException();
4269     SwTable* pTable = lcl_EnsureTableNotComplex(SwTable::FindTable(pFrameFormat), static_cast<cppu::OWeakObject*>(this));
4270     const OUString sTLName = sw_GetCellName(nIndex, 0);
4271     const SwTableBox* pTLBox = pTable->GetTableBox( sTLName );
4272     if(!pTLBox)
4273         throw uno::RuntimeException("Cell not found", static_cast<cppu::OWeakObject*>(this));
4274     const SwStartNode* pSttNd = pTLBox->GetSttNd();
4275     SwPosition aPos(*pSttNd);
4276     // set cursor to the upper-left cell of the range
4277     auto pUnoCursor(pFrameFormat->GetDoc()->CreateUnoCursor(aPos, true));
4278     pUnoCursor->Move(fnMoveForward, GoInNode);
4279     pUnoCursor->SetRemainInSection(false);
4280     const OUString sTRName = sw_GetCellName(nIndex + nCount - 1, 0);
4281     const SwTableBox* pTRBox = pTable->GetTableBox(sTRName);
4282     if(!pTRBox)
4283         throw uno::RuntimeException("Cell not found", static_cast<cppu::OWeakObject*>(this));
4284     pUnoCursor->SetMark();
4285     pUnoCursor->GetPoint()->nNode = *pTRBox->GetSttNd();
4286     pUnoCursor->Move(fnMoveForward, GoInNode);
4287     SwUnoTableCursor& rCursor = dynamic_cast<SwUnoTableCursor&>(*pUnoCursor);
4288     {
4289         // HACK: remove pending actions for selecting old style tables
4290         UnoActionRemoveContext aRemoveContext(rCursor);
4291     }
4292     rCursor.MakeBoxSels();
4293     {   // these braces are important
4294         UnoActionContext aAction(pFrameFormat->GetDoc());
4295         pFrameFormat->GetDoc()->DeleteCol(*pUnoCursor);
4296         pUnoCursor.reset();
4297     }
4298     {
4299         // invalidate all actions - TODO: why?
4300         UnoActionRemoveContext aRemoveContext(pFrameFormat->GetDoc());
4301     }
4302 }
4303 
4304 void SwXTableColumns::Impl::Notify(const SfxHint& rHint)
4305 {
4306     if(rHint.GetId() == SfxHintId::Dying)
4307         m_pFrameFormat = nullptr;
4308 }
4309 
4310 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
4311