/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; using ::editeng::SvxBorderLine; namespace { template struct FindUnoInstanceHint final : SfxHint { FindUnoInstanceHint(Tcoretype* pCore) : m_pCore(pCore), m_pResult(nullptr) {}; const Tcoretype* const m_pCore; mutable Tunotype* m_pResult; }; SwFrameFormat* lcl_EnsureCoreConnected(SwFrameFormat* pFormat, cppu::OWeakObject* pObject) { if(!pFormat) throw uno::RuntimeException("Lost connection to core objects", pObject); return pFormat; } SwTable* lcl_EnsureTableNotComplex(SwTable* pTable, cppu::OWeakObject* pObject) { if(pTable->IsTableComplex()) throw uno::RuntimeException("Table too complex", pObject); return pTable; } chart::ChartDataChangeEvent createChartEvent(uno::Reference const& xSource) { //TODO: find appropriate settings of the Event chart::ChartDataChangeEvent event; event.Source = xSource; event.Type = chart::ChartDataChangeType_ALL; event.StartColumn = 0; event.EndColumn = 1; event.StartRow = 0; event.EndRow = 1; return event; } void lcl_SendChartEvent( uno::Reference const& xSource, ::comphelper::OInterfaceContainerHelper2& rListeners) { rListeners.notifyEach( &chart::XChartDataChangeEventListener::chartDataChanged, createChartEvent(xSource)); } void lcl_SendChartEvent( uno::Reference const& xSource, ::cppu::OMultiTypeInterfaceContainerHelper const& rListeners) { auto pContainer(rListeners.getContainer(cppu::UnoType::get())); if (pContainer) pContainer->notifyEach( &chart::XChartDataChangeEventListener::chartDataChanged, createChartEvent(xSource)); } } #define UNO_TABLE_COLUMN_SUM 10000 static bool lcl_LineToSvxLine(const table::BorderLine& rLine, SvxBorderLine& rSvxLine) { rSvxLine.SetColor(Color(rLine.Color)); rSvxLine.GuessLinesWidths( SvxBorderLineStyle::NONE, convertMm100ToTwip( rLine.OuterLineWidth ), convertMm100ToTwip( rLine.InnerLineWidth ), convertMm100ToTwip( rLine.LineDistance ) ); return rLine.InnerLineWidth > 0 || rLine.OuterLineWidth > 0; } /// @throws lang::IllegalArgumentException /// @throws uno::RuntimeException static void lcl_SetSpecialProperty(SwFrameFormat* pFormat, const SfxItemPropertySimpleEntry* pEntry, const uno::Any& aValue) { // special treatment for "non-items" switch(pEntry->nWID) { case FN_TABLE_HEADLINE_REPEAT: case FN_TABLE_HEADLINE_COUNT: { SwTable* pTable = SwTable::FindTable( pFormat ); UnoActionContext aAction(pFormat->GetDoc()); if( pEntry->nWID == FN_TABLE_HEADLINE_REPEAT) { pFormat->GetDoc()->SetRowsToRepeat( *pTable, aValue.get() ? 1 : 0 ); } else { sal_Int32 nRepeat = 0; aValue >>= nRepeat; if( nRepeat >= 0 && nRepeat < SAL_MAX_UINT16 ) pFormat->GetDoc()->SetRowsToRepeat( *pTable, static_cast(nRepeat) ); } } break; case FN_TABLE_IS_RELATIVE_WIDTH: case FN_TABLE_WIDTH: case FN_TABLE_RELATIVE_WIDTH: { SwFormatFrameSize aSz( pFormat->GetFrameSize() ); if(FN_TABLE_WIDTH == pEntry->nWID) { sal_Int32 nWidth = 0; aValue >>= nWidth; aSz.SetWidthPercent(0); aSz.SetWidth ( convertMm100ToTwip ( nWidth ) ); } else if(FN_TABLE_RELATIVE_WIDTH == pEntry->nWID) { sal_Int16 nSet = 0; aValue >>= nSet; if(nSet && nSet <=100) aSz.SetWidthPercent( static_cast(nSet) ); } else if(FN_TABLE_IS_RELATIVE_WIDTH == pEntry->nWID) { if(!aValue.get()) aSz.SetWidthPercent(0); else { lang::IllegalArgumentException aExcept; aExcept.Message = "relative width cannot be switched on with this property"; throw aExcept; } } pFormat->GetDoc()->SetAttr(aSz, *pFormat); } break; case RES_PAGEDESC: { OUString sPageStyle; aValue >>= sPageStyle; const SwPageDesc* pDesc = nullptr; if (!sPageStyle.isEmpty()) { SwStyleNameMapper::FillUIName(sPageStyle, sPageStyle, SwGetPoolIdFromName::PageDesc); pDesc = SwPageDesc::GetByName(*pFormat->GetDoc(), sPageStyle); } SwFormatPageDesc aDesc( pDesc ); pFormat->GetDoc()->SetAttr(aDesc, *pFormat); } break; default: throw lang::IllegalArgumentException(); } } static uno::Any lcl_GetSpecialProperty(SwFrameFormat* pFormat, const SfxItemPropertySimpleEntry* pEntry ) { switch(pEntry->nWID) { case FN_TABLE_HEADLINE_REPEAT: case FN_TABLE_HEADLINE_COUNT: { SwTable* pTable = SwTable::FindTable( pFormat ); const sal_uInt16 nRepeat = pTable->GetRowsToRepeat(); if(pEntry->nWID == FN_TABLE_HEADLINE_REPEAT) return uno::makeAny(nRepeat > 0); return uno::makeAny(nRepeat); } case FN_TABLE_WIDTH: case FN_TABLE_IS_RELATIVE_WIDTH: case FN_TABLE_RELATIVE_WIDTH: { uno::Any aRet; const SwFormatFrameSize& rSz = pFormat->GetFrameSize(); if(FN_TABLE_WIDTH == pEntry->nWID) rSz.QueryValue(aRet, MID_FRMSIZE_WIDTH|CONVERT_TWIPS); else if(FN_TABLE_RELATIVE_WIDTH == pEntry->nWID) rSz.QueryValue(aRet, MID_FRMSIZE_REL_WIDTH); else aRet <<= (0 != rSz.GetWidthPercent()); return aRet; } case RES_PAGEDESC: { const SfxItemSet& rSet = pFormat->GetAttrSet(); const SfxPoolItem* pItem; if(SfxItemState::SET == rSet.GetItemState(RES_PAGEDESC, false, &pItem)) { const SwPageDesc* pDsc = static_cast(pItem)->GetPageDesc(); if(pDsc) return uno::makeAny(SwStyleNameMapper::GetProgName(pDsc->GetName(), SwGetPoolIdFromName::PageDesc )); } return uno::makeAny(OUString()); } case RES_ANCHOR: return uno::makeAny(text::TextContentAnchorType_AT_PARAGRAPH); case FN_UNO_ANCHOR_TYPES: { uno::Sequence aTypes{text::TextContentAnchorType_AT_PARAGRAPH}; return uno::makeAny(aTypes); } case FN_UNO_WRAP : return uno::makeAny(text::WrapTextMode_NONE); case FN_PARAM_LINK_DISPLAY_NAME : return uno::makeAny(pFormat->GetName()); case FN_UNO_REDLINE_NODE_START: case FN_UNO_REDLINE_NODE_END: { SwTable* pTable = SwTable::FindTable( pFormat ); SwNode* pTableNode = pTable->GetTableNode(); if(FN_UNO_REDLINE_NODE_END == pEntry->nWID) pTableNode = pTableNode->EndOfSectionNode(); for(const SwRangeRedline* pRedline : pFormat->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable()) { const SwNode& rRedPointNode = pRedline->GetNode(); const SwNode& rRedMarkNode = pRedline->GetNode(false); if(&rRedPointNode == pTableNode || &rRedMarkNode == pTableNode) { const SwNode& rStartOfRedline = SwNodeIndex(rRedPointNode) <= SwNodeIndex(rRedMarkNode) ? rRedPointNode : rRedMarkNode; bool bIsStart = &rStartOfRedline == pTableNode; return uno::makeAny(SwXRedlinePortion::CreateRedlineProperties(*pRedline, bIsStart)); } } } } return uno::Any(); } /** get position of a cell with a given name * * If everything was OK, the indices for column and row are changed (both >= 0). * In case of errors, at least one of them is < 0. * * Also since the implementations of tables does not really have columns using * this function is appropriate only for tables that are not complex (i.e. * where IsTableComplex() returns false). * * @param rCellName e.g. A1..Z1, a1..z1, AA1..AZ1, Aa1..Az1, BA1..BZ1, Ba1..Bz1, ... * @param [IN,OUT] o_rColumn (0-based) * @param [IN,OUT] o_rRow (0-based) */ //TODO: potential for throwing proper exceptions instead of having every caller to check for errors void SwXTextTable::GetCellPosition(const OUString& rCellName, sal_Int32& o_rColumn, sal_Int32& o_rRow) { o_rColumn = o_rRow = -1; // default return values indicating failure const sal_Int32 nLen = rCellName.getLength(); if(!nLen) { SAL_WARN("sw.uno", "failed to get column or row index"); return; } sal_Int32 nRowPos = 0; while (nRowPos='0' && rCellName[nRowPos]<='9') { break; } ++nRowPos; } if (nRowPos<=0 || nRowPos>=nLen) return; sal_Int32 nColIdx = 0; for (sal_Int32 i = 0; i < nRowPos; ++i) { nColIdx *= 52; if (i < nRowPos - 1) ++nColIdx; const sal_Unicode cChar = rCellName[i]; if ('A' <= cChar && cChar <= 'Z') nColIdx += cChar - 'A'; else if ('a' <= cChar && cChar <= 'z') nColIdx += 26 + cChar - 'a'; else { nColIdx = -1; // sth failed break; } } o_rColumn = nColIdx; o_rRow = rCellName.copy(nRowPos).toInt32() - 1; // - 1 because indices ought to be 0 based } /** compare position of two cells (check rows first) * * @note this function probably also make sense only * for cell names of non-complex tables * * @param rCellName1 e.g. "A1" (non-empty string with valid cell name) * @param rCellName2 e.g. "A1" (non-empty string with valid cell name) * @return -1 if cell_1 < cell_2; 0 if both cells are equal; +1 if cell_1 > cell_2 */ int sw_CompareCellsByRowFirst( const OUString &rCellName1, const OUString &rCellName2 ) { sal_Int32 nCol1 = -1, nRow1 = -1, nCol2 = -1, nRow2 = -1; SwXTextTable::GetCellPosition( rCellName1, nCol1, nRow1 ); SwXTextTable::GetCellPosition( rCellName2, nCol2, nRow2 ); if (nRow1 < nRow2 || (nRow1 == nRow2 && nCol1 < nCol2)) return -1; else if (nCol1 == nCol2 && nRow1 == nRow2) return 0; else return +1; } /** compare position of two cells (check columns first) * * @note this function probably also make sense only * for cell names of non-complex tables * * @param rCellName1 e.g. "A1" (non-empty string with valid cell name) * @param rCellName2 e.g. "A1" (non-empty string with valid cell name) * @return -1 if cell_1 < cell_2; 0 if both cells are equal; +1 if cell_1 > cell_2 */ int sw_CompareCellsByColFirst( const OUString &rCellName1, const OUString &rCellName2 ) { sal_Int32 nCol1 = -1, nRow1 = -1, nCol2 = -1, nRow2 = -1; SwXTextTable::GetCellPosition( rCellName1, nCol1, nRow1 ); SwXTextTable::GetCellPosition( rCellName2, nCol2, nRow2 ); if (nCol1 < nCol2 || (nCol1 == nCol2 && nRow1 < nRow2)) return -1; else if (nRow1 == nRow2 && nCol1 == nCol2) return 0; else return +1; } /** compare position of two cell ranges * * @note this function probably also make sense only * for cell names of non-complex tables * * @param rRange1StartCell e.g. "A1" (non-empty string with valid cell name) * @param rRange1EndCell e.g. "A1" (non-empty string with valid cell name) * @param rRange2StartCell e.g. "A1" (non-empty string with valid cell name) * @param rRange2EndCell e.g. "A1" (non-empty string with valid cell name) * @param bCmpColsFirst if position in columns will be compared first before rows * * @return -1 if cell_range_1 < cell_range_2; 0 if both cell ranges are equal; +1 if cell_range_1 > cell_range_2 */ int sw_CompareCellRanges( const OUString &rRange1StartCell, const OUString &rRange1EndCell, const OUString &rRange2StartCell, const OUString &rRange2EndCell, bool bCmpColsFirst ) { int (*pCompareCells)( const OUString &, const OUString & ) = bCmpColsFirst ? &sw_CompareCellsByColFirst : &sw_CompareCellsByRowFirst; int nCmpResStartCells = pCompareCells( rRange1StartCell, rRange2StartCell ); if ((-1 == nCmpResStartCells ) || ( 0 == nCmpResStartCells && -1 == pCompareCells( rRange1EndCell, rRange2EndCell ) )) return -1; else if (0 == nCmpResStartCells && 0 == pCompareCells( rRange1EndCell, rRange2EndCell )) return 0; else return +1; } /** get cell name at a specified coordinate * * @param nColumn column index (0-based) * @param nRow row index (0-based) * @return the cell name */ OUString sw_GetCellName( sal_Int32 nColumn, sal_Int32 nRow ) { if (nColumn < 0 || nRow < 0) return OUString(); OUString sCellName; sw_GetTableBoxColStr( static_cast< sal_uInt16 >(nColumn), sCellName ); return sCellName + OUString::number( nRow + 1 ); } /** Find the top left or bottom right corner box in given table. Consider nested lines when finding the box. @param rTableLines the table @param i_bTopLeft if true, find top left box, otherwise find bottom right box */ static const SwTableBox* lcl_FindCornerTableBox(const SwTableLines& rTableLines, const bool i_bTopLeft) { const SwTableLines* pLines(&rTableLines); while(true) { assert(!pLines->empty()); if(pLines->empty()) return nullptr; const SwTableLine* pLine(i_bTopLeft ? pLines->front() : pLines->back()); assert(pLine); const SwTableBoxes& rBoxes(pLine->GetTabBoxes()); assert(rBoxes.size() != 0); const SwTableBox* pBox = i_bTopLeft ? rBoxes.front() : rBoxes.back(); assert(pBox); if (pBox->GetSttNd()) return pBox; pLines = &pBox->GetTabLines(); } } /** cleanup order in a range * * Sorts the input to a uniform format. I.e. for the four possible representation * A1:C5, C5:A1, A5:C1, C1:A5 * the result will be always A1:C5. * * @param [IN,OUT] rCell1 cell name (will be modified to upper-left corner), e.g. "A1" (non-empty string with valid cell name) * @param [IN,OUT] rCell2 cell name (will be modified to lower-right corner), e.g. "A1" (non-empty string with valid cell name) */ void sw_NormalizeRange(OUString &rCell1, OUString &rCell2) { sal_Int32 nCol1 = -1, nRow1 = -1, nCol2 = -1, nRow2 = -1; SwXTextTable::GetCellPosition( rCell1, nCol1, nRow1 ); SwXTextTable::GetCellPosition( rCell2, nCol2, nRow2 ); if (nCol2 < nCol1 || nRow2 < nRow1) { rCell1 = sw_GetCellName( std::min(nCol1, nCol2), std::min(nRow1, nRow2) ); rCell2 = sw_GetCellName( std::max(nCol1, nCol2), std::max(nRow1, nRow2) ); } } void SwRangeDescriptor::Normalize() { if (nTop > nBottom) std::swap(nBottom, nTop); if (nLeft > nRight) std::swap(nLeft, nRight); } static SwXCell* lcl_CreateXCell(SwFrameFormat* pFormat, sal_Int32 nColumn, sal_Int32 nRow) { const OUString sCellName = sw_GetCellName(nColumn, nRow); SwTable* pTable = SwTable::FindTable(pFormat); SwTableBox* pBox = const_cast(pTable->GetTableBox(sCellName)); if(!pBox) return nullptr; return SwXCell::CreateXCell(pFormat, pBox, pTable); } static void lcl_InspectLines(SwTableLines& rLines, std::vector& rAllNames) { for(auto pLine : rLines) { for(auto pBox : pLine->GetTabBoxes()) { if(!pBox->GetName().isEmpty() && pBox->getRowSpan() > 0) rAllNames.push_back(pBox->GetName()); SwTableLines& rBoxLines = pBox->GetTabLines(); if(!rBoxLines.empty()) lcl_InspectLines(rBoxLines, rAllNames); } } } static bool lcl_FormatTable(SwFrameFormat const * pTableFormat) { bool bHasFrames = false; SwIterator aIter( *pTableFormat ); for(SwFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next()) { vcl::RenderContext* pRenderContext = pFrame->getRootFrame()->GetCurrShell()->GetOut(); // mba: no TYPEINFO for SwTabFrame if(!pFrame->IsTabFrame()) continue; DisableCallbackAction a(*pFrame->getRootFrame()); SwTabFrame* pTabFrame = static_cast(pFrame); if(pTabFrame->isFrameAreaDefinitionValid()) pTabFrame->InvalidatePos(); pTabFrame->SetONECalcLowers(); pTabFrame->Calc(pRenderContext); bHasFrames = true; } return bHasFrames; } static void lcl_CursorSelect(SwPaM& rCursor, bool bExpand) { if(bExpand) { if(!rCursor.HasMark()) rCursor.SetMark(); } else if(rCursor.HasMark()) rCursor.DeleteMark(); } static void lcl_GetTableSeparators(uno::Any& rRet, SwTable const * pTable, SwTableBox const * pBox, bool bRow) { SwTabCols aCols; aCols.SetLeftMin ( 0 ); aCols.SetLeft ( 0 ); aCols.SetRight ( UNO_TABLE_COLUMN_SUM ); aCols.SetRightMax( UNO_TABLE_COLUMN_SUM ); pTable->GetTabCols( aCols, pBox, false, bRow ); const size_t nSepCount = aCols.Count(); uno::Sequence< text::TableColumnSeparator> aColSeq(nSepCount); text::TableColumnSeparator* pArray = aColSeq.getArray(); bool bError = false; for(size_t i = 0; i < nSepCount; ++i) { pArray[i].Position = static_cast< sal_Int16 >(aCols[i]); pArray[i].IsVisible = !aCols.IsHidden(i); if(!bRow && !pArray[i].IsVisible) { bError = true; break; } } if(!bError) rRet <<= aColSeq; } static void lcl_SetTableSeparators(const uno::Any& rVal, SwTable* pTable, SwTableBox const * pBox, bool bRow, SwDoc* pDoc) { SwTabCols aOldCols; aOldCols.SetLeftMin ( 0 ); aOldCols.SetLeft ( 0 ); aOldCols.SetRight ( UNO_TABLE_COLUMN_SUM ); aOldCols.SetRightMax( UNO_TABLE_COLUMN_SUM ); pTable->GetTabCols( aOldCols, pBox, false, bRow ); const size_t nOldCount = aOldCols.Count(); // there is no use in setting tab cols if there is only one column if( !nOldCount ) return; auto pSepSeq = o3tl::tryAccess>(rVal); if(!pSepSeq || static_cast(pSepSeq->getLength()) != nOldCount) return; SwTabCols aCols(aOldCols); const text::TableColumnSeparator* pArray = pSepSeq->getConstArray(); long nLastValue = 0; //sal_Int32 nTableWidth = aCols.GetRight() - aCols.GetLeft(); for(size_t i = 0; i < nOldCount; ++i) { aCols[i] = pArray[i].Position; if(bool(pArray[i].IsVisible) == aCols.IsHidden(i) || (!bRow && aCols.IsHidden(i)) || aCols[i] < nLastValue || UNO_TABLE_COLUMN_SUM < aCols[i] ) return; // probably this should assert() nLastValue = aCols[i]; } pDoc->SetTabCols(*pTable, aCols, aOldCols, pBox, bRow ); } /* non UNO function call to set string in SwXCell */ void sw_setString( SwXCell &rCell, const OUString &rText, bool bKeepNumberFormat = false ) { if(rCell.IsValid()) { SwFrameFormat* pBoxFormat = rCell.m_pBox->ClaimFrameFormat(); pBoxFormat->LockModify(); pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMULA ); pBoxFormat->ResetFormatAttr( RES_BOXATR_VALUE ); if (!bKeepNumberFormat) pBoxFormat->SetFormatAttr( SwTableBoxNumFormat(/*default Text*/) ); pBoxFormat->UnlockModify(); } rCell.SwXText::setString(rText); } /* non UNO function call to set value in SwXCell */ void sw_setValue( SwXCell &rCell, double nVal ) { if(!rCell.IsValid()) return; // first this text (maybe) needs to be deleted sal_uLong nNdPos = rCell.m_pBox->IsValidNumTextNd(); if(ULONG_MAX != nNdPos) sw_setString( rCell, OUString(), true ); // true == keep number format SwDoc* pDoc = rCell.GetDoc(); UnoActionContext aAction(pDoc); SwFrameFormat* pBoxFormat = rCell.m_pBox->ClaimFrameFormat(); SfxItemSet aSet(pDoc->GetAttrPool(), svl::Items{}); const SfxPoolItem* pItem; //!! do we need to set a new number format? Yes, if // - there is no current number format // - the current number format is not a number format according to the number formatter, but rather a text format if(SfxItemState::SET != pBoxFormat->GetAttrSet().GetItemState(RES_BOXATR_FORMAT, true, &pItem) || pDoc->GetNumberFormatter()->IsTextFormat(static_cast(pItem)->GetValue())) { aSet.Put(SwTableBoxNumFormat(0)); } SwTableBoxValue aVal(nVal); aSet.Put(aVal); pDoc->SetTableBoxFormulaAttrs( *rCell.m_pBox, aSet ); // update table SwTableFormulaUpdate aTableUpdate( SwTable::FindTable( rCell.GetFrameFormat() )); pDoc->getIDocumentFieldsAccess().UpdateTableFields( &aTableUpdate ); } SwXCell::SwXCell(SwFrameFormat* pTableFormat, SwTableBox* pBx, size_t const nPos) : SwXText(pTableFormat->GetDoc(), CursorType::TableText), m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TABLE_CELL)), m_pBox(pBx), m_pStartNode(nullptr), m_pTableFormat(pTableFormat), m_nFndPos(nPos) { StartListening(pTableFormat->GetNotifier()); } SwXCell::SwXCell(SwFrameFormat* pTableFormat, const SwStartNode& rStartNode) : SwXText(pTableFormat->GetDoc(), CursorType::TableText), m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TABLE_CELL)), m_pBox(nullptr), m_pStartNode(&rStartNode), m_pTableFormat(pTableFormat), m_nFndPos(NOTFOUND) { StartListening(pTableFormat->GetNotifier()); } SwXCell::~SwXCell() { SolarMutexGuard aGuard; EndListeningAll(); } namespace { class theSwXCellUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXCellUnoTunnelId > {}; } const uno::Sequence< sal_Int8 > & SwXCell::getUnoTunnelId() { return theSwXCellUnoTunnelId::get().getSeq(); } sal_Int64 SAL_CALL SwXCell::getSomething( const uno::Sequence< sal_Int8 >& rId ) { if( isUnoTunnelId(rId) ) { return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) ); } else return SwXText::getSomething(rId); } uno::Sequence< uno::Type > SAL_CALL SwXCell::getTypes( ) { return comphelper::concatSequences( SwXCellBaseClass::getTypes(), SwXText::getTypes() ); } uno::Sequence< sal_Int8 > SAL_CALL SwXCell::getImplementationId( ) { return css::uno::Sequence(); } void SAL_CALL SwXCell::acquire( ) throw() { SwXCellBaseClass::acquire(); } void SAL_CALL SwXCell::release( ) throw() { SolarMutexGuard aGuard; SwXCellBaseClass::release(); } uno::Any SAL_CALL SwXCell::queryInterface( const uno::Type& aType ) { uno::Any aRet = SwXCellBaseClass::queryInterface(aType); if(aRet.getValueType() == cppu::UnoType::get()) aRet = SwXText::queryInterface(aType); return aRet; } const SwStartNode *SwXCell::GetStartNode() const { const SwStartNode* pSttNd = nullptr; if( m_pStartNode || IsValid() ) pSttNd = m_pStartNode ? m_pStartNode : m_pBox->GetSttNd(); return pSttNd; } uno::Reference< text::XTextCursor > SwXCell::CreateCursor() { return createTextCursor(); } bool SwXCell::IsValid() const { // FIXME: this is now a const method, to make SwXText::IsValid invisible // but the const_cast here are still ridiculous. TODO: find a better way. SwFrameFormat* pTableFormat = m_pBox ? GetFrameFormat() : nullptr; if(!pTableFormat) { const_cast(this)->m_pBox = nullptr; } else { SwTable* pTable = SwTable::FindTable( pTableFormat ); SwTableBox const*const pFoundBox = const_cast(this)->FindBox(pTable, m_pBox); if (!pFoundBox) { const_cast(this)->m_pBox = nullptr; } } return nullptr != m_pBox; } OUString SwXCell::getFormula() { SolarMutexGuard aGuard; if(!IsValid()) return OUString(); SwTableBoxFormula aFormula( m_pBox->GetFrameFormat()->GetTableBoxFormula() ); SwTable* pTable = SwTable::FindTable( GetFrameFormat() ); aFormula.PtrToBoxNm( pTable ); return aFormula.GetFormula(); } ///@see sw_setValue (TODO: seems to be copy and paste programming here) void SwXCell::setFormula(const OUString& rFormula) { SolarMutexGuard aGuard; if(!IsValid()) return; // first this text (maybe) needs to be deleted sal_uInt32 nNdPos = m_pBox->IsValidNumTextNd(); if(USHRT_MAX == nNdPos) sw_setString( *this, OUString(), true ); OUString sFormula(comphelper::string::stripStart(rFormula, ' ')); if( !sFormula.isEmpty() && '=' == sFormula[0] ) sFormula = sFormula.copy( 1 ); SwTableBoxFormula aFormula( sFormula ); SwDoc* pMyDoc = GetDoc(); UnoActionContext aAction(pMyDoc); SfxItemSet aSet(pMyDoc->GetAttrPool(), svl::Items{}); const SfxPoolItem* pItem; SwFrameFormat* pBoxFormat = m_pBox->GetFrameFormat(); if(SfxItemState::SET != pBoxFormat->GetAttrSet().GetItemState(RES_BOXATR_FORMAT, true, &pItem) || pMyDoc->GetNumberFormatter()->IsTextFormat(static_cast(pItem)->GetValue())) { aSet.Put(SwTableBoxNumFormat(0)); } aSet.Put(aFormula); GetDoc()->SetTableBoxFormulaAttrs( *m_pBox, aSet ); // update table SwTableFormulaUpdate aTableUpdate( SwTable::FindTable( GetFrameFormat() )); pMyDoc->getIDocumentFieldsAccess().UpdateTableFields( &aTableUpdate ); } double SwXCell::getValue() { SolarMutexGuard aGuard; // #i112652# a table cell may contain NaN as a value, do not filter that double fRet; if(IsValid() && !getString().isEmpty()) fRet = m_pBox->GetFrameFormat()->GetTableBoxValue().GetValue(); else ::rtl::math::setNan( &fRet ); return fRet; } void SwXCell::setValue(double rValue) { SolarMutexGuard aGuard; sw_setValue( *this, rValue ); } table::CellContentType SwXCell::getType() { SolarMutexGuard aGuard; table::CellContentType nRes = table::CellContentType_EMPTY; sal_uInt32 nNdPos = m_pBox->IsFormulaOrValueBox(); switch (nNdPos) { case 0 : nRes = table::CellContentType_TEXT; break; case USHRT_MAX : nRes = table::CellContentType_EMPTY; break; case RES_BOXATR_VALUE : nRes = table::CellContentType_VALUE; break; case RES_BOXATR_FORMULA : nRes = table::CellContentType_FORMULA; break; default : OSL_FAIL( "unexpected case" ); } return nRes; } void SwXCell::setString(const OUString& aString) { SolarMutexGuard aGuard; sw_setString( *this, aString ); } sal_Int32 SwXCell::getError() { SolarMutexGuard aGuard; OUString sContent = getString(); return sal_Int32(sContent == SwViewShell::GetShellRes()->aCalc_Error); } uno::Reference SwXCell::createTextCursor() { SolarMutexGuard aGuard; if(!m_pStartNode && !IsValid()) throw uno::RuntimeException(); const SwStartNode* pSttNd = m_pStartNode ? m_pStartNode : m_pBox->GetSttNd(); SwPosition aPos(*pSttNd); SwXTextCursor* const pXCursor = new SwXTextCursor(*GetDoc(), this, CursorType::TableText, aPos); auto& rUnoCursor(pXCursor->GetCursor()); rUnoCursor.Move(fnMoveForward, GoInNode); return static_cast(pXCursor); } uno::Reference SwXCell::createTextCursorByRange(const uno::Reference< text::XTextRange > & xTextPosition) { SolarMutexGuard aGuard; SwUnoInternalPaM aPam(*GetDoc()); if((!m_pStartNode && !IsValid()) || !::sw::XTextRangeToSwPaM(aPam, xTextPosition)) throw uno::RuntimeException(); const SwStartNode* pSttNd = m_pStartNode ? m_pStartNode : m_pBox->GetSttNd(); // skip sections SwStartNode* p1 = aPam.GetNode().StartOfSectionNode(); while(p1->IsSectionNode()) p1 = p1->StartOfSectionNode(); if( p1 != pSttNd ) return nullptr; return static_cast( new SwXTextCursor(*GetDoc(), this, CursorType::TableText, *aPam.GetPoint(), aPam.GetMark())); } uno::Reference< beans::XPropertySetInfo > SwXCell::getPropertySetInfo() { static uno::Reference< beans::XPropertySetInfo > xRef = m_pPropSet->getPropertySetInfo(); return xRef; } void SwXCell::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue) { SolarMutexGuard aGuard; if(!IsValid()) return; // Hack to support hidden property to transfer textDirection if(rPropertyName == "FRMDirection") { SvxFrameDirectionItem aItem(SvxFrameDirection::Environment, RES_FRAMEDIR); aItem.PutValue(aValue, 0); m_pBox->GetFrameFormat()->SetFormatAttr(aItem); } else if(rPropertyName == "TableRedlineParams") { // Get the table row properties uno::Sequence tableCellProperties = aValue.get< uno::Sequence< beans::PropertyValue > >(); comphelper::SequenceAsHashMap aPropMap(tableCellProperties); OUString sRedlineType; if(!(aPropMap.getValue("RedlineType") >>= sRedlineType)) throw beans::UnknownPropertyException("No redline type property: ", static_cast(this)); // Create a 'Table Cell Redline' object SwUnoCursorHelper::makeTableCellRedline(*m_pBox, sRedlineType, tableCellProperties); } else { auto pEntry(m_pPropSet->getPropertyMap().getByName(rPropertyName)); if ( !pEntry ) { // not a table property: ignore it, if it is a paragraph/character property const SfxItemPropertySet& rParaPropSet = *aSwMapProvider.GetPropertySet(PROPERTY_MAP_PARAGRAPH); pEntry = rParaPropSet.getPropertyMap().getByName(rPropertyName); if ( pEntry ) return; } if(!pEntry) throw beans::UnknownPropertyException(rPropertyName, static_cast(this)); if(pEntry->nWID != FN_UNO_CELL_ROW_SPAN) { SwFrameFormat* pBoxFormat = m_pBox->ClaimFrameFormat(); SwAttrSet aSet(pBoxFormat->GetAttrSet()); m_pPropSet->setPropertyValue(rPropertyName, aValue, aSet); pBoxFormat->GetDoc()->SetAttr(aSet, *pBoxFormat); } else if(aValue.isExtractableTo(cppu::UnoType::get())) m_pBox->setRowSpan(aValue.get()); } } uno::Any SwXCell::getPropertyValue(const OUString& rPropertyName) { SolarMutexGuard aGuard; if(!IsValid()) return uno::Any(); auto pEntry(m_pPropSet->getPropertyMap().getByName(rPropertyName)); if(!pEntry) throw beans::UnknownPropertyException(rPropertyName, static_cast(this)); switch(pEntry->nWID) { case FN_UNO_CELL_ROW_SPAN: return uno::makeAny(m_pBox->getRowSpan()); break; case FN_UNO_TEXT_SECTION: { SwFrameFormat* pTableFormat = GetFrameFormat(); SwTable* pTable = SwTable::FindTable(pTableFormat); SwTableNode* pTableNode = pTable->GetTableNode(); SwSectionNode* pSectionNode = pTableNode->FindSectionNode(); if(!pSectionNode) return uno::Any(); SwSection& rSect = pSectionNode->GetSection(); return uno::makeAny(SwXTextSections::GetObject(*rSect.GetFormat())); } break; case FN_UNO_CELL_NAME: return uno::makeAny(m_pBox->GetName()); break; case FN_UNO_REDLINE_NODE_START: case FN_UNO_REDLINE_NODE_END: { //redline can only be returned if it's a living object return SwXText::getPropertyValue(rPropertyName); } break; case FN_UNO_PARENT_TEXT: { if (!m_xParentText.is()) { const SwStartNode* pSttNd = m_pBox->GetSttNd(); if (!pSttNd) return uno::Any(); const SwTableNode* pTableNode = pSttNd->FindTableNode(); if (!pTableNode) return uno::Any(); SwPosition aPos(*pTableNode); SwDoc* pDoc = aPos.GetDoc(); if (!pDoc) return uno::Any(); m_xParentText = sw::CreateParentXText(*pDoc, aPos); } return uno::makeAny(m_xParentText); } break; default: { const SwAttrSet& rSet = m_pBox->GetFrameFormat()->GetAttrSet(); uno::Any aResult; m_pPropSet->getPropertyValue(rPropertyName, rSet, aResult); return aResult; } } } void SwXCell::addPropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/) { throw uno::RuntimeException("not implemented", static_cast(this)); }; void SwXCell::removePropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/) { throw uno::RuntimeException("not implemented", static_cast(this)); }; void SwXCell::addVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/) { throw uno::RuntimeException("not implemented", static_cast(this)); }; void SwXCell::removeVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/) { throw uno::RuntimeException("not implemented", static_cast(this)); }; uno::Reference SwXCell::createEnumeration() { SolarMutexGuard aGuard; if(!IsValid()) return uno::Reference(); const SwStartNode* pSttNd = m_pBox->GetSttNd(); SwPosition aPos(*pSttNd); auto pUnoCursor(GetDoc()->CreateUnoCursor(aPos)); pUnoCursor->Move(fnMoveForward, GoInNode); // remember table and start node for later travelling // (used in export of tables in tables) SwTable const*const pTable(&pSttNd->FindTableNode()->GetTable()); return SwXParagraphEnumeration::Create(this, pUnoCursor, CursorType::TableText, pSttNd, pTable); } uno::Type SAL_CALL SwXCell::getElementType() { return cppu::UnoType::get(); } sal_Bool SwXCell::hasElements() { return true; } void SwXCell::Notify(const SfxHint& rHint) { if(rHint.GetId() == SfxHintId::Dying) { m_pTableFormat = nullptr; } else if(auto pFindHint = dynamic_cast*>(&rHint)) { if(!pFindHint->m_pResult && pFindHint->m_pCore == GetTableBox()) pFindHint->m_pResult = this; } } SwXCell* SwXCell::CreateXCell(SwFrameFormat* pTableFormat, SwTableBox* pBox, SwTable *pTable ) { if(!pTableFormat || !pBox) return nullptr; if(!pTable) pTable = SwTable::FindTable(pTableFormat); SwTableSortBoxes::const_iterator it = pTable->GetTabSortBoxes().find(pBox); if(it == pTable->GetTabSortBoxes().end()) return nullptr; size_t const nPos = it - pTable->GetTabSortBoxes().begin(); FindUnoInstanceHint aHint{pBox}; pTableFormat->GetNotifier().Broadcast(aHint); return aHint.m_pResult ? aHint.m_pResult : new SwXCell(pTableFormat, pBox, nPos); } /** search if a box exists in a table * * @param pTable the table to search in * @param pBox2 box model to find * @return the box if existent in pTable, 0 (!!!) if not found */ SwTableBox* SwXCell::FindBox(SwTable* pTable, SwTableBox* pBox2) { // check if nFndPos happens to point to the right table box if( m_nFndPos < pTable->GetTabSortBoxes().size() && pBox2 == pTable->GetTabSortBoxes()[ m_nFndPos ] ) return pBox2; // if not, seek the entry (and return, if successful) SwTableSortBoxes::const_iterator it = pTable->GetTabSortBoxes().find( pBox2 ); if( it != pTable->GetTabSortBoxes().end() ) { m_nFndPos = it - pTable->GetTabSortBoxes().begin(); return pBox2; } // box not found: reset nFndPos pointer m_nFndPos = NOTFOUND; return nullptr; } double SwXCell::GetForcedNumericalValue() const { if(table::CellContentType_TEXT != const_cast(this)->getType()) return getValue(); // now we'll try to get a useful numerical value // from the text in the cell... sal_uInt32 nFIndex; SvNumberFormatter* pNumFormatter(const_cast(GetDoc()->GetNumberFormatter())); // look for SwTableBoxNumFormat value in parents as well const SfxPoolItem* pItem; auto pBoxFormat(GetTableBox()->GetFrameFormat()); SfxItemState eState = pBoxFormat->GetAttrSet().GetItemState(RES_BOXATR_FORMAT, true, &pItem); if (eState == SfxItemState::SET) { // please note that the language of the numberformat // is implicitly coded into the below value as well nFIndex = static_cast(pItem)->GetValue(); // since the current value indicates a text format but the call // to 'IsNumberFormat' below won't work for text formats // we need to get rid of the part that indicates the text format. // According to ER this can be done like this: nFIndex -= (nFIndex % SV_COUNTRY_LANGUAGE_OFFSET); } else { // system language is probably not the best possible choice // but since we have to guess anyway (because the language of at // the text is NOT the one used for the number format!) // it is at least conform to what is used in // SwTableShell::Execute when // SID_ATTR_NUMBERFORMAT_VALUE is set... LanguageType eLang = LANGUAGE_SYSTEM; nFIndex = pNumFormatter->GetStandardIndex( eLang ); } double fTmp; if (!const_cast(GetDoc())->IsNumberFormat(const_cast(this)->getString(), nFIndex, fTmp)) ::rtl::math::setNan(&fTmp); return fTmp; } uno::Any SwXCell::GetAny() const { if(!m_pBox) throw uno::RuntimeException(); // check if table box value item is set auto pBoxFormat(m_pBox->GetFrameFormat()); const bool bIsNum = pBoxFormat->GetItemState(RES_BOXATR_VALUE, false) == SfxItemState::SET; return bIsNum ? uno::makeAny(getValue()) : uno::makeAny(const_cast(this)->getString()); } OUString SwXCell::getImplementationName() { return "SwXCell"; } sal_Bool SwXCell::supportsService(const OUString& rServiceName) { return cppu::supportsService(this, rServiceName); } uno::Sequence< OUString > SwXCell::getSupportedServiceNames() { return {"com.sun.star.text.CellProperties"}; } OUString SwXTextTableRow::getImplementationName() { return "SwXTextTableRow"; } sal_Bool SwXTextTableRow::supportsService(const OUString& rServiceName) { return cppu::supportsService(this, rServiceName); } uno::Sequence< OUString > SwXTextTableRow::getSupportedServiceNames() { return {"com.sun.star.text.TextTableRow"}; } SwXTextTableRow::SwXTextTableRow(SwFrameFormat* pFormat, SwTableLine* pLn) : m_pFormat(pFormat), pLine(pLn), m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_TABLE_ROW)) { StartListening(m_pFormat->GetNotifier()); } SwXTextTableRow::~SwXTextTableRow() { SolarMutexGuard aGuard; EndListeningAll(); } uno::Reference< beans::XPropertySetInfo > SwXTextTableRow::getPropertySetInfo() { static uno::Reference xRef = m_pPropSet->getPropertySetInfo(); return xRef; } void SwXTextTableRow::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue) { SolarMutexGuard aGuard; SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast(this)); SwTable* pTable = SwTable::FindTable( pFormat ); SwTableLine* pLn = SwXTextTableRow::FindLine(pTable, pLine); if(!pLn) return; // Check for a specific property if ( rPropertyName == "TableRedlineParams" ) { // Get the table row properties uno::Sequence< beans::PropertyValue > tableRowProperties = aValue.get< uno::Sequence< beans::PropertyValue > >(); comphelper::SequenceAsHashMap aPropMap( tableRowProperties ); OUString sRedlineType; if( !(aPropMap.getValue("RedlineType") >>= sRedlineType) ) { throw beans::UnknownPropertyException("No redline type property: ", static_cast < cppu::OWeakObject * > ( this ) ); } // Create a 'Table Row Redline' object SwUnoCursorHelper::makeTableRowRedline( *pLn, sRedlineType, tableRowProperties); } else { const SfxItemPropertySimpleEntry* pEntry = m_pPropSet->getPropertyMap().getByName(rPropertyName); SwDoc* pDoc = pFormat->GetDoc(); if (!pEntry) throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) ); if ( pEntry->nFlags & beans::PropertyAttribute::READONLY) throw beans::PropertyVetoException("Property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) ); switch(pEntry->nWID) { case FN_UNO_ROW_HEIGHT: case FN_UNO_ROW_AUTO_HEIGHT: { SwFormatFrameSize aFrameSize(pLn->GetFrameFormat()->GetFrameSize()); if(FN_UNO_ROW_AUTO_HEIGHT== pEntry->nWID) { bool bSet = *o3tl::doAccess(aValue); aFrameSize.SetHeightSizeType(bSet ? SwFrameSize::Variable : SwFrameSize::Fixed); } else { sal_Int32 nHeight = 0; aValue >>= nHeight; Size aSz(aFrameSize.GetSize()); aSz.setHeight( convertMm100ToTwip(nHeight) ); aFrameSize.SetSize(aSz); } pDoc->SetAttr(aFrameSize, *pLn->ClaimFrameFormat()); } break; case FN_UNO_TABLE_COLUMN_SEPARATORS: { UnoActionContext aContext(pDoc); SwTable* pTable2 = SwTable::FindTable( pFormat ); lcl_SetTableSeparators(aValue, pTable2, pLine->GetTabBoxes()[0], true, pDoc); } break; default: { SwFrameFormat* pLnFormat = pLn->ClaimFrameFormat(); SwAttrSet aSet(pLnFormat->GetAttrSet()); m_pPropSet->setPropertyValue(*pEntry, aValue, aSet); pDoc->SetAttr(aSet, *pLnFormat); } } } } uno::Any SwXTextTableRow::getPropertyValue(const OUString& rPropertyName) { SolarMutexGuard aGuard; uno::Any aRet; SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast(this)); SwTable* pTable = SwTable::FindTable( pFormat ); SwTableLine* pLn = SwXTextTableRow::FindLine(pTable, pLine); if(pLn) { const SfxItemPropertySimpleEntry* pEntry = m_pPropSet->getPropertyMap().getByName(rPropertyName); if (!pEntry) throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) ); switch(pEntry->nWID) { case FN_UNO_ROW_HEIGHT: case FN_UNO_ROW_AUTO_HEIGHT: { const SwFormatFrameSize& rSize = pLn->GetFrameFormat()->GetFrameSize(); if(FN_UNO_ROW_AUTO_HEIGHT== pEntry->nWID) { aRet <<= SwFrameSize::Variable == rSize.GetHeightSizeType(); } else aRet <<= static_cast(convertTwipToMm100(rSize.GetSize().Height())); } break; case FN_UNO_TABLE_COLUMN_SEPARATORS: { lcl_GetTableSeparators(aRet, pTable, pLine->GetTabBoxes()[0], true); } break; default: { const SwAttrSet& rSet = pLn->GetFrameFormat()->GetAttrSet(); m_pPropSet->getPropertyValue(*pEntry, rSet, aRet); } } } return aRet; } void SwXTextTableRow::addPropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/) { throw uno::RuntimeException("not implemented", static_cast(this)); }; void SwXTextTableRow::removePropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/) { throw uno::RuntimeException("not implemented", static_cast(this)); }; void SwXTextTableRow::addVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/) { throw uno::RuntimeException("not implemented", static_cast(this)); }; void SwXTextTableRow::removeVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/) { throw uno::RuntimeException("not implemented", static_cast(this)); }; void SwXTextTableRow::Notify(const SfxHint& rHint) { if(rHint.GetId() == SfxHintId::Dying) { m_pFormat = nullptr; } else if(auto pFindHint = dynamic_cast*>(&rHint)) { if(!pFindHint->m_pCore && pFindHint->m_pCore == pLine) pFindHint->m_pResult = this; } } SwTableLine* SwXTextTableRow::FindLine(SwTable* pTable, SwTableLine const * pLine) { for(const auto& pCurrentLine : pTable->GetTabLines()) if(pCurrentLine == pLine) return pCurrentLine; return nullptr; } // SwXTextTableCursor OUString SwXTextTableCursor::getImplementationName() { return "SwXTextTableCursor"; } sal_Bool SwXTextTableCursor::supportsService(const OUString& rServiceName) { return cppu::supportsService(this, rServiceName); } void SwXTextTableCursor::acquire() throw() { SwXTextTableCursor_Base::acquire(); } void SwXTextTableCursor::release() throw() { SolarMutexGuard aGuard; SwXTextTableCursor_Base::release(); } css::uno::Any SAL_CALL SwXTextTableCursor::queryInterface( const css::uno::Type& _rType ) { css::uno::Any aReturn = SwXTextTableCursor_Base::queryInterface( _rType ); if ( !aReturn.hasValue() ) aReturn = OTextCursorHelper::queryInterface( _rType ); return aReturn; } const SwPaM* SwXTextTableCursor::GetPaM() const { return &GetCursor(); } SwPaM* SwXTextTableCursor::GetPaM() { return &GetCursor(); } const SwDoc* SwXTextTableCursor::GetDoc() const { return GetFrameFormat()->GetDoc(); } SwDoc* SwXTextTableCursor::GetDoc() { return GetFrameFormat()->GetDoc(); } const SwUnoCursor& SwXTextTableCursor::GetCursor() const { return *m_pUnoCursor; } SwUnoCursor& SwXTextTableCursor::GetCursor() { return *m_pUnoCursor; } uno::Sequence SwXTextTableCursor::getSupportedServiceNames() { return {"com.sun.star.text.TextTableCursor"}; } SwXTextTableCursor::SwXTextTableCursor(SwFrameFormat* pFrameFormat, SwTableBox const* pBox) : m_pFrameFormat(pFrameFormat) , m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_TABLE_CURSOR)) { StartListening(m_pFrameFormat->GetNotifier()); SwDoc* pDoc = m_pFrameFormat->GetDoc(); const SwStartNode* pSttNd = pBox->GetSttNd(); SwPosition aPos(*pSttNd); m_pUnoCursor = pDoc->CreateUnoCursor(aPos, true); m_pUnoCursor->Move( fnMoveForward, GoInNode ); SwUnoTableCursor& rTableCursor = dynamic_cast(*m_pUnoCursor); rTableCursor.MakeBoxSels(); } SwXTextTableCursor::SwXTextTableCursor(SwFrameFormat& rTableFormat, const SwTableCursor* pTableSelection) : m_pFrameFormat(&rTableFormat) , m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_TABLE_CURSOR)) { StartListening(m_pFrameFormat->GetNotifier()); m_pUnoCursor = pTableSelection->GetDoc()->CreateUnoCursor(*pTableSelection->GetPoint(), true); if(pTableSelection->HasMark()) { m_pUnoCursor->SetMark(); *m_pUnoCursor->GetMark() = *pTableSelection->GetMark(); } const SwSelBoxes& rBoxes = pTableSelection->GetSelectedBoxes(); SwUnoTableCursor& rTableCursor = dynamic_cast(*m_pUnoCursor); for(auto pBox : rBoxes) rTableCursor.InsertBox(*pBox); rTableCursor.MakeBoxSels(); } OUString SwXTextTableCursor::getRangeName() { SolarMutexGuard aGuard; SwUnoCursor& rUnoCursor = GetCursor(); SwUnoTableCursor* pTableCursor = dynamic_cast(&rUnoCursor); //!! see also SwChartDataSequence::getSourceRangeRepresentation if(!pTableCursor) return OUString(); pTableCursor->MakeBoxSels(); const SwStartNode* pNode = pTableCursor->GetPoint()->nNode.GetNode().FindTableBoxStartNode(); const SwTable* pTable = SwTable::FindTable(GetFrameFormat()); const SwTableBox* pEndBox = pTable->GetTableBox(pNode->GetIndex()); if(pTableCursor->HasMark()) { pNode = pTableCursor->GetMark()->nNode.GetNode().FindTableBoxStartNode(); const SwTableBox* pStartBox = pTable->GetTableBox(pNode->GetIndex()); if(pEndBox != pStartBox) { // need to switch start and end? if(*pTableCursor->GetPoint() < *pTableCursor->GetMark()) std::swap(pStartBox, pEndBox); return pStartBox->GetName() + ":" + pEndBox->GetName(); } } return pEndBox->GetName(); } sal_Bool SwXTextTableCursor::gotoCellByName(const OUString& sCellName, sal_Bool bExpand) { SolarMutexGuard aGuard; SwUnoCursor& rUnoCursor = GetCursor(); auto& rTableCursor = dynamic_cast(rUnoCursor); lcl_CursorSelect(rTableCursor, bExpand); return rTableCursor.GotoTableBox(sCellName); } sal_Bool SwXTextTableCursor::goLeft(sal_Int16 Count, sal_Bool bExpand) { SolarMutexGuard aGuard; SwUnoCursor& rUnoCursor = GetCursor(); SwUnoTableCursor& rTableCursor = dynamic_cast(rUnoCursor); lcl_CursorSelect(rTableCursor, bExpand); return rTableCursor.Left(Count); } sal_Bool SwXTextTableCursor::goRight(sal_Int16 Count, sal_Bool bExpand) { SolarMutexGuard aGuard; SwUnoCursor& rUnoCursor = GetCursor(); SwUnoTableCursor& rTableCursor = dynamic_cast(rUnoCursor); lcl_CursorSelect(rTableCursor, bExpand); return rTableCursor.Right(Count); } sal_Bool SwXTextTableCursor::goUp(sal_Int16 Count, sal_Bool bExpand) { SolarMutexGuard aGuard; SwUnoCursor& rUnoCursor = GetCursor(); SwUnoTableCursor& rTableCursor = dynamic_cast(rUnoCursor); lcl_CursorSelect(rTableCursor, bExpand); return rTableCursor.UpDown(true, Count, nullptr, 0, *rUnoCursor.GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()); } sal_Bool SwXTextTableCursor::goDown(sal_Int16 Count, sal_Bool bExpand) { SolarMutexGuard aGuard; SwUnoCursor& rUnoCursor = GetCursor(); SwUnoTableCursor& rTableCursor = dynamic_cast(rUnoCursor); lcl_CursorSelect(rTableCursor, bExpand); return rTableCursor.UpDown(false, Count, nullptr, 0, *rUnoCursor.GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()); } void SwXTextTableCursor::gotoStart(sal_Bool bExpand) { SolarMutexGuard aGuard; SwUnoCursor& rUnoCursor = GetCursor(); SwUnoTableCursor& rTableCursor = dynamic_cast(rUnoCursor); lcl_CursorSelect(rTableCursor, bExpand); rTableCursor.MoveTable(GotoCurrTable, fnTableStart); } void SwXTextTableCursor::gotoEnd(sal_Bool bExpand) { SolarMutexGuard aGuard; SwUnoCursor& rUnoCursor = GetCursor(); SwUnoTableCursor& rTableCursor = dynamic_cast(rUnoCursor); lcl_CursorSelect(rTableCursor, bExpand); rTableCursor.MoveTable(GotoCurrTable, fnTableEnd); } sal_Bool SwXTextTableCursor::mergeRange() { SolarMutexGuard aGuard; SwUnoCursor& rUnoCursor = GetCursor(); SwUnoTableCursor& rTableCursor = dynamic_cast(rUnoCursor); { // HACK: remove pending actions for selecting old style tables UnoActionRemoveContext aRemoveContext(rTableCursor); } rTableCursor.MakeBoxSels(); bool bResult; { UnoActionContext aContext(rUnoCursor.GetDoc()); bResult = TableMergeErr::Ok == rTableCursor.GetDoc()->MergeTable(rTableCursor); } if(bResult) { size_t nCount = rTableCursor.GetSelectedBoxesCount(); while (nCount--) rTableCursor.DeleteBox(nCount); } rTableCursor.MakeBoxSels(); return bResult; } sal_Bool SwXTextTableCursor::splitRange(sal_Int16 Count, sal_Bool Horizontal) { SolarMutexGuard aGuard; if (Count <= 0) throw uno::RuntimeException("Illegal first argument: needs to be > 0", static_cast(this)); SwUnoCursor& rUnoCursor = GetCursor(); SwUnoTableCursor& rTableCursor = dynamic_cast(rUnoCursor); { // HACK: remove pending actions for selecting old style tables UnoActionRemoveContext aRemoveContext(rTableCursor); } rTableCursor.MakeBoxSels(); bool bResult; { UnoActionContext aContext(rUnoCursor.GetDoc()); bResult = rTableCursor.GetDoc()->SplitTable(rTableCursor.GetSelectedBoxes(), !Horizontal, Count); } rTableCursor.MakeBoxSels(); return bResult; } uno::Reference< beans::XPropertySetInfo > SwXTextTableCursor::getPropertySetInfo() { static uno::Reference< beans::XPropertySetInfo > xRef = m_pPropSet->getPropertySetInfo(); return xRef; } void SwXTextTableCursor::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue) { SolarMutexGuard aGuard; SwUnoCursor& rUnoCursor = GetCursor(); auto pEntry(m_pPropSet->getPropertyMap().getByName(rPropertyName)); if(!pEntry) throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast(this)); if(pEntry->nFlags & beans::PropertyAttribute::READONLY) throw beans::PropertyVetoException("Property is read-only: " + rPropertyName, static_cast(this)); { auto pSttNode = rUnoCursor.GetNode().StartOfSectionNode(); const SwTableNode* pTableNode = pSttNode->FindTableNode(); lcl_FormatTable(pTableNode->GetTable().GetFrameFormat()); } auto& rTableCursor = dynamic_cast(rUnoCursor); rTableCursor.MakeBoxSels(); SwDoc* pDoc = rUnoCursor.GetDoc(); switch(pEntry->nWID) { case FN_UNO_TABLE_CELL_BACKGROUND: { std::unique_ptr aBrush(std::make_unique(RES_BACKGROUND)); SwDoc::GetBoxAttr(rUnoCursor, aBrush); aBrush->PutValue(aValue, pEntry->nMemberId); pDoc->SetBoxAttr(rUnoCursor, *aBrush); } break; case RES_BOXATR_FORMAT: { SfxUInt32Item aNumberFormat(RES_BOXATR_FORMAT); aNumberFormat.PutValue(aValue, 0); pDoc->SetBoxAttr(rUnoCursor, aNumberFormat); } break; case FN_UNO_PARA_STYLE: SwUnoCursorHelper::SetTextFormatColl(aValue, rUnoCursor); break; default: { SfxItemSet aItemSet(pDoc->GetAttrPool(), {{pEntry->nWID, pEntry->nWID}}); SwUnoCursorHelper::GetCursorAttr(rTableCursor.GetSelRing(), aItemSet); if (!SwUnoCursorHelper::SetCursorPropertyValue( *pEntry, aValue, rTableCursor.GetSelRing(), aItemSet)) { m_pPropSet->setPropertyValue(*pEntry, aValue, aItemSet); } SwUnoCursorHelper::SetCursorAttr(rTableCursor.GetSelRing(), aItemSet, SetAttrMode::DEFAULT, true); } } } uno::Any SwXTextTableCursor::getPropertyValue(const OUString& rPropertyName) { SolarMutexGuard aGuard; SwUnoCursor& rUnoCursor = GetCursor(); { auto pSttNode = rUnoCursor.GetNode().StartOfSectionNode(); const SwTableNode* pTableNode = pSttNode->FindTableNode(); lcl_FormatTable(pTableNode->GetTable().GetFrameFormat()); } SwUnoTableCursor& rTableCursor = dynamic_cast(rUnoCursor); auto pEntry(m_pPropSet->getPropertyMap().getByName(rPropertyName)); if(!pEntry) throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast(this)); rTableCursor.MakeBoxSels(); uno::Any aResult; switch(pEntry->nWID) { case FN_UNO_TABLE_CELL_BACKGROUND: { std::unique_ptr aBrush(std::make_unique(RES_BACKGROUND)); if (SwDoc::GetBoxAttr(rUnoCursor, aBrush)) aBrush->QueryValue(aResult, pEntry->nMemberId); } break; case RES_BOXATR_FORMAT: // TODO: GetAttr for table selections in a Doc is missing throw uno::RuntimeException("Unknown property: " + rPropertyName, static_cast(this)); break; case FN_UNO_PARA_STYLE: { auto pFormat(SwUnoCursorHelper::GetCurTextFormatColl(rUnoCursor, false)); if(pFormat) aResult <<= pFormat->GetName(); } break; default: { SfxItemSet aSet(rTableCursor.GetDoc()->GetAttrPool(), svl::Items{}); SwUnoCursorHelper::GetCursorAttr(rTableCursor.GetSelRing(), aSet); m_pPropSet->getPropertyValue(*pEntry, aSet, aResult); } } return aResult; } void SwXTextTableCursor::addPropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/) { throw uno::RuntimeException("not implemented", static_cast(this)); }; void SwXTextTableCursor::removePropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/) { throw uno::RuntimeException("not implemented", static_cast(this)); }; void SwXTextTableCursor::addVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/) { throw uno::RuntimeException("not implemented", static_cast(this)); }; void SwXTextTableCursor::removeVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/) { throw uno::RuntimeException("not implemented", static_cast(this)); }; void SwXTextTableCursor::Notify( const SfxHint& rHint ) { if(rHint.GetId() == SfxHintId::Dying) m_pFrameFormat = nullptr; } // SwXTextTable =========================================================== namespace { class SwTableProperties_Impl { SwUnoCursorHelper::SwAnyMapHelper aAnyMap; public: SwTableProperties_Impl(); void SetProperty(sal_uInt16 nWhichId, sal_uInt16 nMemberId, const uno::Any& aVal); bool GetProperty(sal_uInt16 nWhichId, sal_uInt16 nMemberId, const uno::Any*& rpAny); void AddItemToSet(SfxItemSet& rSet, std::function()> aItemFactory, sal_uInt16 nWhich, std::initializer_list vMember, bool bAddTwips = false); void ApplyTableAttr(const SwTable& rTable, SwDoc& rDoc); }; } SwTableProperties_Impl::SwTableProperties_Impl() { } void SwTableProperties_Impl::SetProperty(sal_uInt16 nWhichId, sal_uInt16 nMemberId, const uno::Any& rVal) { aAnyMap.SetValue( nWhichId, nMemberId, rVal ); } bool SwTableProperties_Impl::GetProperty(sal_uInt16 nWhichId, sal_uInt16 nMemberId, const uno::Any*& rpAny ) { return aAnyMap.FillValue( nWhichId, nMemberId, rpAny ); } void SwTableProperties_Impl::AddItemToSet(SfxItemSet& rSet, std::function()> aItemFactory, sal_uInt16 nWhich, std::initializer_list vMember, bool bAddTwips) { std::vector< std::pair > vMemberAndAny; for(sal_uInt16 nMember : vMember) { const uno::Any* pAny = nullptr; GetProperty(nWhich, nMember, pAny); if(pAny) vMemberAndAny.emplace_back(nMember, pAny); } if(!vMemberAndAny.empty()) { std::unique_ptr aItem(aItemFactory()); for(const auto& aMemberAndAny : vMemberAndAny) aItem->PutValue(*aMemberAndAny.second, aMemberAndAny.first | (bAddTwips ? CONVERT_TWIPS : 0) ); rSet.Put(*aItem); } } void SwTableProperties_Impl::ApplyTableAttr(const SwTable& rTable, SwDoc& rDoc) { SfxItemSet aSet( rDoc.GetAttrPool(), svl::Items< RES_FRM_SIZE, RES_BREAK, RES_HORI_ORIENT, RES_HORI_ORIENT, RES_BACKGROUND, RES_BACKGROUND, RES_SHADOW, RES_SHADOW, RES_KEEP, RES_KEEP, RES_LAYOUT_SPLIT, RES_LAYOUT_SPLIT>{}); const uno::Any* pRepHead; const SwFrameFormat &rFrameFormat = *rTable.GetFrameFormat(); if(GetProperty(FN_TABLE_HEADLINE_REPEAT, 0xff, pRepHead )) { bool bVal(pRepHead->get()); const_cast(rTable).SetRowsToRepeat( bVal ? 1 : 0 ); // TODO: MULTIHEADER } AddItemToSet(aSet, [&rFrameFormat]() { return rFrameFormat.makeBackgroundBrushItem(); }, RES_BACKGROUND, { MID_BACK_COLOR, MID_GRAPHIC_TRANSPARENT, MID_GRAPHIC_POSITION, MID_GRAPHIC, MID_GRAPHIC_FILTER }); bool bPutBreak = true; const uno::Any* pPage; if(GetProperty(FN_UNO_PAGE_STYLE, 0, pPage) || GetProperty(RES_PAGEDESC, 0xff, pPage)) { OUString sPageStyle = pPage->get(); if(!sPageStyle.isEmpty()) { SwStyleNameMapper::FillUIName(sPageStyle, sPageStyle, SwGetPoolIdFromName::PageDesc); const SwPageDesc* pDesc = SwPageDesc::GetByName(rDoc, sPageStyle); if(pDesc) { SwFormatPageDesc aDesc(pDesc); const uno::Any* pPgNo; if(GetProperty(RES_PAGEDESC, MID_PAGEDESC_PAGENUMOFFSET, pPgNo)) { aDesc.SetNumOffset(pPgNo->get()); } aSet.Put(aDesc); bPutBreak = false; } } } if(bPutBreak) AddItemToSet(aSet, [&rFrameFormat]() { return std::unique_ptr(rFrameFormat.GetBreak().Clone()); }, RES_BREAK, {0}); AddItemToSet(aSet, [&rFrameFormat]() { return std::unique_ptr(rFrameFormat.GetShadow().Clone()); }, RES_SHADOW, {0}, true); AddItemToSet(aSet, [&rFrameFormat]() { return std::unique_ptr(rFrameFormat.GetKeep().Clone()); }, RES_KEEP, {0}); AddItemToSet(aSet, [&rFrameFormat]() { return std::unique_ptr(rFrameFormat.GetHoriOrient().Clone()); }, RES_HORI_ORIENT, {MID_HORIORIENT_ORIENT}, true); const uno::Any* pSzRel(nullptr); GetProperty(FN_TABLE_IS_RELATIVE_WIDTH, 0xff, pSzRel); const uno::Any* pRelWidth(nullptr); GetProperty(FN_TABLE_RELATIVE_WIDTH, 0xff, pRelWidth); const uno::Any* pWidth(nullptr); GetProperty(FN_TABLE_WIDTH, 0xff, pWidth); bool bPutSize = pWidth != nullptr; SwFormatFrameSize aSz(SwFrameSize::Variable); if(pWidth) { aSz.PutValue(*pWidth, MID_FRMSIZE_WIDTH); bPutSize = true; } if(pSzRel && pSzRel->get() && pRelWidth) { aSz.PutValue(*pRelWidth, MID_FRMSIZE_REL_WIDTH|CONVERT_TWIPS); bPutSize = true; } if(bPutSize) { if(!aSz.GetWidth()) aSz.SetWidth(MINLAY); aSet.Put(aSz); } AddItemToSet(aSet, [&rFrameFormat]() { return std::unique_ptr(rFrameFormat.GetLRSpace().Clone()); }, RES_LR_SPACE, { MID_L_MARGIN|CONVERT_TWIPS, MID_R_MARGIN|CONVERT_TWIPS }); AddItemToSet(aSet, [&rFrameFormat]() { return std::unique_ptr(rFrameFormat.GetULSpace().Clone()); }, RES_UL_SPACE, { MID_UP_MARGIN|CONVERT_TWIPS, MID_LO_MARGIN|CONVERT_TWIPS }); const::uno::Any* pSplit(nullptr); if(GetProperty(RES_LAYOUT_SPLIT, 0, pSplit)) { SwFormatLayoutSplit aSp(pSplit->get()); aSet.Put(aSp); } if(aSet.Count()) { rDoc.SetAttr(aSet, *rTable.GetFrameFormat()); } } class SwXTextTable::Impl : public SvtListener { private: SwFrameFormat* m_pFrameFormat; ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2 public: uno::WeakReference m_wThis; ::cppu::OMultiTypeInterfaceContainerHelper m_Listeners; const SfxItemPropertySet * m_pPropSet; css::uno::WeakReference m_xRows; css::uno::WeakReference m_xColumns; bool m_bFirstRowAsLabel; bool m_bFirstColumnAsLabel; // Descriptor-interface std::unique_ptr m_pTableProps; OUString m_sTableName; unsigned short m_nRows; unsigned short m_nColumns; explicit Impl(SwFrameFormat* const pFrameFormat) : m_pFrameFormat(pFrameFormat) , m_Listeners(m_Mutex) , m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_TABLE)) , m_bFirstRowAsLabel(false) , m_bFirstColumnAsLabel(false) , m_pTableProps(pFrameFormat ? nullptr : new SwTableProperties_Impl) , m_nRows(pFrameFormat ? 0 : 2) , m_nColumns(pFrameFormat ? 0 : 2) { if(m_pFrameFormat) StartListening(m_pFrameFormat->GetNotifier()); } SwFrameFormat* GetFrameFormat() { return m_pFrameFormat; } void SetFrameFormat(SwFrameFormat& rFrameFormat) { EndListeningAll(); m_pFrameFormat = &rFrameFormat; StartListening(m_pFrameFormat->GetNotifier()); } bool IsDescriptor() const { return m_pTableProps != nullptr; } // note: lock mutex before calling this to avoid concurrent update static std::pair ThrowIfComplex(SwXTextTable &rThis) { sal_uInt16 const nRowCount(rThis.m_pImpl->GetRowCount()); sal_uInt16 const nColCount(rThis.m_pImpl->GetColumnCount()); if (!nRowCount || !nColCount) { throw uno::RuntimeException("Table too complex", static_cast(&rThis)); } return std::make_pair(nRowCount, nColCount); } sal_uInt16 GetRowCount(); sal_uInt16 GetColumnCount(); virtual void Notify(const SfxHint&) override; }; namespace { class theSwXTextTableUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextTableUnoTunnelId > {}; } const uno::Sequence< sal_Int8 > & SwXTextTable::getUnoTunnelId() { return theSwXTextTableUnoTunnelId::get().getSeq(); } sal_Int64 SAL_CALL SwXTextTable::getSomething( const uno::Sequence< sal_Int8 >& rId ) { if(isUnoTunnelId(rId)) { return sal::static_int_cast(reinterpret_cast(this)); } return 0; } SwXTextTable::SwXTextTable() : m_pImpl(new Impl(nullptr)) { } SwXTextTable::SwXTextTable(SwFrameFormat& rFrameFormat) : m_pImpl(new Impl(&rFrameFormat)) { } SwXTextTable::~SwXTextTable() { } uno::Reference SwXTextTable::CreateXTextTable(SwFrameFormat* const pFrameFormat) { uno::Reference xTable; if(pFrameFormat) xTable.set(pFrameFormat->GetXObject(), uno::UNO_QUERY); // cached? if(xTable.is()) return xTable; SwXTextTable* const pNew( pFrameFormat ? new SwXTextTable(*pFrameFormat) : new SwXTextTable()); xTable.set(pNew); if(pFrameFormat) pFrameFormat->SetXObject(xTable); // need a permanent Reference to initialize m_wThis pNew->m_pImpl->m_wThis = xTable; return xTable; } SwFrameFormat* SwXTextTable::GetFrameFormat() { return m_pImpl->GetFrameFormat(); } void SwXTextTable::initialize(sal_Int32 nR, sal_Int32 nC) { if (!m_pImpl->IsDescriptor() || nR <= 0 || nC <= 0 || nR >= SAL_MAX_UINT16 || nC >= SAL_MAX_UINT16) throw uno::RuntimeException(); m_pImpl->m_nRows = static_cast(nR); m_pImpl->m_nColumns = static_cast(nC); } uno::Reference SAL_CALL SwXTextTable::getRows() { SolarMutexGuard aGuard; uno::Reference xResult(m_pImpl->m_xRows); if(xResult.is()) return xResult; if(SwFrameFormat* pFormat = GetFrameFormat()) m_pImpl->m_xRows = xResult = new SwXTableRows(*pFormat); if(!xResult.is()) throw uno::RuntimeException(); return xResult; } uno::Reference SAL_CALL SwXTextTable::getColumns() { SolarMutexGuard aGuard; uno::Reference xResult(m_pImpl->m_xColumns); if(xResult.is()) return xResult; if(SwFrameFormat* pFormat = GetFrameFormat()) m_pImpl->m_xColumns = xResult = new SwXTableColumns(*pFormat); if(!xResult.is()) throw uno::RuntimeException(); return xResult; } uno::Reference SwXTextTable::getCellByName(const OUString& sCellName) { SolarMutexGuard aGuard; SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast(this)); SwTable* pTable = SwTable::FindTable(pFormat); SwTableBox* pBox = const_cast(pTable->GetTableBox(sCellName)); if(!pBox) return nullptr; return SwXCell::CreateXCell(pFormat, pBox); } uno::Sequence SwXTextTable::getCellNames() { SolarMutexGuard aGuard; SwFrameFormat* pFormat(GetFrameFormat()); if(!pFormat) return {}; SwTable* pTable = SwTable::FindTable(pFormat); // exists at the table and at all boxes SwTableLines& rTableLines = pTable->GetTabLines(); std::vector aAllNames; lcl_InspectLines(rTableLines, aAllNames); return comphelper::containerToSequence(aAllNames); } uno::Reference SwXTextTable::createCursorByCellName(const OUString& sCellName) { SolarMutexGuard aGuard; SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast(this)); SwTable* pTable = SwTable::FindTable(pFormat); SwTableBox* pBox = const_cast(pTable->GetTableBox(sCellName)); if(!pBox || pBox->getRowSpan() == 0) throw uno::RuntimeException(); return new SwXTextTableCursor(pFormat, pBox); } void SAL_CALL SwXTextTable::attach(const uno::Reference & xTextRange) { SolarMutexGuard aGuard; // attach() must only be called once if (!m_pImpl->IsDescriptor()) /* already attached ? */ throw uno::RuntimeException("SwXTextTable: already attached to range.", static_cast(this)); uno::Reference xRangeTunnel(xTextRange, uno::UNO_QUERY); SwXTextRange* pRange(nullptr); OTextCursorHelper* pCursor(nullptr); if(xRangeTunnel.is()) { pRange = reinterpret_cast( sal::static_int_cast(xRangeTunnel->getSomething(SwXTextRange::getUnoTunnelId()))); pCursor = reinterpret_cast( sal::static_int_cast(xRangeTunnel->getSomething(OTextCursorHelper::getUnoTunnelId()))); } SwDoc* pDoc = pRange ? &pRange->GetDoc() : pCursor ? pCursor->GetDoc() : nullptr; if (!pDoc || !m_pImpl->m_nRows || !m_pImpl->m_nColumns) throw lang::IllegalArgumentException(); SwUnoInternalPaM aPam(*pDoc); // this now needs to return TRUE ::sw::XTextRangeToSwPaM(aPam, xTextRange); { UnoActionContext aCont(pDoc); pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr); const SwTable* pTable(nullptr); if( 0 != aPam.Start()->nContent.GetIndex() ) { pDoc->getIDocumentContentOperations().SplitNode(*aPam.Start(), false); } //TODO: if it is the last paragraph than add another one! if(aPam.HasMark()) { pDoc->getIDocumentContentOperations().DeleteAndJoin(aPam); aPam.DeleteMark(); } pTable = pDoc->InsertTable(SwInsertTableOptions( SwInsertTableFlags::Headline | SwInsertTableFlags::DefaultBorder | SwInsertTableFlags::SplitLayout, 0 ), *aPam.GetPoint(), m_pImpl->m_nRows, m_pImpl->m_nColumns, text::HoriOrientation::FULL); if(pTable) { // here, the properties of the descriptor need to be analyzed m_pImpl->m_pTableProps->ApplyTableAttr(*pTable, *pDoc); SwFrameFormat* pTableFormat(pTable->GetFrameFormat()); lcl_FormatTable(pTableFormat); m_pImpl->SetFrameFormat(*pTableFormat); if (!m_pImpl->m_sTableName.isEmpty()) { sal_uInt16 nIndex = 1; OUString sTmpNameIndex(m_pImpl->m_sTableName); while(pDoc->FindTableFormatByName(sTmpNameIndex, true) && nIndex < USHRT_MAX) { sTmpNameIndex = m_pImpl->m_sTableName + OUString::number(nIndex++); } pDoc->SetTableName( *pTableFormat, sTmpNameIndex); } const::uno::Any* pName; if (m_pImpl->m_pTableProps->GetProperty(FN_UNO_TABLE_NAME, 0, pName)) setName(pName->get()); m_pImpl->m_pTableProps.reset(); } pDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr ); } } uno::Reference SwXTextTable::getAnchor() { SolarMutexGuard aGuard; SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast(this)); return new SwXTextRange(*pFormat); } void SwXTextTable::dispose() { SolarMutexGuard aGuard; SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast(this)); SwTable* pTable = SwTable::FindTable(pFormat); SwSelBoxes aSelBoxes; for(auto& rBox : pTable->GetTabSortBoxes() ) aSelBoxes.insert(rBox); pFormat->GetDoc()->DeleteRowCol(aSelBoxes); } void SAL_CALL SwXTextTable::addEventListener( const uno::Reference & xListener) { // no need to lock here as m_pImpl is const and container threadsafe m_pImpl->m_Listeners.addInterface( cppu::UnoType::get(), xListener); } void SAL_CALL SwXTextTable::removeEventListener( const uno::Reference< lang::XEventListener > & xListener) { // no need to lock here as m_pImpl is const and container threadsafe m_pImpl->m_Listeners.removeInterface( cppu::UnoType::get(), xListener); } uno::Reference SwXTextTable::getCellByPosition(sal_Int32 nColumn, sal_Int32 nRow) { SolarMutexGuard aGuard; SwFrameFormat* pFormat(GetFrameFormat()); // sheet is unimportant if(nColumn >= 0 && nRow >= 0 && pFormat) { auto pXCell = lcl_CreateXCell(pFormat, nColumn, nRow); if(pXCell) return pXCell; } throw lang::IndexOutOfBoundsException(); } namespace { uno::Reference GetRangeByName( SwFrameFormat* pFormat, SwTable const * pTable, const OUString& rTLName, const OUString& rBRName, SwRangeDescriptor const & rDesc) { const SwTableBox* pTLBox = pTable->GetTableBox(rTLName); if(!pTLBox) return nullptr; const SwStartNode* pSttNd = pTLBox->GetSttNd(); SwPosition aPos(*pSttNd); // set cursor to the upper-left cell of the range auto pUnoCursor(pFormat->GetDoc()->CreateUnoCursor(aPos, true)); pUnoCursor->Move(fnMoveForward, GoInNode); pUnoCursor->SetRemainInSection(false); const SwTableBox* pBRBox(pTable->GetTableBox(rBRName)); if(!pBRBox) return nullptr; pUnoCursor->SetMark(); pUnoCursor->GetPoint()->nNode = *pBRBox->GetSttNd(); pUnoCursor->Move( fnMoveForward, GoInNode ); SwUnoTableCursor& rCursor = dynamic_cast(*pUnoCursor); // HACK: remove pending actions for selecting old style tables UnoActionRemoveContext aRemoveContext(rCursor); rCursor.MakeBoxSels(); // pUnoCursor will be provided and will not be deleted return SwXCellRange::CreateXCellRange(pUnoCursor, *pFormat, rDesc).get(); } } // namespace uno::Reference SwXTextTable::getCellRangeByPosition(sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom) { SolarMutexGuard aGuard; SwFrameFormat* pFormat(GetFrameFormat()); if(pFormat && nLeft <= nRight && nTop <= nBottom && nLeft >= 0 && nRight >= 0 && nTop >= 0 && nBottom >= 0 ) { SwTable* pTable = SwTable::FindTable(pFormat); if(!pTable->IsTableComplex()) { SwRangeDescriptor aDesc; aDesc.nTop = nTop; aDesc.nBottom = nBottom; aDesc.nLeft = nLeft; aDesc.nRight = nRight; const OUString sTLName = sw_GetCellName(aDesc.nLeft, aDesc.nTop); const OUString sBRName = sw_GetCellName(aDesc.nRight, aDesc.nBottom); // please note that according to the 'if' statement at the begin // sTLName:sBRName already denotes the normalized range string return GetRangeByName(pFormat, pTable, sTLName, sBRName, aDesc); } } throw lang::IndexOutOfBoundsException(); } uno::Reference SwXTextTable::getCellRangeByName(const OUString& sRange) { SolarMutexGuard aGuard; SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast(this)); SwTable* pTable = lcl_EnsureTableNotComplex(SwTable::FindTable(pFormat), static_cast(this)); sal_Int32 nPos = 0; const OUString sTLName(sRange.getToken(0, ':', nPos)); const OUString sBRName(sRange.getToken(0, ':', nPos)); if(sTLName.isEmpty() || sBRName.isEmpty()) throw uno::RuntimeException(); SwRangeDescriptor aDesc; aDesc.nTop = aDesc.nLeft = aDesc.nBottom = aDesc.nRight = -1; SwXTextTable::GetCellPosition(sTLName, aDesc.nLeft, aDesc.nTop ); SwXTextTable::GetCellPosition(sBRName, aDesc.nRight, aDesc.nBottom ); // we should normalize the range now (e.g. A5:C1 will become A1:C5) // since (depending on what is done later) it will be troublesome // elsewhere when the cursor in the implementation does not // point to the top-left and bottom-right cells aDesc.Normalize(); return GetRangeByName(pFormat, pTable, sTLName, sBRName, aDesc); } uno::Sequence< uno::Sequence< uno::Any > > SAL_CALL SwXTextTable::getDataArray() { SolarMutexGuard aGuard; std::pair const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this)); uno::Reference const xAllRange( getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1), uno::UNO_QUERY_THROW); return xAllRange->getDataArray(); } void SAL_CALL SwXTextTable::setDataArray(const uno::Sequence< uno::Sequence< uno::Any > >& rArray) { SolarMutexGuard aGuard; std::pair const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this)); uno::Reference const xAllRange( getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1), uno::UNO_QUERY_THROW); return xAllRange->setDataArray(rArray); } uno::Sequence< uno::Sequence< double > > SwXTextTable::getData() { SolarMutexGuard aGuard; std::pair const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this)); uno::Reference const xAllRange( getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1), uno::UNO_QUERY_THROW); static_cast(xAllRange.get())->SetLabels( m_pImpl->m_bFirstRowAsLabel, m_pImpl->m_bFirstColumnAsLabel); return xAllRange->getData(); } void SwXTextTable::setData(const uno::Sequence< uno::Sequence< double > >& rData) { SolarMutexGuard aGuard; std::pair const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this)); uno::Reference const xAllRange( getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1), uno::UNO_QUERY_THROW); static_cast(xAllRange.get())->SetLabels( m_pImpl->m_bFirstRowAsLabel, m_pImpl->m_bFirstColumnAsLabel); xAllRange->setData(rData); // this is rather inconsistent: setData on XTextTable sends events, but e.g. CellRanges do not lcl_SendChartEvent(*this, m_pImpl->m_Listeners); } uno::Sequence SwXTextTable::getRowDescriptions() { SolarMutexGuard aGuard; std::pair const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this)); uno::Reference const xAllRange( getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1), uno::UNO_QUERY_THROW); static_cast(xAllRange.get())->SetLabels( m_pImpl->m_bFirstRowAsLabel, m_pImpl->m_bFirstColumnAsLabel); return xAllRange->getRowDescriptions(); } void SwXTextTable::setRowDescriptions(const uno::Sequence& rRowDesc) { SolarMutexGuard aGuard; std::pair const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this)); uno::Reference const xAllRange( getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1), uno::UNO_QUERY_THROW); static_cast(xAllRange.get())->SetLabels( m_pImpl->m_bFirstRowAsLabel, m_pImpl->m_bFirstColumnAsLabel); xAllRange->setRowDescriptions(rRowDesc); } uno::Sequence SwXTextTable::getColumnDescriptions() { SolarMutexGuard aGuard; std::pair const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this)); uno::Reference const xAllRange( getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1), uno::UNO_QUERY_THROW); static_cast(xAllRange.get())->SetLabels( m_pImpl->m_bFirstRowAsLabel, m_pImpl->m_bFirstColumnAsLabel); return xAllRange->getColumnDescriptions(); } void SwXTextTable::setColumnDescriptions(const uno::Sequence& rColumnDesc) { SolarMutexGuard aGuard; std::pair const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this)); uno::Reference const xAllRange( getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1), uno::UNO_QUERY_THROW); static_cast(xAllRange.get())->SetLabels( m_pImpl->m_bFirstRowAsLabel, m_pImpl->m_bFirstColumnAsLabel); return xAllRange->setColumnDescriptions(rColumnDesc); } void SAL_CALL SwXTextTable::addChartDataChangeEventListener( const uno::Reference & xListener) { // no need to lock here as m_pImpl is const and container threadsafe m_pImpl->m_Listeners.addInterface( cppu::UnoType::get(), xListener); } void SAL_CALL SwXTextTable::removeChartDataChangeEventListener( const uno::Reference & xListener) { // no need to lock here as m_pImpl is const and container threadsafe m_pImpl->m_Listeners.removeInterface( cppu::UnoType::get(), xListener); } sal_Bool SwXTextTable::isNotANumber(double nNumber) { // We use DBL_MIN because starcalc does (which uses it because chart // wants it that way!) return ( nNumber == DBL_MIN ); } double SwXTextTable::getNotANumber() { // We use DBL_MIN because starcalc does (which uses it because chart // wants it that way!) return DBL_MIN; } uno::Sequence< beans::PropertyValue > SwXTextTable::createSortDescriptor() { SolarMutexGuard aGuard; return SwUnoCursorHelper::CreateSortDescriptor(true); } void SwXTextTable::sort(const uno::Sequence< beans::PropertyValue >& rDescriptor) { SolarMutexGuard aGuard; SwSortOptions aSortOpt; SwFrameFormat* pFormat = GetFrameFormat(); if(!(pFormat && SwUnoCursorHelper::ConvertSortProperties(rDescriptor, aSortOpt))) return; SwTable* pTable = SwTable::FindTable( pFormat ); SwSelBoxes aBoxes; const SwTableSortBoxes& rTBoxes = pTable->GetTabSortBoxes(); for (size_t n = 0; n < rTBoxes.size(); ++n) { SwTableBox* pBox = rTBoxes[ n ]; aBoxes.insert( pBox ); } UnoActionContext aContext( pFormat->GetDoc() ); pFormat->GetDoc()->SortTable(aBoxes, aSortOpt); } void SwXTextTable::autoFormat(const OUString& sAutoFormatName) { SolarMutexGuard aGuard; SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast(this)); SwTable* pTable = lcl_EnsureTableNotComplex(SwTable::FindTable(pFormat), static_cast(this)); SwTableAutoFormatTable aAutoFormatTable; aAutoFormatTable.Load(); for (size_t i = aAutoFormatTable.size(); i;) if( sAutoFormatName == aAutoFormatTable[ --i ].GetName() ) { SwSelBoxes aBoxes; const SwTableSortBoxes& rTBoxes = pTable->GetTabSortBoxes(); for (size_t n = 0; n < rTBoxes.size(); ++n) { SwTableBox* pBox = rTBoxes[ n ]; aBoxes.insert( pBox ); } UnoActionContext aContext( pFormat->GetDoc() ); pFormat->GetDoc()->SetTableAutoFormat( aBoxes, aAutoFormatTable[i] ); break; } } uno::Reference< beans::XPropertySetInfo > SwXTextTable::getPropertySetInfo() { static uno::Reference xRef = m_pImpl->m_pPropSet->getPropertySetInfo(); return xRef; } void SwXTextTable::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue) { SolarMutexGuard aGuard; SwFrameFormat* pFormat = GetFrameFormat(); if(!aValue.hasValue()) throw lang::IllegalArgumentException(); const SfxItemPropertySimpleEntry* pEntry = m_pImpl->m_pPropSet->getPropertyMap().getByName(rPropertyName); if( !pEntry ) throw lang::IllegalArgumentException(); if(pFormat) { if ( pEntry->nFlags & beans::PropertyAttribute::READONLY) throw beans::PropertyVetoException("Property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) ); if(0xBF == pEntry->nMemberId) { lcl_SetSpecialProperty(pFormat, pEntry, aValue); } else { switch(pEntry->nWID) { case FN_UNO_TABLE_NAME : { OUString sName; aValue >>= sName; setName( sName ); } break; case FN_UNO_RANGE_ROW_LABEL: { bool bTmp = *o3tl::doAccess(aValue); if (m_pImpl->m_bFirstRowAsLabel != bTmp) { lcl_SendChartEvent(*this, m_pImpl->m_Listeners); m_pImpl->m_bFirstRowAsLabel = bTmp; } } break; case FN_UNO_RANGE_COL_LABEL: { bool bTmp = *o3tl::doAccess(aValue); if (m_pImpl->m_bFirstColumnAsLabel != bTmp) { lcl_SendChartEvent(*this, m_pImpl->m_Listeners); m_pImpl->m_bFirstColumnAsLabel = bTmp; } } break; case FN_UNO_TABLE_BORDER: case FN_UNO_TABLE_BORDER2: { table::TableBorder oldBorder; table::TableBorder2 aBorder; SvxBorderLine aTopLine; SvxBorderLine aBottomLine; SvxBorderLine aLeftLine; SvxBorderLine aRightLine; SvxBorderLine aHoriLine; SvxBorderLine aVertLine; if (aValue >>= oldBorder) { aBorder.IsTopLineValid = oldBorder.IsTopLineValid; aBorder.IsBottomLineValid = oldBorder.IsBottomLineValid; aBorder.IsLeftLineValid = oldBorder.IsLeftLineValid; aBorder.IsRightLineValid = oldBorder.IsRightLineValid; aBorder.IsHorizontalLineValid = oldBorder.IsHorizontalLineValid; aBorder.IsVerticalLineValid = oldBorder.IsVerticalLineValid; aBorder.Distance = oldBorder.Distance; aBorder.IsDistanceValid = oldBorder.IsDistanceValid; lcl_LineToSvxLine( oldBorder.TopLine, aTopLine); lcl_LineToSvxLine( oldBorder.BottomLine, aBottomLine); lcl_LineToSvxLine( oldBorder.LeftLine, aLeftLine); lcl_LineToSvxLine( oldBorder.RightLine, aRightLine); lcl_LineToSvxLine( oldBorder.HorizontalLine, aHoriLine); lcl_LineToSvxLine( oldBorder.VerticalLine, aVertLine); } else if (aValue >>= aBorder) { SvxBoxItem::LineToSvxLine( aBorder.TopLine, aTopLine, true); SvxBoxItem::LineToSvxLine( aBorder.BottomLine, aBottomLine, true); SvxBoxItem::LineToSvxLine( aBorder.LeftLine, aLeftLine, true); SvxBoxItem::LineToSvxLine( aBorder.RightLine, aRightLine, true); SvxBoxItem::LineToSvxLine( aBorder.HorizontalLine, aHoriLine, true); SvxBoxItem::LineToSvxLine( aBorder.VerticalLine, aVertLine, true); } else { break; // something else } SwDoc* pDoc = pFormat->GetDoc(); if(!lcl_FormatTable(pFormat)) break; SwTable* pTable = SwTable::FindTable( pFormat ); SwTableLines &rLines = pTable->GetTabLines(); const SwTableBox* pTLBox = lcl_FindCornerTableBox(rLines, true); const SwStartNode* pSttNd = pTLBox->GetSttNd(); SwPosition aPos(*pSttNd); // set cursor to top left cell auto pUnoCursor(pDoc->CreateUnoCursor(aPos, true)); pUnoCursor->Move( fnMoveForward, GoInNode ); pUnoCursor->SetRemainInSection( false ); const SwTableBox* pBRBox = lcl_FindCornerTableBox(rLines, false); pUnoCursor->SetMark(); pUnoCursor->GetPoint()->nNode = *pBRBox->GetSttNd(); pUnoCursor->Move( fnMoveForward, GoInNode ); SwUnoTableCursor& rCursor = dynamic_cast(*pUnoCursor); // HACK: remove pending actions for selecting old style tables UnoActionRemoveContext aRemoveContext(rCursor); rCursor.MakeBoxSels(); SfxItemSet aSet(pDoc->GetAttrPool(), svl::Items{}); SvxBoxItem aBox( RES_BOX ); SvxBoxInfoItem aBoxInfo( SID_ATTR_BORDER_INNER ); aBox.SetLine(aTopLine.isEmpty() ? nullptr : &aTopLine, SvxBoxItemLine::TOP); aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::TOP, aBorder.IsTopLineValid); aBox.SetLine(aBottomLine.isEmpty() ? nullptr : &aBottomLine, SvxBoxItemLine::BOTTOM); aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::BOTTOM, aBorder.IsBottomLineValid); aBox.SetLine(aLeftLine.isEmpty() ? nullptr : &aLeftLine, SvxBoxItemLine::LEFT); aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::LEFT, aBorder.IsLeftLineValid); aBox.SetLine(aRightLine.isEmpty() ? nullptr : &aRightLine, SvxBoxItemLine::RIGHT); aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::RIGHT, aBorder.IsRightLineValid); aBoxInfo.SetLine(aHoriLine.isEmpty() ? nullptr : &aHoriLine, SvxBoxInfoItemLine::HORI); aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI, aBorder.IsHorizontalLineValid); aBoxInfo.SetLine(aVertLine.isEmpty() ? nullptr : &aVertLine, SvxBoxInfoItemLine::VERT); aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::VERT, aBorder.IsVerticalLineValid); aBox.SetAllDistances(static_cast(convertMm100ToTwip(aBorder.Distance))); aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::DISTANCE, aBorder.IsDistanceValid); aSet.Put(aBox); aSet.Put(aBoxInfo); pDoc->SetTabBorders(rCursor, aSet); } break; case FN_UNO_TABLE_BORDER_DISTANCES: { table::TableBorderDistances aTableBorderDistances; if( !(aValue >>= aTableBorderDistances) || (!aTableBorderDistances.IsLeftDistanceValid && !aTableBorderDistances.IsRightDistanceValid && !aTableBorderDistances.IsTopDistanceValid && !aTableBorderDistances.IsBottomDistanceValid )) break; const sal_uInt16 nLeftDistance = convertMm100ToTwip(aTableBorderDistances.LeftDistance); const sal_uInt16 nRightDistance = convertMm100ToTwip(aTableBorderDistances.RightDistance); const sal_uInt16 nTopDistance = convertMm100ToTwip(aTableBorderDistances.TopDistance); const sal_uInt16 nBottomDistance = convertMm100ToTwip(aTableBorderDistances.BottomDistance); SwDoc* pDoc = pFormat->GetDoc(); SwTable* pTable = SwTable::FindTable( pFormat ); SwTableLines &rLines = pTable->GetTabLines(); pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::START, nullptr); for(size_t i = 0; i < rLines.size(); ++i) { SwTableLine* pLine = rLines[i]; SwTableBoxes& rBoxes = pLine->GetTabBoxes(); for(size_t k = 0; k < rBoxes.size(); ++k) { SwTableBox* pBox = rBoxes[k]; const SwFrameFormat* pBoxFormat = pBox->GetFrameFormat(); const SvxBoxItem& rBox = pBoxFormat->GetBox(); if( (aTableBorderDistances.IsLeftDistanceValid && nLeftDistance != rBox.GetDistance( SvxBoxItemLine::LEFT )) || (aTableBorderDistances.IsRightDistanceValid && nRightDistance != rBox.GetDistance( SvxBoxItemLine::RIGHT )) || (aTableBorderDistances.IsTopDistanceValid && nTopDistance != rBox.GetDistance( SvxBoxItemLine::TOP )) || (aTableBorderDistances.IsBottomDistanceValid && nBottomDistance != rBox.GetDistance( SvxBoxItemLine::BOTTOM ))) { SvxBoxItem aSetBox( rBox ); SwFrameFormat* pSetBoxFormat = pBox->ClaimFrameFormat(); if( aTableBorderDistances.IsLeftDistanceValid ) aSetBox.SetDistance( nLeftDistance, SvxBoxItemLine::LEFT ); if( aTableBorderDistances.IsRightDistanceValid ) aSetBox.SetDistance( nRightDistance, SvxBoxItemLine::RIGHT ); if( aTableBorderDistances.IsTopDistanceValid ) aSetBox.SetDistance( nTopDistance, SvxBoxItemLine::TOP ); if( aTableBorderDistances.IsBottomDistanceValid ) aSetBox.SetDistance( nBottomDistance, SvxBoxItemLine::BOTTOM ); pDoc->SetAttr( aSetBox, *pSetBoxFormat ); } } } pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr); } break; case FN_UNO_TABLE_COLUMN_SEPARATORS: { UnoActionContext aContext(pFormat->GetDoc()); SwTable* pTable = SwTable::FindTable( pFormat ); lcl_SetTableSeparators(aValue, pTable, pTable->GetTabLines()[0]->GetTabBoxes()[0], false, pFormat->GetDoc()); } break; case FN_UNO_TABLE_COLUMN_RELATIVE_SUM:/*_readonly_*/ break; case FN_UNO_TABLE_TEMPLATE_NAME: { SwTable* pTable = SwTable::FindTable(pFormat); OUString sName; if (!(aValue >>= sName)) break; SwStyleNameMapper::FillUIName(sName, sName, SwGetPoolIdFromName::TabStyle); pTable->SetTableStyleName(sName); SwDoc* pDoc = pFormat->GetDoc(); pDoc->GetDocShell()->GetFEShell()->UpdateTableStyleFormatting(pTable->GetTableNode()); } break; default: { SwAttrSet aSet(pFormat->GetAttrSet()); m_pImpl->m_pPropSet->setPropertyValue(*pEntry, aValue, aSet); pFormat->GetDoc()->SetAttr(aSet, *pFormat); } } } } else if (m_pImpl->IsDescriptor()) { m_pImpl->m_pTableProps->SetProperty(pEntry->nWID, pEntry->nMemberId, aValue); } else throw uno::RuntimeException(); } uno::Any SwXTextTable::getPropertyValue(const OUString& rPropertyName) { SolarMutexGuard aGuard; uno::Any aRet; SwFrameFormat* pFormat = GetFrameFormat(); const SfxItemPropertySimpleEntry* pEntry = m_pImpl->m_pPropSet->getPropertyMap().getByName(rPropertyName); if (!pEntry) throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) ); if(pFormat) { if(0xBF == pEntry->nMemberId) { aRet = lcl_GetSpecialProperty(pFormat, pEntry ); } else { switch(pEntry->nWID) { case FN_UNO_TABLE_NAME: { aRet <<= getName(); } break; case FN_UNO_ANCHOR_TYPES: case FN_UNO_TEXT_WRAP: case FN_UNO_ANCHOR_TYPE: ::sw::GetDefaultTextContentValue( aRet, OUString(), pEntry->nWID); break; case FN_UNO_RANGE_ROW_LABEL: { aRet <<= m_pImpl->m_bFirstRowAsLabel; } break; case FN_UNO_RANGE_COL_LABEL: aRet <<= m_pImpl->m_bFirstColumnAsLabel; break; case FN_UNO_TABLE_BORDER: case FN_UNO_TABLE_BORDER2: { SwDoc* pDoc = pFormat->GetDoc(); // tables without layout (invisible header/footer?) if(!lcl_FormatTable(pFormat)) break; SwTable* pTable = SwTable::FindTable( pFormat ); SwTableLines &rLines = pTable->GetTabLines(); const SwTableBox* pTLBox = lcl_FindCornerTableBox(rLines, true); const SwStartNode* pSttNd = pTLBox->GetSttNd(); SwPosition aPos(*pSttNd); // set cursor to top left cell auto pUnoCursor(pDoc->CreateUnoCursor(aPos, true)); pUnoCursor->Move( fnMoveForward, GoInNode ); pUnoCursor->SetRemainInSection( false ); const SwTableBox* pBRBox = lcl_FindCornerTableBox(rLines, false); pUnoCursor->SetMark(); const SwStartNode* pLastNd = pBRBox->GetSttNd(); pUnoCursor->GetPoint()->nNode = *pLastNd; pUnoCursor->Move( fnMoveForward, GoInNode ); SwUnoTableCursor& rCursor = dynamic_cast(*pUnoCursor); // HACK: remove pending actions for selecting old style tables UnoActionRemoveContext aRemoveContext(rCursor); rCursor.MakeBoxSels(); SfxItemSet aSet(pDoc->GetAttrPool(), svl::Items{}); aSet.Put(SvxBoxInfoItem( SID_ATTR_BORDER_INNER )); SwDoc::GetTabBorders(rCursor, aSet); const SvxBoxInfoItem& rBoxInfoItem = aSet.Get(SID_ATTR_BORDER_INNER); const SvxBoxItem& rBox = aSet.Get(RES_BOX); if (FN_UNO_TABLE_BORDER == pEntry->nWID) { table::TableBorder aTableBorder; aTableBorder.TopLine = SvxBoxItem::SvxLineToLine(rBox.GetTop(), true); aTableBorder.IsTopLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::TOP); aTableBorder.BottomLine = SvxBoxItem::SvxLineToLine(rBox.GetBottom(), true); aTableBorder.IsBottomLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::BOTTOM); aTableBorder.LeftLine = SvxBoxItem::SvxLineToLine(rBox.GetLeft(), true); aTableBorder.IsLeftLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::LEFT); aTableBorder.RightLine = SvxBoxItem::SvxLineToLine(rBox.GetRight(), true); aTableBorder.IsRightLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::RIGHT ); aTableBorder.HorizontalLine = SvxBoxItem::SvxLineToLine(rBoxInfoItem.GetHori(), true); aTableBorder.IsHorizontalLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::HORI); aTableBorder.VerticalLine = SvxBoxItem::SvxLineToLine(rBoxInfoItem.GetVert(), true); aTableBorder.IsVerticalLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::VERT); aTableBorder.Distance = convertTwipToMm100(rBox.GetSmallestDistance()); aTableBorder.IsDistanceValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::DISTANCE); aRet <<= aTableBorder; } else { table::TableBorder2 aTableBorder; aTableBorder.TopLine = SvxBoxItem::SvxLineToLine(rBox.GetTop(), true); aTableBorder.IsTopLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::TOP); aTableBorder.BottomLine = SvxBoxItem::SvxLineToLine(rBox.GetBottom(), true); aTableBorder.IsBottomLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::BOTTOM); aTableBorder.LeftLine = SvxBoxItem::SvxLineToLine(rBox.GetLeft(), true); aTableBorder.IsLeftLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::LEFT); aTableBorder.RightLine = SvxBoxItem::SvxLineToLine(rBox.GetRight(), true); aTableBorder.IsRightLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::RIGHT ); aTableBorder.HorizontalLine = SvxBoxItem::SvxLineToLine(rBoxInfoItem.GetHori(), true); aTableBorder.IsHorizontalLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::HORI); aTableBorder.VerticalLine = SvxBoxItem::SvxLineToLine(rBoxInfoItem.GetVert(), true); aTableBorder.IsVerticalLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::VERT); aTableBorder.Distance = convertTwipToMm100(rBox.GetSmallestDistance()); aTableBorder.IsDistanceValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::DISTANCE); aRet <<= aTableBorder; } } break; case FN_UNO_TABLE_BORDER_DISTANCES : { table::TableBorderDistances aTableBorderDistances( 0, true, 0, true, 0, true, 0, true ) ; SwTable* pTable = SwTable::FindTable( pFormat ); const SwTableLines &rLines = pTable->GetTabLines(); bool bFirst = true; sal_uInt16 nLeftDistance = 0; sal_uInt16 nRightDistance = 0; sal_uInt16 nTopDistance = 0; sal_uInt16 nBottomDistance = 0; for(size_t i = 0; i < rLines.size(); ++i) { const SwTableLine* pLine = rLines[i]; const SwTableBoxes& rBoxes = pLine->GetTabBoxes(); for(size_t k = 0; k < rBoxes.size(); ++k) { const SwTableBox* pBox = rBoxes[k]; SwFrameFormat* pBoxFormat = pBox->GetFrameFormat(); const SvxBoxItem& rBox = pBoxFormat->GetBox(); if( bFirst ) { nLeftDistance = convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::LEFT )); nRightDistance = convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::RIGHT )); nTopDistance = convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::TOP )); nBottomDistance = convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::BOTTOM )); bFirst = false; } else { if( aTableBorderDistances.IsLeftDistanceValid && nLeftDistance != convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::LEFT ))) aTableBorderDistances.IsLeftDistanceValid = false; if( aTableBorderDistances.IsRightDistanceValid && nRightDistance != convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::RIGHT ))) aTableBorderDistances.IsRightDistanceValid = false; if( aTableBorderDistances.IsTopDistanceValid && nTopDistance != convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::TOP ))) aTableBorderDistances.IsTopDistanceValid = false; if( aTableBorderDistances.IsBottomDistanceValid && nBottomDistance != convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::BOTTOM ))) aTableBorderDistances.IsBottomDistanceValid = false; } } if( !aTableBorderDistances.IsLeftDistanceValid && !aTableBorderDistances.IsRightDistanceValid && !aTableBorderDistances.IsTopDistanceValid && !aTableBorderDistances.IsBottomDistanceValid ) break; } if( aTableBorderDistances.IsLeftDistanceValid) aTableBorderDistances.LeftDistance = nLeftDistance; if( aTableBorderDistances.IsRightDistanceValid) aTableBorderDistances.RightDistance = nRightDistance; if( aTableBorderDistances.IsTopDistanceValid) aTableBorderDistances.TopDistance = nTopDistance; if( aTableBorderDistances.IsBottomDistanceValid) aTableBorderDistances.BottomDistance = nBottomDistance; aRet <<= aTableBorderDistances; } break; case FN_UNO_TABLE_COLUMN_SEPARATORS: { SwTable* pTable = SwTable::FindTable( pFormat ); lcl_GetTableSeparators(aRet, pTable, pTable->GetTabLines()[0]->GetTabBoxes()[0], false); } break; case FN_UNO_TABLE_COLUMN_RELATIVE_SUM: aRet <<= sal_Int16(UNO_TABLE_COLUMN_SUM); break; case RES_ANCHOR: // AnchorType is readonly and might be void (no return value) break; case FN_UNO_TEXT_SECTION: { SwTable* pTable = SwTable::FindTable( pFormat ); SwTableNode* pTableNode = pTable->GetTableNode(); SwSectionNode* pSectionNode = pTableNode->FindSectionNode(); if(pSectionNode) { SwSection& rSect = pSectionNode->GetSection(); uno::Reference< text::XTextSection > xSect = SwXTextSections::GetObject( *rSect.GetFormat() ); aRet <<= xSect; } } break; case FN_UNO_TABLE_TEMPLATE_NAME: { SwTable* pTable = SwTable::FindTable(pFormat); OUString sName; SwStyleNameMapper::FillProgName(pTable->GetTableStyleName(), sName, SwGetPoolIdFromName::TabStyle); aRet <<= sName; } break; default: { const SwAttrSet& rSet = pFormat->GetAttrSet(); m_pImpl->m_pPropSet->getPropertyValue(*pEntry, rSet, aRet); } } } } else if (m_pImpl->IsDescriptor()) { const uno::Any* pAny = nullptr; if (!m_pImpl->m_pTableProps->GetProperty(pEntry->nWID, pEntry->nMemberId, pAny)) throw lang::IllegalArgumentException(); else if(pAny) aRet = *pAny; } else throw uno::RuntimeException(); return aRet; } void SwXTextTable::addPropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/) { throw uno::RuntimeException("Not implemented", static_cast(this)); } void SwXTextTable::removePropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/) { throw uno::RuntimeException("Not implemented", static_cast(this)); } void SwXTextTable::addVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/) { throw uno::RuntimeException("Not implemented", static_cast(this)); } void SwXTextTable::removeVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/) { throw uno::RuntimeException("Not implemented", static_cast(this)); } OUString SwXTextTable::getName() { SolarMutexGuard aGuard; SwFrameFormat* pFormat = GetFrameFormat(); if (!pFormat && !m_pImpl->IsDescriptor()) throw uno::RuntimeException(); if(pFormat) { return pFormat->GetName(); } return m_pImpl->m_sTableName; } void SwXTextTable::setName(const OUString& rName) { SolarMutexGuard aGuard; SwFrameFormat* pFormat = GetFrameFormat(); if ((!pFormat && !m_pImpl->IsDescriptor()) || rName.isEmpty() || rName.indexOf('.')>=0 || rName.indexOf(' ')>=0 ) throw uno::RuntimeException(); if(pFormat) { const OUString aOldName( pFormat->GetName() ); const SwFrameFormats* pFrameFormats = pFormat->GetDoc()->GetTableFrameFormats(); for (size_t i = pFrameFormats->size(); i;) { const SwFrameFormat* pTmpFormat = (*pFrameFormats)[--i]; if( !pTmpFormat->IsDefault() && pTmpFormat->GetName() == rName && pFormat->GetDoc()->IsUsed( *pTmpFormat )) { throw uno::RuntimeException(); } } pFormat->SetName( rName ); SwStartNode *pStNd; SwNodeIndex aIdx( *pFormat->GetDoc()->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 ); while ( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) ) { ++aIdx; SwNode *const pNd = & aIdx.GetNode(); if ( pNd->IsOLENode() && aOldName == static_cast(pNd)->GetChartTableName() ) { static_cast(pNd)->SetChartTableName( rName ); SwTable* pTable = SwTable::FindTable( pFormat ); //TL_CHART2: chart needs to be notified about name changes pFormat->GetDoc()->UpdateCharts( pTable->GetFrameFormat()->GetName() ); } aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 ); } pFormat->GetDoc()->getIDocumentState().SetModified(); } else m_pImpl->m_sTableName = rName; } sal_uInt16 SwXTextTable::Impl::GetRowCount() { sal_uInt16 nRet = 0; SwFrameFormat* pFormat = GetFrameFormat(); if(pFormat) { SwTable* pTable = SwTable::FindTable( pFormat ); if(!pTable->IsTableComplex()) { nRet = pTable->GetTabLines().size(); } } return nRet; } sal_uInt16 SwXTextTable::Impl::GetColumnCount() { SwFrameFormat* pFormat = GetFrameFormat(); sal_uInt16 nRet = 0; if(pFormat) { SwTable* pTable = SwTable::FindTable( pFormat ); if(!pTable->IsTableComplex()) { SwTableLines& rLines = pTable->GetTabLines(); SwTableLine* pLine = rLines.front(); nRet = pLine->GetTabBoxes().size(); } } return nRet; } void SwXTextTable::Impl::Notify(const SfxHint& rHint) { if(rHint.GetId() == SfxHintId::Dying) { m_pFrameFormat = nullptr; EndListeningAll(); } uno::Reference const xThis(m_wThis); if (xThis.is()) { // fdo#72695: if UNO object is already dead, don't revive it with event if(!m_pFrameFormat) { lang::EventObject const ev(xThis); m_Listeners.disposeAndClear(ev); } else { lcl_SendChartEvent(xThis.get(), m_Listeners); } } } OUString SAL_CALL SwXTextTable::getImplementationName() { return "SwXTextTable"; } sal_Bool SwXTextTable::supportsService(const OUString& rServiceName) { return cppu::supportsService(this, rServiceName); } uno::Sequence SwXTextTable::getSupportedServiceNames() { return { "com.sun.star.document.LinkTarget", "com.sun.star.text.TextTable", "com.sun.star.text.TextContent", "com.sun.star.text.TextSortable" }; } class SwXCellRange::Impl : public SvtListener { private: ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2 SwFrameFormat* m_pFrameFormat; public: uno::WeakReference m_wThis; ::comphelper::OInterfaceContainerHelper2 m_ChartListeners; sw::UnoCursorPointer m_pTableCursor; SwRangeDescriptor m_RangeDescriptor; const SfxItemPropertySet* m_pPropSet; bool m_bFirstRowAsLabel; bool m_bFirstColumnAsLabel; Impl(sw::UnoCursorPointer const& pCursor, SwFrameFormat& rFrameFormat, SwRangeDescriptor const& rDesc) : m_pFrameFormat(&rFrameFormat) , m_ChartListeners(m_Mutex) , m_pTableCursor(pCursor) , m_RangeDescriptor(rDesc) , m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TABLE_RANGE)) , m_bFirstRowAsLabel(false) , m_bFirstColumnAsLabel(false) { StartListening(rFrameFormat.GetNotifier()); m_RangeDescriptor.Normalize(); } SwFrameFormat* GetFrameFormat() { return m_pFrameFormat; } std::tuple GetLabelCoordinates(bool bRow); uno::Sequence GetLabelDescriptions(SwXCellRange & rThis, bool bRow); void SetLabelDescriptions(SwXCellRange & rThis, const css::uno::Sequence& rDesc, bool bRow); sal_Int32 GetRowCount() const; sal_Int32 GetColumnCount() const; virtual void Notify(const SfxHint& ) override; }; namespace { class theSwXCellRangeUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXCellRangeUnoTunnelId > {}; } const uno::Sequence< sal_Int8 > & SwXCellRange::getUnoTunnelId() { return theSwXCellRangeUnoTunnelId::get().getSeq(); } sal_Int64 SAL_CALL SwXCellRange::getSomething( const uno::Sequence< sal_Int8 >& rId ) { if( isUnoTunnelId(rId) ) { return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) ); } return 0; } OUString SwXCellRange::getImplementationName() { return "SwXCellRange"; } sal_Bool SwXCellRange::supportsService(const OUString& rServiceName) { return cppu::supportsService(this, rServiceName); } uno::Sequence SwXCellRange::getSupportedServiceNames() { return { "com.sun.star.text.CellRange", "com.sun.star.style.CharacterProperties", "com.sun.star.style.CharacterPropertiesAsian", "com.sun.star.style.CharacterPropertiesComplex", "com.sun.star.style.ParagraphProperties", "com.sun.star.style.ParagraphPropertiesAsian", "com.sun.star.style.ParagraphPropertiesComplex" }; } SwXCellRange::SwXCellRange(sw::UnoCursorPointer const& pCursor, SwFrameFormat& rFrameFormat, SwRangeDescriptor const & rDesc) : m_pImpl(new Impl(pCursor, rFrameFormat, rDesc)) { } SwXCellRange::~SwXCellRange() { } rtl::Reference SwXCellRange::CreateXCellRange( sw::UnoCursorPointer const& pCursor, SwFrameFormat& rFrameFormat, SwRangeDescriptor const & rDesc) { SwXCellRange *const pCellRange(new SwXCellRange(pCursor, rFrameFormat, rDesc)); uno::Reference xCellRange(pCellRange); // need a permanent Reference to initialize m_wThis pCellRange->m_pImpl->m_wThis = xCellRange; return pCellRange; } void SwXCellRange::SetLabels(bool bFirstRowAsLabel, bool bFirstColumnAsLabel) { m_pImpl->m_bFirstRowAsLabel = bFirstRowAsLabel; m_pImpl->m_bFirstColumnAsLabel = bFirstColumnAsLabel; } std::vector< uno::Reference< table::XCell > > SwXCellRange::GetCells() { SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat(); const sal_Int32 nRowCount(m_pImpl->GetRowCount()); const sal_Int32 nColCount(m_pImpl->GetColumnCount()); std::vector< uno::Reference< table::XCell > > vResult; vResult.reserve(static_cast(nRowCount)*static_cast(nColCount)); for(sal_Int32 nRow = 0; nRow < nRowCount; ++nRow) for(sal_Int32 nCol = 0; nCol < nColCount; ++nCol) vResult.emplace_back(lcl_CreateXCell(pFormat, m_pImpl->m_RangeDescriptor.nLeft + nCol, m_pImpl->m_RangeDescriptor.nTop + nRow)); return vResult; } uno::Reference SAL_CALL SwXCellRange::getCellByPosition(sal_Int32 nColumn, sal_Int32 nRow) { SolarMutexGuard aGuard; uno::Reference< table::XCell > aRet; SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat(); if(pFormat) { if(nColumn >= 0 && nRow >= 0 && m_pImpl->GetColumnCount() > nColumn && m_pImpl->GetRowCount() > nRow ) { SwXCell* pXCell = lcl_CreateXCell(pFormat, m_pImpl->m_RangeDescriptor.nLeft + nColumn, m_pImpl->m_RangeDescriptor.nTop + nRow); if(pXCell) aRet = pXCell; } } if(!aRet.is()) throw lang::IndexOutOfBoundsException(); return aRet; } uno::Reference SAL_CALL SwXCellRange::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom) { SolarMutexGuard aGuard; uno::Reference< table::XCellRange > aRet; SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat(); if (pFormat && m_pImpl->GetColumnCount() > nRight && m_pImpl->GetRowCount() > nBottom && nLeft <= nRight && nTop <= nBottom && nLeft >= 0 && nRight >= 0 && nTop >= 0 && nBottom >= 0 ) { SwTable* pTable = SwTable::FindTable( pFormat ); if(!pTable->IsTableComplex()) { SwRangeDescriptor aNewDesc; aNewDesc.nTop = nTop + m_pImpl->m_RangeDescriptor.nTop; aNewDesc.nBottom = nBottom + m_pImpl->m_RangeDescriptor.nTop; aNewDesc.nLeft = nLeft + m_pImpl->m_RangeDescriptor.nLeft; aNewDesc.nRight = nRight + m_pImpl->m_RangeDescriptor.nLeft; aNewDesc.Normalize(); const OUString sTLName = sw_GetCellName(aNewDesc.nLeft, aNewDesc.nTop); const OUString sBRName = sw_GetCellName(aNewDesc.nRight, aNewDesc.nBottom); const SwTableBox* pTLBox = pTable->GetTableBox( sTLName ); if(pTLBox) { const SwStartNode* pSttNd = pTLBox->GetSttNd(); SwPosition aPos(*pSttNd); // set cursor in the upper-left cell of the range auto pUnoCursor(pFormat->GetDoc()->CreateUnoCursor(aPos, true)); pUnoCursor->Move( fnMoveForward, GoInNode ); pUnoCursor->SetRemainInSection( false ); const SwTableBox* pBRBox = pTable->GetTableBox( sBRName ); if(pBRBox) { pUnoCursor->SetMark(); pUnoCursor->GetPoint()->nNode = *pBRBox->GetSttNd(); pUnoCursor->Move( fnMoveForward, GoInNode ); SwUnoTableCursor& rCursor = dynamic_cast(*pUnoCursor); // HACK: remove pending actions for selecting old style tables UnoActionRemoveContext aRemoveContext(rCursor); rCursor.MakeBoxSels(); // pUnoCursor will be provided and will not be deleted aRet = SwXCellRange::CreateXCellRange(pUnoCursor, *pFormat, aNewDesc).get(); } } } } if(!aRet.is()) throw lang::IndexOutOfBoundsException(); return aRet; } uno::Reference SAL_CALL SwXCellRange::getCellRangeByName(const OUString& rRange) { SolarMutexGuard aGuard; sal_Int32 nPos = 0; const OUString sTLName(rRange.getToken(0, ':', nPos)); const OUString sBRName(rRange.getToken(0, ':', nPos)); if(sTLName.isEmpty() || sBRName.isEmpty()) throw uno::RuntimeException(); SwRangeDescriptor aDesc; aDesc.nTop = aDesc.nLeft = aDesc.nBottom = aDesc.nRight = -1; SwXTextTable::GetCellPosition( sTLName, aDesc.nLeft, aDesc.nTop ); SwXTextTable::GetCellPosition( sBRName, aDesc.nRight, aDesc.nBottom ); aDesc.Normalize(); return getCellRangeByPosition( aDesc.nLeft - m_pImpl->m_RangeDescriptor.nLeft, aDesc.nTop - m_pImpl->m_RangeDescriptor.nTop, aDesc.nRight - m_pImpl->m_RangeDescriptor.nLeft, aDesc.nBottom - m_pImpl->m_RangeDescriptor.nTop); } uno::Reference< beans::XPropertySetInfo > SwXCellRange::getPropertySetInfo() { static uno::Reference xRef = m_pImpl->m_pPropSet->getPropertySetInfo(); return xRef; } void SAL_CALL SwXCellRange::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue) { SolarMutexGuard aGuard; SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat(); if(!pFormat) return; const SfxItemPropertySimpleEntry *const pEntry = m_pImpl->m_pPropSet->getPropertyMap().getByName(rPropertyName); if(!pEntry) throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) ); if ( pEntry->nFlags & beans::PropertyAttribute::READONLY) throw beans::PropertyVetoException("Property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) ); SwDoc *const pDoc = m_pImpl->m_pTableCursor->GetDoc(); SwUnoTableCursor& rCursor(dynamic_cast(*m_pImpl->m_pTableCursor)); { // HACK: remove pending actions for selecting old style tables UnoActionRemoveContext aRemoveContext(rCursor); } rCursor.MakeBoxSels(); switch(pEntry->nWID ) { case FN_UNO_TABLE_CELL_BACKGROUND: { std::unique_ptr aBrush(std::make_unique(RES_BACKGROUND)); SwDoc::GetBoxAttr(*m_pImpl->m_pTableCursor, aBrush); aBrush->PutValue(aValue, pEntry->nMemberId); pDoc->SetBoxAttr(*m_pImpl->m_pTableCursor, *aBrush); } break; case RES_BOX : { SfxItemSet aSet(pDoc->GetAttrPool(), svl::Items{}); SvxBoxInfoItem aBoxInfo( SID_ATTR_BORDER_INNER ); aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::ALL, false); SvxBoxInfoItemValidFlags nValid = SvxBoxInfoItemValidFlags::NONE; switch(pEntry->nMemberId & ~CONVERT_TWIPS) { case LEFT_BORDER : nValid = SvxBoxInfoItemValidFlags::LEFT; break; case RIGHT_BORDER: nValid = SvxBoxInfoItemValidFlags::RIGHT; break; case TOP_BORDER : nValid = SvxBoxInfoItemValidFlags::TOP; break; case BOTTOM_BORDER: nValid = SvxBoxInfoItemValidFlags::BOTTOM; break; case LEFT_BORDER_DISTANCE : case RIGHT_BORDER_DISTANCE: case TOP_BORDER_DISTANCE : case BOTTOM_BORDER_DISTANCE: nValid = SvxBoxInfoItemValidFlags::DISTANCE; break; } aBoxInfo.SetValid(nValid); aSet.Put(aBoxInfo); SwDoc::GetTabBorders(rCursor, aSet); aSet.Put(aBoxInfo); SvxBoxItem aBoxItem(aSet.Get(RES_BOX)); static_cast(aBoxItem).PutValue(aValue, pEntry->nMemberId); aSet.Put(aBoxItem); pDoc->SetTabBorders(*m_pImpl->m_pTableCursor, aSet); } break; case RES_BOXATR_FORMAT: { SfxUInt32Item aNumberFormat(RES_BOXATR_FORMAT); static_cast(aNumberFormat).PutValue(aValue, 0); pDoc->SetBoxAttr(rCursor, aNumberFormat); } break; case FN_UNO_RANGE_ROW_LABEL: { bool bTmp = *o3tl::doAccess(aValue); if (m_pImpl->m_bFirstRowAsLabel != bTmp) { lcl_SendChartEvent(*this, m_pImpl->m_ChartListeners); m_pImpl->m_bFirstRowAsLabel = bTmp; } } break; case FN_UNO_RANGE_COL_LABEL: { bool bTmp = *o3tl::doAccess(aValue); if (m_pImpl->m_bFirstColumnAsLabel != bTmp) { lcl_SendChartEvent(*this, m_pImpl->m_ChartListeners); m_pImpl->m_bFirstColumnAsLabel = bTmp; } } break; case RES_VERT_ORIENT: { sal_Int16 nAlign = -1; aValue >>= nAlign; if( nAlign >= text::VertOrientation::NONE && nAlign <= text::VertOrientation::BOTTOM) pDoc->SetBoxAlign( rCursor, nAlign ); } break; default: { SfxItemSet aItemSet( pDoc->GetAttrPool(), {{pEntry->nWID, pEntry->nWID}} ); SwUnoCursorHelper::GetCursorAttr(rCursor.GetSelRing(), aItemSet); if (!SwUnoCursorHelper::SetCursorPropertyValue( *pEntry, aValue, rCursor.GetSelRing(), aItemSet)) { m_pImpl->m_pPropSet->setPropertyValue(*pEntry, aValue, aItemSet); } SwUnoCursorHelper::SetCursorAttr(rCursor.GetSelRing(), aItemSet, SetAttrMode::DEFAULT, true); } } } uno::Any SAL_CALL SwXCellRange::getPropertyValue(const OUString& rPropertyName) { SolarMutexGuard aGuard; uno::Any aRet; SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat(); if(pFormat) { const SfxItemPropertySimpleEntry *const pEntry = m_pImpl->m_pPropSet->getPropertyMap().getByName(rPropertyName); if(!pEntry) throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) ); switch(pEntry->nWID ) { case FN_UNO_TABLE_CELL_BACKGROUND: { std::unique_ptr aBrush(std::make_unique(RES_BACKGROUND)); if (SwDoc::GetBoxAttr(*m_pImpl->m_pTableCursor, aBrush)) aBrush->QueryValue(aRet, pEntry->nMemberId); } break; case RES_BOX : { SwDoc *const pDoc = m_pImpl->m_pTableCursor->GetDoc(); SfxItemSet aSet(pDoc->GetAttrPool(), svl::Items{}); aSet.Put(SvxBoxInfoItem( SID_ATTR_BORDER_INNER )); SwDoc::GetTabBorders(*m_pImpl->m_pTableCursor, aSet); const SvxBoxItem& rBoxItem = aSet.Get(RES_BOX); rBoxItem.QueryValue(aRet, pEntry->nMemberId); } break; case RES_BOXATR_FORMAT: OSL_FAIL("not implemented"); break; case FN_UNO_PARA_STYLE: { SwFormatColl *const pTmpFormat = SwUnoCursorHelper::GetCurTextFormatColl(*m_pImpl->m_pTableCursor, false); OUString sRet; if (pTmpFormat) sRet = pTmpFormat->GetName(); aRet <<= sRet; } break; case FN_UNO_RANGE_ROW_LABEL: aRet <<= m_pImpl->m_bFirstRowAsLabel; break; case FN_UNO_RANGE_COL_LABEL: aRet <<= m_pImpl->m_bFirstColumnAsLabel; break; case RES_VERT_ORIENT: { std::unique_ptr aVertOrient( std::make_unique(RES_VERT_ORIENT)); if (SwDoc::GetBoxAttr(*m_pImpl->m_pTableCursor, aVertOrient)) { aVertOrient->QueryValue( aRet, pEntry->nMemberId ); } } break; default: { SfxItemSet aSet( m_pImpl->m_pTableCursor->GetDoc()->GetAttrPool(), svl::Items< RES_CHRATR_BEGIN, RES_FRMATR_END - 1, RES_UNKNOWNATR_CONTAINER, RES_UNKNOWNATR_CONTAINER>{}); // first look at the attributes of the cursor SwUnoTableCursor *const pCursor = dynamic_cast(&(*m_pImpl->m_pTableCursor)); SwUnoCursorHelper::GetCursorAttr(pCursor->GetSelRing(), aSet); m_pImpl->m_pPropSet->getPropertyValue(*pEntry, aSet, aRet); } } } return aRet; } void SwXCellRange::addPropertyChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/) { throw uno::RuntimeException("Not implemented", static_cast(this)); } void SwXCellRange::removePropertyChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/) { throw uno::RuntimeException("Not implemented", static_cast(this)); } void SwXCellRange::addVetoableChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/) { throw uno::RuntimeException("Not implemented", static_cast(this)); } void SwXCellRange::removeVetoableChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/) { throw uno::RuntimeException("Not implemented", static_cast(this)); } ///@see SwXCellRange::getData uno::Sequence> SAL_CALL SwXCellRange::getDataArray() { SolarMutexGuard aGuard; const sal_Int32 nRowCount = m_pImpl->GetRowCount(); const sal_Int32 nColCount = m_pImpl->GetColumnCount(); if(!nRowCount || !nColCount) throw uno::RuntimeException("Table too complex", static_cast(this)); lcl_EnsureCoreConnected(m_pImpl->GetFrameFormat(), static_cast(this)); uno::Sequence< uno::Sequence< uno::Any > > aRowSeq(nRowCount); auto vCells(GetCells()); auto pCurrentCell(vCells.begin()); for(auto& rRow : aRowSeq) { rRow = uno::Sequence< uno::Any >(nColCount); for(auto& rCellAny : rRow) { auto pCell(static_cast(pCurrentCell->get())); if(!pCell) throw uno::RuntimeException("Table too complex", static_cast(this)); rCellAny = pCell->GetAny(); ++pCurrentCell; } } return aRowSeq; } ///@see SwXCellRange::setData void SAL_CALL SwXCellRange::setDataArray(const uno::Sequence< uno::Sequence< uno::Any > >& rArray) { SolarMutexGuard aGuard; const sal_Int32 nRowCount = m_pImpl->GetRowCount(); const sal_Int32 nColCount = m_pImpl->GetColumnCount(); if(!nRowCount || !nColCount) throw uno::RuntimeException("Table too complex", static_cast(this)); SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat(); if(!pFormat) return; if(rArray.getLength() != nRowCount) throw uno::RuntimeException("Row count mismatch. expected: " + OUString::number(nRowCount) + " got: " + OUString::number(rArray.getLength()), static_cast(this)); auto vCells(GetCells()); auto pCurrentCell(vCells.begin()); for(const auto& rColSeq : rArray) { if(rColSeq.getLength() != nColCount) throw uno::RuntimeException("Column count mismatch. expected: " + OUString::number(nColCount) + " got: " + OUString::number(rColSeq.getLength()), static_cast(this)); for(const auto& aValue : rColSeq) { auto pCell(static_cast(pCurrentCell->get())); if(!pCell || !pCell->GetTableBox()) throw uno::RuntimeException("Box for cell missing", static_cast(this)); if(aValue.isExtractableTo(cppu::UnoType::get())) sw_setString(*pCell, aValue.get()); else if(aValue.isExtractableTo(cppu::UnoType::get())) sw_setValue(*pCell, aValue.get()); else sw_setString(*pCell, OUString(), true); ++pCurrentCell; } } } uno::Sequence> SAL_CALL SwXCellRange::getData() { SolarMutexGuard aGuard; const sal_Int32 nRowCount = m_pImpl->GetRowCount(); const sal_Int32 nColCount = m_pImpl->GetColumnCount(); if(!nRowCount || !nColCount) throw uno::RuntimeException("Table too complex", static_cast(this)); if (m_pImpl->m_bFirstColumnAsLabel || m_pImpl->m_bFirstRowAsLabel) { uno::Reference const xDataRange( getCellRangeByPosition((m_pImpl->m_bFirstColumnAsLabel) ? 1 : 0, (m_pImpl->m_bFirstRowAsLabel) ? 1 : 0, nColCount-1, nRowCount-1), uno::UNO_QUERY_THROW); return xDataRange->getData(); } uno::Sequence< uno::Sequence< double > > vRows(nRowCount); auto vCells(GetCells()); auto pCurrentCell(vCells.begin()); for(auto& rRow : vRows) { rRow = uno::Sequence(nColCount); for(auto& rValue : rRow) { if(!(*pCurrentCell)) throw uno::RuntimeException("Table too complex", static_cast(this)); rValue = (*pCurrentCell)->getValue(); ++pCurrentCell; } } return vRows; } void SAL_CALL SwXCellRange::setData(const uno::Sequence< uno::Sequence >& rData) { SolarMutexGuard aGuard; const sal_Int32 nRowCount = m_pImpl->GetRowCount(); const sal_Int32 nColCount = m_pImpl->GetColumnCount(); if(!nRowCount || !nColCount) throw uno::RuntimeException("Table too complex", static_cast(this)); if (m_pImpl->m_bFirstColumnAsLabel || m_pImpl->m_bFirstRowAsLabel) { uno::Reference const xDataRange( getCellRangeByPosition((m_pImpl->m_bFirstColumnAsLabel) ? 1 : 0, (m_pImpl->m_bFirstRowAsLabel) ? 1 : 0, nColCount-1, nRowCount-1), uno::UNO_QUERY_THROW); return xDataRange->setData(rData); } lcl_EnsureCoreConnected(m_pImpl->GetFrameFormat(), static_cast(this)); if(rData.getLength() != nRowCount) throw uno::RuntimeException("Row count mismatch. expected: " + OUString::number(nRowCount) + " got: " + OUString::number(rData.getLength()), static_cast(this)); auto vCells(GetCells()); auto pCurrentCell(vCells.begin()); for(const auto& rRow : rData) { if(rRow.getLength() != nColCount) throw uno::RuntimeException("Column count mismatch. expected: " + OUString::number(nColCount) + " got: " + OUString::number(rRow.getLength()), static_cast(this)); for(const auto& rValue : rRow) { uno::Reference(*pCurrentCell, uno::UNO_SET_THROW)->setValue(rValue); ++pCurrentCell; } } } std::tuple SwXCellRange::Impl::GetLabelCoordinates(bool bRow) { sal_uInt32 nLeft, nTop, nRight, nBottom; nLeft = nTop = nRight = nBottom = 0; if(bRow) { nTop = m_bFirstRowAsLabel ? 1 : 0; nBottom = GetRowCount() - 1; } else { nLeft = m_bFirstColumnAsLabel ? 1 : 0; nRight = GetColumnCount() - 1; } return std::make_tuple(nLeft, nTop, nRight, nBottom); } uno::Sequence SwXCellRange::Impl::GetLabelDescriptions(SwXCellRange & rThis, bool bRow) { SolarMutexGuard aGuard; sal_uInt32 nLeft, nTop, nRight, nBottom; std::tie(nLeft, nTop, nRight, nBottom) = GetLabelCoordinates(bRow); if(!nRight && !nBottom) throw uno::RuntimeException("Table too complex", static_cast(&rThis)); lcl_EnsureCoreConnected(GetFrameFormat(), static_cast(&rThis)); if (!(bRow ? m_bFirstColumnAsLabel : m_bFirstRowAsLabel)) return {}; // without labels we have no descriptions auto xLabelRange(rThis.getCellRangeByPosition(nLeft, nTop, nRight, nBottom)); auto vCells(static_cast(xLabelRange.get())->GetCells()); uno::Sequence vResult(vCells.size()); std::transform(vCells.begin(), vCells.end(), vResult.begin(), [](uno::Reference xCell) -> OUString { return uno::Reference(xCell, uno::UNO_QUERY_THROW)->getString(); }); return vResult; } uno::Sequence SAL_CALL SwXCellRange::getRowDescriptions() { return m_pImpl->GetLabelDescriptions(*this, true); } uno::Sequence SAL_CALL SwXCellRange::getColumnDescriptions() { return m_pImpl->GetLabelDescriptions(*this, false); } void SwXCellRange::Impl::SetLabelDescriptions(SwXCellRange & rThis, const uno::Sequence& rDesc, bool bRow) { SolarMutexGuard aGuard; lcl_EnsureCoreConnected(GetFrameFormat(), static_cast(&rThis)); if (!(bRow ? m_bFirstColumnAsLabel : m_bFirstRowAsLabel)) return; // if there are no labels we cannot set descriptions sal_uInt32 nLeft, nTop, nRight, nBottom; std::tie(nLeft, nTop, nRight, nBottom) = GetLabelCoordinates(bRow); if(!nRight && !nBottom) throw uno::RuntimeException("Table too complex", static_cast(&rThis)); auto xLabelRange(rThis.getCellRangeByPosition(nLeft, nTop, nRight, nBottom)); if (!xLabelRange.is()) throw uno::RuntimeException("Missing Cell Range", static_cast(&rThis)); auto vCells(static_cast(xLabelRange.get())->GetCells()); if (sal::static_int_cast(rDesc.getLength()) != vCells.size()) throw uno::RuntimeException("Too few or too many descriptions", static_cast(&rThis)); auto pDescIterator(rDesc.begin()); for(auto& xCell : vCells) uno::Reference(xCell, uno::UNO_QUERY_THROW)->setString(*pDescIterator++); } void SAL_CALL SwXCellRange::setRowDescriptions( const uno::Sequence& rRowDesc) { m_pImpl->SetLabelDescriptions(*this, rRowDesc, true); } void SAL_CALL SwXCellRange::setColumnDescriptions( const uno::Sequence& rColumnDesc) { m_pImpl->SetLabelDescriptions(*this, rColumnDesc, false); } void SAL_CALL SwXCellRange::addChartDataChangeEventListener( const uno::Reference & xListener) { // no need to lock here as m_pImpl is const and container threadsafe m_pImpl->m_ChartListeners.addInterface(xListener); } void SAL_CALL SwXCellRange::removeChartDataChangeEventListener( const uno::Reference & xListener) { // no need to lock here as m_pImpl is const and container threadsafe m_pImpl->m_ChartListeners.removeInterface(xListener); } sal_Bool SwXCellRange::isNotANumber(double /*fNumber*/) { throw uno::RuntimeException("Not implemented", static_cast(this)); } double SwXCellRange::getNotANumber() { throw uno::RuntimeException("Not implemented", static_cast(this)); } uno::Sequence< beans::PropertyValue > SwXCellRange::createSortDescriptor() { SolarMutexGuard aGuard; return SwUnoCursorHelper::CreateSortDescriptor(true); } void SAL_CALL SwXCellRange::sort(const uno::Sequence< beans::PropertyValue >& rDescriptor) { SolarMutexGuard aGuard; SwSortOptions aSortOpt; SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat(); if(pFormat && SwUnoCursorHelper::ConvertSortProperties(rDescriptor, aSortOpt)) { SwUnoTableCursor& rTableCursor = dynamic_cast(*m_pImpl->m_pTableCursor); rTableCursor.MakeBoxSels(); UnoActionContext aContext(pFormat->GetDoc()); pFormat->GetDoc()->SortTable(rTableCursor.GetSelectedBoxes(), aSortOpt); } } sal_Int32 SwXCellRange::Impl::GetColumnCount() const { return m_RangeDescriptor.nRight - m_RangeDescriptor.nLeft + 1; } sal_Int32 SwXCellRange::Impl::GetRowCount() const { return m_RangeDescriptor.nBottom - m_RangeDescriptor.nTop + 1; } const SwUnoCursor* SwXCellRange::GetTableCursor() const { SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat(); return pFormat ? &(*m_pImpl->m_pTableCursor) : nullptr; } void SwXCellRange::Impl::Notify( const SfxHint& rHint ) { uno::Reference const xThis(m_wThis); if(rHint.GetId() == SfxHintId::Dying) { m_pFrameFormat = nullptr; m_pTableCursor.reset(nullptr); } if (xThis.is()) { // fdo#72695: if UNO object is already dead, don't revive it with event if(m_pFrameFormat) lcl_SendChartEvent(xThis.get(), m_ChartListeners); else m_ChartListeners.disposeAndClear(lang::EventObject(xThis)); } } class SwXTableRows::Impl : public SvtListener { private: SwFrameFormat* m_pFrameFormat; public: explicit Impl(SwFrameFormat& rFrameFormat) : m_pFrameFormat(&rFrameFormat) { StartListening(rFrameFormat.GetNotifier()); } SwFrameFormat* GetFrameFormat() { return m_pFrameFormat; } virtual void Notify(const SfxHint&) override; }; // SwXTableRows OUString SwXTableRows::getImplementationName() { return "SwXTableRows"; } sal_Bool SwXTableRows::supportsService(const OUString& rServiceName) { return cppu::supportsService(this, rServiceName); } uno::Sequence< OUString > SwXTableRows::getSupportedServiceNames() { return { "com.sun.star.text.TableRows" }; } SwXTableRows::SwXTableRows(SwFrameFormat& rFrameFormat) : m_pImpl(new SwXTableRows::Impl(rFrameFormat)) { } SwXTableRows::~SwXTableRows() { } SwFrameFormat* SwXTableRows::GetFrameFormat() { return m_pImpl->GetFrameFormat(); } sal_Int32 SwXTableRows::getCount() { SolarMutexGuard aGuard; SwFrameFormat* pFrameFormat = GetFrameFormat(); if(!pFrameFormat) throw uno::RuntimeException(); SwTable* pTable = SwTable::FindTable(pFrameFormat); return pTable->GetTabLines().size(); } ///@see SwXCell::CreateXCell (TODO: seems to be copy and paste programming here) uno::Any SwXTableRows::getByIndex(sal_Int32 nIndex) { SolarMutexGuard aGuard; SwFrameFormat* pFrameFormat(lcl_EnsureCoreConnected(GetFrameFormat(), static_cast(this))); if(nIndex < 0) throw lang::IndexOutOfBoundsException(); SwTable* pTable = SwTable::FindTable( pFrameFormat ); if(o3tl::make_unsigned(nIndex) >= pTable->GetTabLines().size()) throw lang::IndexOutOfBoundsException(); SwTableLine* pLine = pTable->GetTabLines()[nIndex]; FindUnoInstanceHint aHint{pLine}; pFrameFormat->GetNotifier().Broadcast(aHint); if(!aHint.m_pResult) aHint.m_pResult = new SwXTextTableRow(pFrameFormat, pLine); uno::Reference xRet = static_cast(aHint.m_pResult); return uno::makeAny(xRet); } uno::Type SAL_CALL SwXTableRows::getElementType() { return cppu::UnoType::get(); } sal_Bool SwXTableRows::hasElements() { SolarMutexGuard aGuard; SwFrameFormat* pFrameFormat = GetFrameFormat(); if(!pFrameFormat) throw uno::RuntimeException(); // a table always has rows return true; } void SwXTableRows::insertByIndex(sal_Int32 nIndex, sal_Int32 nCount) { SolarMutexGuard aGuard; if (nCount == 0) return; SwFrameFormat* pFrameFormat(lcl_EnsureCoreConnected(GetFrameFormat(), static_cast(this))); SwTable* pTable = lcl_EnsureTableNotComplex(SwTable::FindTable(pFrameFormat), static_cast(this)); const size_t nRowCount = pTable->GetTabLines().size(); if (nCount <= 0 || 0 > nIndex || o3tl::make_unsigned(nIndex) > nRowCount) throw uno::RuntimeException("Illegal arguments", static_cast(this)); const OUString sTLName = sw_GetCellName(0, nIndex); const SwTableBox* pTLBox = pTable->GetTableBox(sTLName); bool bAppend = false; if(!pTLBox) { bAppend = true; // to append at the end the cursor must be in the last line SwTableLines& rLines = pTable->GetTabLines(); SwTableLine* pLine = rLines.back(); SwTableBoxes& rBoxes = pLine->GetTabBoxes(); pTLBox = rBoxes.front(); } if(!pTLBox) throw uno::RuntimeException("Illegal arguments", static_cast(this)); const SwStartNode* pSttNd = pTLBox->GetSttNd(); SwPosition aPos(*pSttNd); // set cursor to the upper-left cell of the range UnoActionContext aAction(pFrameFormat->GetDoc()); std::shared_ptr const pUnoCursor( std::dynamic_pointer_cast( pFrameFormat->GetDoc()->CreateUnoCursor(aPos, true))); pUnoCursor->Move( fnMoveForward, GoInNode ); { // remove actions - TODO: why? UnoActionRemoveContext aRemoveContext(pUnoCursor->GetDoc()); } pFrameFormat->GetDoc()->InsertRow(*pUnoCursor, static_cast(nCount), bAppend); } void SwXTableRows::removeByIndex(sal_Int32 nIndex, sal_Int32 nCount) { SolarMutexGuard aGuard; if (nCount == 0) return; SwFrameFormat* pFrameFormat(lcl_EnsureCoreConnected(GetFrameFormat(), static_cast(this))); if(nIndex < 0 || nCount <=0 ) throw uno::RuntimeException(); SwTable* pTable = lcl_EnsureTableNotComplex(SwTable::FindTable(pFrameFormat), static_cast(this)); OUString sTLName = sw_GetCellName(0, nIndex); const SwTableBox* pTLBox = pTable->GetTableBox(sTLName); if(!pTLBox) throw uno::RuntimeException("Illegal arguments", static_cast(this)); const SwStartNode* pSttNd = pTLBox->GetSttNd(); SwPosition aPos(*pSttNd); // set cursor to the upper-left cell of the range auto pUnoCursor(pFrameFormat->GetDoc()->CreateUnoCursor(aPos, true)); pUnoCursor->Move(fnMoveForward, GoInNode); pUnoCursor->SetRemainInSection( false ); const OUString sBLName = sw_GetCellName(0, nIndex + nCount - 1); const SwTableBox* pBLBox = pTable->GetTableBox( sBLName ); if(!pBLBox) throw uno::RuntimeException("Illegal arguments", static_cast(this)); pUnoCursor->SetMark(); pUnoCursor->GetPoint()->nNode = *pBLBox->GetSttNd(); pUnoCursor->Move(fnMoveForward, GoInNode); SwUnoTableCursor& rCursor = dynamic_cast(*pUnoCursor); { // HACK: remove pending actions for selecting old style tables UnoActionRemoveContext aRemoveContext(rCursor); } rCursor.MakeBoxSels(); { // these braces are important UnoActionContext aAction(pFrameFormat->GetDoc()); pFrameFormat->GetDoc()->DeleteRow(*pUnoCursor); pUnoCursor.reset(); } { // invalidate all actions - TODO: why? UnoActionRemoveContext aRemoveContext(pFrameFormat->GetDoc()); } } void SwXTableRows::Impl::Notify( const SfxHint& rHint) { if(rHint.GetId() == SfxHintId::Dying) m_pFrameFormat = nullptr; } // SwXTableColumns class SwXTableColumns::Impl : public SvtListener { SwFrameFormat* m_pFrameFormat; public: explicit Impl(SwFrameFormat& rFrameFormat) : m_pFrameFormat(&rFrameFormat) { StartListening(rFrameFormat.GetNotifier()); } SwFrameFormat* GetFrameFormat() { return m_pFrameFormat; } virtual void Notify(const SfxHint&) override; }; OUString SwXTableColumns::getImplementationName() { return "SwXTableColumns"; } sal_Bool SwXTableColumns::supportsService(const OUString& rServiceName) { return cppu::supportsService(this, rServiceName); } uno::Sequence< OUString > SwXTableColumns::getSupportedServiceNames() { return { "com.sun.star.text.TableColumns"}; } SwXTableColumns::SwXTableColumns(SwFrameFormat& rFrameFormat) : m_pImpl(new SwXTableColumns::Impl(rFrameFormat)) { } SwXTableColumns::~SwXTableColumns() { } SwFrameFormat* SwXTableColumns::GetFrameFormat() const { return m_pImpl->GetFrameFormat(); } sal_Int32 SwXTableColumns::getCount() { SolarMutexGuard aGuard; SwFrameFormat* pFrameFormat(lcl_EnsureCoreConnected(GetFrameFormat(), static_cast(this))); SwTable* pTable = SwTable::FindTable( pFrameFormat ); // if(!pTable->IsTableComplex()) // throw uno::RuntimeException("Table too complex", static_cast(this)); SwTableLines& rLines = pTable->GetTabLines(); SwTableLine* pLine = rLines.front(); return pLine->GetTabBoxes().size(); } uno::Any SwXTableColumns::getByIndex(sal_Int32 nIndex) { SolarMutexGuard aGuard; if(nIndex < 0 || getCount() <= nIndex) throw lang::IndexOutOfBoundsException(); return uno::makeAny(uno::Reference()); // i#21699 not supported } uno::Type SAL_CALL SwXTableColumns::getElementType() { return cppu::UnoType::get(); } sal_Bool SwXTableColumns::hasElements() { SolarMutexGuard aGuard; lcl_EnsureCoreConnected(GetFrameFormat(), static_cast(this)); return true; } ///@see SwXTableRows::insertByIndex (TODO: seems to be copy and paste programming here) void SwXTableColumns::insertByIndex(sal_Int32 nIndex, sal_Int32 nCount) { SolarMutexGuard aGuard; if (nCount == 0) return; SwFrameFormat* pFrameFormat(lcl_EnsureCoreConnected(GetFrameFormat(), static_cast(this))); SwTable* pTable = lcl_EnsureTableNotComplex(SwTable::FindTable(pFrameFormat), static_cast(this)); SwTableLines& rLines = pTable->GetTabLines(); SwTableLine* pLine = rLines.front(); const size_t nColCount = pLine->GetTabBoxes().size(); if (nCount <= 0 || 0 > nIndex || o3tl::make_unsigned(nIndex) > nColCount) throw uno::RuntimeException("Illegal arguments", static_cast(this)); const OUString sTLName = sw_GetCellName(nIndex, 0); const SwTableBox* pTLBox = pTable->GetTableBox( sTLName ); bool bAppend = false; if(!pTLBox) { bAppend = true; // to append at the end the cursor must be in the last line SwTableBoxes& rBoxes = pLine->GetTabBoxes(); pTLBox = rBoxes.back(); } if(!pTLBox) throw uno::RuntimeException("Illegal arguments", static_cast(this)); const SwStartNode* pSttNd = pTLBox->GetSttNd(); SwPosition aPos(*pSttNd); UnoActionContext aAction(pFrameFormat->GetDoc()); auto pUnoCursor(pFrameFormat->GetDoc()->CreateUnoCursor(aPos, true)); pUnoCursor->Move(fnMoveForward, GoInNode); { // remove actions - TODO: why? UnoActionRemoveContext aRemoveContext(pUnoCursor->GetDoc()); } pFrameFormat->GetDoc()->InsertCol(*pUnoCursor, static_cast(nCount), bAppend); } ///@see SwXTableRows::removeByIndex (TODO: seems to be copy and paste programming here) void SwXTableColumns::removeByIndex(sal_Int32 nIndex, sal_Int32 nCount) { SolarMutexGuard aGuard; if (nCount == 0) return; SwFrameFormat* pFrameFormat(lcl_EnsureCoreConnected(GetFrameFormat(), static_cast(this))); if(nIndex < 0 || nCount <=0 ) throw uno::RuntimeException(); SwTable* pTable = lcl_EnsureTableNotComplex(SwTable::FindTable(pFrameFormat), static_cast(this)); const OUString sTLName = sw_GetCellName(nIndex, 0); const SwTableBox* pTLBox = pTable->GetTableBox( sTLName ); if(!pTLBox) throw uno::RuntimeException("Cell not found", static_cast(this)); const SwStartNode* pSttNd = pTLBox->GetSttNd(); SwPosition aPos(*pSttNd); // set cursor to the upper-left cell of the range auto pUnoCursor(pFrameFormat->GetDoc()->CreateUnoCursor(aPos, true)); pUnoCursor->Move(fnMoveForward, GoInNode); pUnoCursor->SetRemainInSection(false); const OUString sTRName = sw_GetCellName(nIndex + nCount - 1, 0); const SwTableBox* pTRBox = pTable->GetTableBox(sTRName); if(!pTRBox) throw uno::RuntimeException("Cell not found", static_cast(this)); pUnoCursor->SetMark(); pUnoCursor->GetPoint()->nNode = *pTRBox->GetSttNd(); pUnoCursor->Move(fnMoveForward, GoInNode); SwUnoTableCursor& rCursor = dynamic_cast(*pUnoCursor); { // HACK: remove pending actions for selecting old style tables UnoActionRemoveContext aRemoveContext(rCursor); } rCursor.MakeBoxSels(); { // these braces are important UnoActionContext aAction(pFrameFormat->GetDoc()); pFrameFormat->GetDoc()->DeleteCol(*pUnoCursor); pUnoCursor.reset(); } { // invalidate all actions - TODO: why? UnoActionRemoveContext aRemoveContext(pFrameFormat->GetDoc()); } } void SwXTableColumns::Impl::Notify(const SfxHint& rHint) { if(rHint.GetId() == SfxHintId::Dying) m_pFrameFormat = nullptr; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */