diff options
Diffstat (limited to 'svx/source/table/svdotable.cxx')
-rw-r--r-- | svx/source/table/svdotable.cxx | 2519 |
1 files changed, 2519 insertions, 0 deletions
diff --git a/svx/source/table/svdotable.cxx b/svx/source/table/svdotable.cxx new file mode 100644 index 0000000000..9398a2c984 --- /dev/null +++ b/svx/source/table/svdotable.cxx @@ -0,0 +1,2519 @@ +/* -*- 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 <memory> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <unotools/configmgr.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/ptrstyle.hxx> +#include <com/sun/star/style/XStyle.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <svl/style.hxx> +#include <editeng/editstat.hxx> +#include <editeng/outlobj.hxx> +#include <sdr/properties/textproperties.hxx> +#include <svx/svdmodel.hxx> +#include <svx/svdotable.hxx> +#include <svx/svdhdl.hxx> +#include "viewcontactoftableobj.hxx" +#include <svx/svdoutl.hxx> +#include <svx/svddrag.hxx> +#include <tablemodel.hxx> +#include <cell.hxx> +#include "tablelayouter.hxx" +#include "tablehandles.hxx" +#include <svx/sdr/table/tabledesign.hxx> +#include <svx/svdundo.hxx> +#include <svx/strings.hrc> +#include <svx/dialmgr.hxx> +#include <editeng/writingmodeitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <libxml/xmlwriter.h> +#include <comphelper/diagnose_ex.hxx> + +#include <boost/property_tree/ptree.hpp> + +#include "sdrtableobjimpl.hxx" + +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::container::XIndexAccess; +using ::com::sun::star::style::XStyle; +using ::com::sun::star::table::XTableRows; +using ::com::sun::star::table::XTableColumns; +using ::com::sun::star::table::XTable; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::util::XModifyBroadcaster; +using sdr::properties::TextProperties; +using sdr::properties::BaseProperties; +using namespace ::com::sun::star; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::style; + +namespace sdr::table { + +namespace { + +class TableProperties : public TextProperties +{ +protected: + // create a new itemset + SfxItemSet CreateObjectSpecificItemSet(SfxItemPool& rPool) override; + +public: + // basic constructor + explicit TableProperties(SdrObject& rObj ); + + // constructor for copying, but using new object + TableProperties(const TableProperties& rProps, SdrObject& rObj ); + + // Clone() operator, normally just calls the local copy constructor + std::unique_ptr<BaseProperties> Clone(SdrObject& rObj) const override; + + virtual void ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem = nullptr) override; +}; + +} + +TableProperties::TableProperties(SdrObject& rObj) +: TextProperties(rObj) +{ +} + +TableProperties::TableProperties(const TableProperties& rProps, SdrObject& rObj) +: TextProperties(rProps, rObj) +{ +} + +std::unique_ptr<BaseProperties> TableProperties::Clone(SdrObject& rObj) const +{ + return std::unique_ptr<BaseProperties>(new TableProperties(*this, rObj)); +} + +void TableProperties::ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem) +{ + if( nWhich == SDRATTR_TEXTDIRECTION ) + AttributeProperties::ItemChange( nWhich, pNewItem ); + else + TextProperties::ItemChange( nWhich, pNewItem ); +} + +// create a new itemset +SfxItemSet TableProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool) +{ + return SfxItemSet(rPool, + + // range from SdrAttrObj + svl::Items<SDRATTR_START, SDRATTR_SHADOW_LAST, + SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, + SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION, + + // range for SdrTableObj + SDRATTR_TABLE_FIRST, SDRATTR_TABLE_LAST, + + // range from SdrTextObj + EE_ITEMS_START, EE_ITEMS_END>); +} + +namespace { + +class TableObjectGeoData : public SdrTextObjGeoData +{ +public: + tools::Rectangle maLogicRect; +}; + +} + +TableStyleSettings::TableStyleSettings() +: mbUseFirstRow(true) +, mbUseLastRow(false) +, mbUseFirstColumn(false) +, mbUseLastColumn(false) +, mbUseRowBanding(true) +, mbUseColumnBanding(false) +{ +} + +TableStyleSettings::TableStyleSettings( const TableStyleSettings& rStyle ) +{ + (*this) = rStyle; +} + +TableStyleSettings& TableStyleSettings::operator=(const TableStyleSettings& rStyle) +{ + mbUseFirstRow = rStyle.mbUseFirstRow; + mbUseLastRow = rStyle.mbUseLastRow; + mbUseFirstColumn = rStyle.mbUseFirstColumn; + mbUseLastColumn = rStyle.mbUseLastColumn; + mbUseRowBanding = rStyle.mbUseRowBanding; + mbUseColumnBanding = rStyle.mbUseColumnBanding; + return *this; +} + +bool TableStyleSettings::operator==( const TableStyleSettings& rStyle ) const +{ + return + (mbUseFirstRow == rStyle.mbUseFirstRow) && + (mbUseLastRow == rStyle.mbUseLastRow) && + (mbUseFirstColumn == rStyle.mbUseFirstColumn) && + (mbUseLastColumn == rStyle.mbUseLastColumn) && + (mbUseRowBanding == rStyle.mbUseRowBanding) && + (mbUseColumnBanding == rStyle.mbUseColumnBanding); +} + + +SdrTableObjImpl* SdrTableObjImpl::lastLayoutTable = nullptr; +tools::Rectangle SdrTableObjImpl::lastLayoutInputRectangle; +tools::Rectangle SdrTableObjImpl::lastLayoutResultRectangle; +bool SdrTableObjImpl::lastLayoutFitWidth; +bool SdrTableObjImpl::lastLayoutFitHeight; +WritingMode SdrTableObjImpl::lastLayoutMode; +sal_Int32 SdrTableObjImpl::lastRowCount; +sal_Int32 SdrTableObjImpl::lastColCount; +bool SdrTableObjImpl::rowSizeChanged = false; +std::vector<sal_Int32> SdrTableObjImpl::lastColWidths; + +SdrTableObjImpl::SdrTableObjImpl() +: mpTableObj( nullptr ) +, mbSkipChangeLayout(false) +{ +} + + +SdrTableObjImpl::~SdrTableObjImpl() +{ + if( lastLayoutTable == this ) + lastLayoutTable = nullptr; +} + + +void SdrTableObjImpl::CropTableModelToSelection(const CellPos& rStart, const CellPos& rEnd) +{ + if(!mxTable.is()) + { + return; + } + + const sal_Int32 nColumns(rEnd.mnCol - rStart.mnCol + 1); + const sal_Int32 nRows(rEnd.mnRow - rStart.mnRow + 1); + + if(nColumns < 1 || nRows < 1 || nColumns > getColumnCount() || nRows > getRowCount()) + { + return; + } + + // tdf#116977 First thought was to create the new TableModel, copy data to it and then exchange + // mxTable and dispose old one. This does *not* work, even when all stuff looks nicely referenced + // and safe *because* Cell::create gets handed over the current SdrTableObj, hands it to + // ::Cell and there the local mxTable is initialized using rTableObj.getTable() (!). Due to This, + // the new created Cells in a new created TableModel based on given mpTableObj *will be disposed* + // when the old mxTable gets disposed - ARGH! + // To avoid, change strategy: Remember old TableModel, reset mxTable immediately - this is the + // SdrTableObjImpl of the current SdrTableObj anyways. Luckily, this works as intended... + + // remember old TableModel + TableModelRef xOldTable(mxTable); + + // immediately create new one and initialize. This creates ::Cell's which then will use + // the correct TableModel (accessed through SdrTableObj, but using local mxTable) + mxTable = new TableModel(mpTableObj); + mxTable->init(nColumns, nRows); + + // copy cells + for( sal_Int32 nRow = 0; nRow < nRows; ++nRow ) + { + for( sal_Int32 nCol = 0; nCol < nColumns; ++nCol ) try + { + CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) ); + if( xTargetCell.is() ) + xTargetCell->cloneFrom( dynamic_cast< Cell* >( xOldTable->getCellByPosition( rStart.mnCol + nCol, rStart.mnRow + nRow ).get() ) ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION("svx.table", ""); + } + } + + // copy row heights + Reference< XTableRows > xNewRows(mxTable->getRows(), css::uno::UNO_SET_THROW ); + static constexpr OUStringLiteral sHeight( u"Height" ); + for( sal_Int32 nRow = 0; nRow < nRows; ++nRow ) + { + Reference< XPropertySet > xNewSet( xNewRows->getByIndex( nRow ), UNO_QUERY_THROW ); + xNewSet->setPropertyValue( sHeight, Any( mpLayouter->getRowHeight( rStart.mnRow + nRow ) ) ); + } + + // copy column widths + Reference< XTableColumns > xNewColumns( mxTable->getColumns(), css::uno::UNO_SET_THROW ); + static constexpr OUStringLiteral sWidth( u"Width" ); + for( sal_Int32 nCol = 0; nCol < nColumns; ++nCol ) + { + Reference< XPropertySet > xNewSet( xNewColumns->getByIndex( nCol ), UNO_QUERY_THROW ); + xNewSet->setPropertyValue( sWidth, Any( mpLayouter->getColumnWidth( rStart.mnCol + nCol ) ) ); + } + + // reset layouter which still holds a copy to old TableModel + mpLayouter.reset(); + + // cleanup old TableModel + { + Reference< XModifyListener > xListener( static_cast< css::util::XModifyListener* >(this) ); + xOldTable->removeModifyListener( xListener ); + xOldTable->dispose(); + xOldTable.clear(); + } + + // create and hand over to new TableLayouter + mpLayouter.reset(new TableLayouter( mxTable )); + + // add needed listener to react on changes + Reference< XModifyListener > xListener( static_cast< css::util::XModifyListener* >(this) ); + mxTable->addModifyListener( xListener ); + + // Apply Style to Cells + ApplyCellStyles(); + + // layout cropped table + auto aRectangle = mpTableObj->getRectangle(); + LayoutTable(aRectangle, false, false); + mpTableObj->setRectangle(aRectangle); +} + +void SdrTableObjImpl::init( SdrTableObj* pTable, sal_Int32 nColumns, sal_Int32 nRows ) +{ + mpTableObj = pTable; + mxTable = new TableModel( pTable ); + mxTable->init( nColumns, nRows ); + Reference< XModifyListener > xListener( static_cast< css::util::XModifyListener* >(this) ); + mxTable->addModifyListener( xListener ); + mpLayouter.reset(new TableLayouter( mxTable )); + auto aRectangle = mpTableObj->getRectangle(); + LayoutTable(aRectangle, true, true); + mpTableObj->setRectangle(aRectangle); + mpTableObj->maLogicRect = aRectangle; +} + + +SdrTableObjImpl& SdrTableObjImpl::operator=( const SdrTableObjImpl& rSource ) +{ + if(this == &rSource) + { + return *this; + } + + if(nullptr == mpTableObj || nullptr == rSource.mpTableObj) + { + // error: need both SdrObjects to successfully copy data + return *this; + } + + // remove evtl. listeners from local + disconnectTableStyle(); + + // reset layouter which holds a copy + mpLayouter.reset(); + + // cleanup local mxTable if used + if( mxTable.is() ) + { + Reference< XModifyListener > xListener( static_cast< css::util::XModifyListener* >(this) ); + mxTable->removeModifyListener( xListener ); + mxTable->dispose(); + mxTable.clear(); + } + + // tdf#127481: reset active cell reference + mxActiveCell.clear(); + + // copy TableStyle (short internal data) + maTableStyle = rSource.maTableStyle; + + // create/copy new mxTable. This will copy all needed cells, too + mxTable = new TableModel( mpTableObj, rSource.mxTable ); + + // create and hand over to new TableLayouter + mpLayouter.reset(new TableLayouter( mxTable )); + + // add needed listener to react on changes + Reference< XModifyListener > xListener( static_cast< css::util::XModifyListener* >(this) ); + mxTable->addModifyListener( xListener ); + + // handle TableStyle + Reference< XIndexAccess > xNewTableStyle; + SdrModel& rSourceSdrModel(rSource.mpTableObj->getSdrModelFromSdrObject()); + SdrModel& rTargetSdrModel(mpTableObj->getSdrModelFromSdrObject()); + + if(rSource.mxTableStyle.is() && &rSourceSdrModel == &rTargetSdrModel) + { + // source and target model the same -> keep current TableStyle + xNewTableStyle = rSource.mxTableStyle; + } + + if(!xNewTableStyle.is() && rSource.mxTableStyle.is()) try + { + // search in target SdrModel for that TableStyle + const OUString sStyleName( Reference< XNamed >( rSource.mxTableStyle, UNO_QUERY_THROW )->getName() ); + Reference< XStyleFamiliesSupplier > xSFS(rTargetSdrModel.getUnoModel(), UNO_QUERY_THROW ); + Reference< XNameAccess > xFamilyNameAccess( xSFS->getStyleFamilies(), css::uno::UNO_SET_THROW ); + Reference< XNameAccess > xTableFamilyAccess( xFamilyNameAccess->getByName( "table" ), UNO_QUERY_THROW ); + + if( xTableFamilyAccess->hasByName( sStyleName ) ) + { + // found table style with the same name + xTableFamilyAccess->getByName( sStyleName ) >>= xNewTableStyle; + } + else + { + // copy or? Not found, use 1st existing TableStyle (or none) + Reference< XIndexAccess > xIndexAccess( xTableFamilyAccess, UNO_QUERY_THROW ); + xIndexAccess->getByIndex( 0 ) >>= xNewTableStyle; + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION("svx.table", ""); + } + + // set that TableStyle + mxTableStyle = xNewTableStyle; + + // Apply Style to Cells + ApplyCellStyles(); + + // copy geometry + mpTableObj->setRectangle(mpTableObj->maLogicRect); + + // layout cloned table + auto aRectangle = mpTableObj->getRectangle(); + LayoutTable(aRectangle, false, false); + mpTableObj->setRectangle(aRectangle); + + // re-connect to styles (evtl. in new SdrModel) + connectTableStyle(); + + return *this; +} + +void SdrTableObjImpl::ApplyCellStyles() +{ + if( !mxTable.is() || !mxTableStyle.is() ) + return; + + const sal_Int32 nColCount = getColumnCount(); + const sal_Int32 nRowCount = getRowCount(); + + const TableStyleSettings& rStyle = maTableStyle; + + CellPos aPos; + for( aPos.mnRow = 0; aPos.mnRow < nRowCount; ++aPos.mnRow ) + { + const bool bFirstRow = (aPos.mnRow == 0) && rStyle.mbUseFirstRow; + const bool bLastRow = (aPos.mnRow == nRowCount-1) && rStyle.mbUseLastRow; + + for( aPos.mnCol = 0; aPos.mnCol < nColCount; ++aPos.mnCol ) + { + Reference< XStyle > xStyle; + + // first and last row win first, if used and available + if( bFirstRow ) + { + mxTableStyle->getByIndex(first_row_style) >>= xStyle; + } + else if( bLastRow ) + { + mxTableStyle->getByIndex(last_row_style) >>= xStyle; + } + + if( !xStyle.is() ) + { + // next come first and last column, if used and available + if( rStyle.mbUseFirstColumn && (aPos.mnCol == 0) ) + { + mxTableStyle->getByIndex(first_column_style) >>= xStyle; + } + else if( rStyle.mbUseLastColumn && (aPos.mnCol == nColCount-1) ) + { + mxTableStyle->getByIndex(last_column_style) >>= xStyle; + } + } + + if( !xStyle.is() && rStyle.mbUseRowBanding ) + { + if( (aPos.mnRow & 1) == 0 ) + { + mxTableStyle->getByIndex(even_rows_style) >>= xStyle; + } + else + { + mxTableStyle->getByIndex(odd_rows_style) >>= xStyle; + } + } + + if( !xStyle.is() && rStyle.mbUseColumnBanding ) + { + if( (aPos.mnCol & 1) == 0 ) + { + mxTableStyle->getByIndex(even_columns_style) >>= xStyle; + } + else + { + mxTableStyle->getByIndex(odd_columns_style) >>= xStyle; + } + } + + if( !xStyle.is() ) + { + // use default cell style if non found yet + mxTableStyle->getByIndex(body_style) >>= xStyle; + } + + + if( xStyle.is() ) + { + SfxUnoStyleSheet* pStyle = SfxUnoStyleSheet::getUnoStyleSheet(xStyle); + + if( pStyle ) + { + CellRef xCell( getCell( aPos ) ); + if( xCell.is() && ( xCell->GetStyleSheet() != pStyle ) ) + { + xCell->SetStyleSheet( pStyle, true ); + } + } + } + } + } +} + + +void SdrTableObjImpl::dispose() +{ + disconnectTableStyle(); + mxTableStyle.clear(); + + mpLayouter.reset(); + + if( mxTable.is() ) + { + Reference< XModifyListener > xListener( static_cast< css::util::XModifyListener* >(this) ); + mxTable->removeModifyListener( xListener ); + mxTable->dispose(); + mxTable.clear(); + } +} + + +void SdrTableObjImpl::DragEdge( bool mbHorizontal, int nEdge, sal_Int32 nOffset ) +{ + if( !((nEdge >= 0) && mxTable.is())) + return; + + try + { + static constexpr OUString sSize( u"Size"_ustr ); + if( mbHorizontal ) + { + if (nEdge <= getRowCount()) + { + sal_Int32 nHeight = mpLayouter->getRowHeight( (!nEdge)?nEdge:(nEdge-1) ); + if(nEdge==0) + nHeight -= nOffset; + else + nHeight += nOffset; + Reference< XIndexAccess > xRows( mxTable->getRows(), UNO_QUERY_THROW ); + Reference< XPropertySet > xRowSet( xRows->getByIndex( (!nEdge)?nEdge:(nEdge-1) ), UNO_QUERY_THROW ); + xRowSet->setPropertyValue( sSize, Any( nHeight ) ); + rowSizeChanged = true; + } + } + else + { + /* + fixes fdo#59889 and resizing of table in edge dragging + Total vertical edges in a NxN table is N+1, indexed from 0 to N and total Columns is N, indexed from 0 to N-1 + In LTR table vertical edge responsible for dragging of column x(x=0 to N-1) is, Edge x+1 + But in RTL table vertical edge responsible for dragging of column x(x=0 to N-1, but from right to left)is, Edge x + In LTR table dragging of edge 0(for RTL table edge N) does nothing. + */ + //Todo: Implement Dragging functionality for leftmost edge of table. + if (nEdge <= getColumnCount()) + { + const bool bRTL = mpTableObj != nullptr && (mpTableObj->GetWritingMode() == WritingMode_RL_TB); + sal_Int32 nWidth; + if(bRTL) + { + nWidth = mpLayouter->getColumnWidth( nEdge ); + } + else + { + nWidth = mpLayouter->getColumnWidth( (!nEdge)?nEdge:(nEdge-1) ); + } + Reference< XIndexAccess > xCols( mxTable->getColumns(), UNO_QUERY_THROW ); + nWidth += nOffset; + if(bRTL && nEdge<getColumnCount()) + { + Reference< XPropertySet > xColSet( xCols->getByIndex( nEdge ), UNO_QUERY_THROW ); + xColSet->setPropertyValue( sSize, Any( nWidth ) ); + } + else if(!bRTL && nEdge>0) + { + Reference< XPropertySet > xColSet( xCols->getByIndex( nEdge-1 ), UNO_QUERY_THROW ); + xColSet->setPropertyValue( sSize, Any( nWidth ) ); + } + /* To prevent the table resizing on edge dragging */ + if( nEdge > 0 && nEdge < mxTable->getColumnCount() ) + { + if( bRTL ) + nEdge--; + + nWidth = mpLayouter->getColumnWidth(nEdge); + nWidth = std::max(static_cast<sal_Int32>(nWidth - nOffset), sal_Int32(0)); + + Reference<XPropertySet> xColSet(xCols->getByIndex(nEdge), UNO_QUERY_THROW); + xColSet->setPropertyValue(sSize, Any(nWidth)); + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION("svx.table", ""); + } +} + + +// XModifyListener + + +void SAL_CALL SdrTableObjImpl::modified( const css::lang::EventObject& aEvent ) +{ + if (aEvent.Source == mxTableStyle && mpTableObj) + static_cast<TextProperties&>(mpTableObj->GetProperties()).increaseVersion(); + + update(); +} + +void SdrTableObjImpl::update() +{ + // source can be the table model itself or the assigned table template + TableModelNotifyGuard aGuard( mxTable.get() ); + if( !mpTableObj ) + return; + + if( (maEditPos.mnRow >= getRowCount()) || (maEditPos.mnCol >= getColumnCount()) || (getCell( maEditPos ) != mxActiveCell) ) + { + if(maEditPos.mnRow >= getRowCount()) + maEditPos.mnRow = getRowCount()-1; + + if(maEditPos.mnCol >= getColumnCount()) + maEditPos.mnCol = getColumnCount()-1; + + mpTableObj->setActiveCell( maEditPos ); + } + + ApplyCellStyles(); + + mpTableObj->setRectangle(mpTableObj->maLogicRect); + auto aRectangle = mpTableObj->getRectangle(); + LayoutTable(aRectangle, false, false); + mpTableObj->setRectangle(aRectangle); + + mpTableObj->SetBoundAndSnapRectsDirty(); + mpTableObj->ActionChanged(); + mpTableObj->BroadcastObjectChange(); +} + + +void SdrTableObjImpl::connectTableStyle() +{ + if( mxTableStyle.is() ) + { + Reference< XModifyBroadcaster > xBroadcaster( mxTableStyle, UNO_QUERY ); + if( xBroadcaster.is() ) + { + Reference< XModifyListener > xListener( static_cast< css::util::XModifyListener* >(this) ); + xBroadcaster->addModifyListener( xListener ); + } + } +} + + +void SdrTableObjImpl::disconnectTableStyle() +{ + if( mxTableStyle.is() ) + { + Reference< XModifyBroadcaster > xBroadcaster( mxTableStyle, UNO_QUERY ); + if( xBroadcaster.is() ) + { + Reference< XModifyListener > xListener( static_cast< css::util::XModifyListener* >(this) ); + xBroadcaster->removeModifyListener( xListener ); + } + } +} + + +bool SdrTableObjImpl::isInUse() +{ + return mpTableObj && mpTableObj->IsInserted(); +} + +void SdrTableObjImpl::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrTableObjImpl")); + if (mpLayouter) + mpLayouter->dumpAsXml(pWriter); + mxTable->dumpAsXml(pWriter); + (void)xmlTextWriterEndElement(pWriter); +} + + +// XEventListener + + +void SAL_CALL SdrTableObjImpl::disposing( const css::lang::EventObject& Source ) +{ + assert(Source.Source == mxTableStyle); + (void)Source; + + Reference<XIndexAccess> xDefaultStyle; + try + { + Reference<XStyleFamiliesSupplier> xSupplier(mpTableObj->getSdrModelFromSdrObject().getUnoModel(), UNO_QUERY_THROW); + Reference<XNameAccess> xTableFamily(xSupplier->getStyleFamilies()->getByName("table"), UNO_QUERY_THROW); + xDefaultStyle.set(xTableFamily->getByName("default"), UNO_QUERY_THROW); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION("svx.table", ""); + } + + mpTableObj->setTableStyle(xDefaultStyle); +} + + +CellRef SdrTableObjImpl::getCell( const CellPos& rPos ) const +{ + CellRef xCell; + if( mxTable.is() ) try + { + xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ).get() ) ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION("svx.table", ""); + } + return xCell; +} + + +sal_Int32 SdrTableObjImpl::getColumnCount() const +{ + return mxTable.is() ? mxTable->getColumnCount() : 0; +} + +std::vector<sal_Int32> SdrTableObjImpl::getColumnWidths() const +{ + std::vector<sal_Int32> aRet; + + if (mxTable.is()) + aRet = mxTable->getColumnWidths(); + + return aRet; +} + +sal_Int32 SdrTableObjImpl::getRowCount() const +{ + return mxTable.is() ? mxTable->getRowCount() : 0; +} + +void SdrTableObjImpl::LayoutTable( tools::Rectangle& rArea, bool bFitWidth, bool bFitHeight ) +{ + if (utl::ConfigManager::IsFuzzing()) + return; + if(!mpLayouter) + return; + + // Optimization: SdrTableObj::SetChanged() can call this very often, repeatedly + // with the same settings, noticeably increasing load time. Skip if already done. + bool bInteractiveMightGrowBecauseTextChanged = + mpTableObj->IsReallyEdited() && (mpTableObj->IsAutoGrowHeight() || mpTableObj->IsAutoGrowWidth()); + WritingMode writingMode = mpTableObj->GetWritingMode(); + if( bInteractiveMightGrowBecauseTextChanged + || lastLayoutTable != this || lastLayoutInputRectangle != rArea + || lastLayoutFitWidth != bFitWidth || lastLayoutFitHeight != bFitHeight + || lastLayoutMode != writingMode + || lastRowCount != getRowCount() + || lastColCount != getColumnCount() + || lastColWidths != getColumnWidths() + || rowSizeChanged ) + { + lastLayoutTable = this; + lastLayoutInputRectangle = rArea; + lastLayoutFitWidth = bFitWidth; + lastLayoutFitHeight = bFitHeight; + lastLayoutMode = writingMode; + lastRowCount = getRowCount(); + lastColCount = getColumnCount(); + // Column resize, when the total width and column count of the + // table is unchanged, but re-layout is still needed. + lastColWidths = getColumnWidths(); + TableModelNotifyGuard aGuard( mxTable.get() ); + mpLayouter->LayoutTable( rArea, bFitWidth, bFitHeight ); + lastLayoutResultRectangle = rArea; + rowSizeChanged = false; + } + else + { + rArea = lastLayoutResultRectangle; + mpLayouter->UpdateBorderLayout(); + } +} + +void SdrTableObjImpl::UpdateCells( tools::Rectangle const & rArea ) +{ + if( mpLayouter && mxTable.is() ) + { + TableModelNotifyGuard aGuard( mxTable.get() ); + mpLayouter->updateCells( rArea ); + mxTable->setModified(true); + } +} + + +// BaseProperties section + + +std::unique_ptr<sdr::properties::BaseProperties> SdrTableObj::CreateObjectSpecificProperties() +{ + return std::make_unique<TableProperties>(*this); +} + + +// DrawContact section + + +std::unique_ptr<sdr::contact::ViewContact> SdrTableObj::CreateObjectSpecificViewContact() +{ + return std::make_unique<sdr::contact::ViewContactOfTableObj>(*this); +} + +SdrTableObj::SdrTableObj(SdrModel& rSdrModel) +: SdrTextObj(rSdrModel) +{ + osl_atomic_increment(&m_refCount); // other I get deleted during construction + init( 1, 1 ); + osl_atomic_decrement(&m_refCount); +} + +SdrTableObj::SdrTableObj(SdrModel& rSdrModel, SdrTableObj const & rSource) +: SdrTextObj(rSdrModel, rSource) +{ + osl_atomic_increment(&m_refCount); + + init( 1, 1 ); + + TableModelNotifyGuard aGuard( mpImpl.is() ? mpImpl->mxTable.get() : nullptr ); + + maLogicRect = rSource.maLogicRect; + maRectangle = rSource.maRectangle; + maGeo = rSource.maGeo; + meTextKind = rSource.meTextKind; + mbTextFrame = rSource.mbTextFrame; + maTextSize = rSource.maTextSize; + mbTextSizeDirty = rSource.mbTextSizeDirty; + mbNoShear = rSource.mbNoShear; + mbDisableAutoWidthOnDragging = rSource.mbDisableAutoWidthOnDragging; + + // use SdrTableObjImpl::operator= now to + // copy model data and other stuff (see there) + *mpImpl = *rSource.mpImpl; + + osl_atomic_decrement(&m_refCount); +} + +SdrTableObj::SdrTableObj( + SdrModel& rSdrModel, + const ::tools::Rectangle& rNewRect, + sal_Int32 nColumns, + sal_Int32 nRows) +: SdrTextObj(rSdrModel, rNewRect) + ,maLogicRect(rNewRect) +{ + osl_atomic_increment(&m_refCount); + + if( nColumns <= 0 ) + nColumns = 1; + + if( nRows <= 0 ) + nRows = 1; + + init( nColumns, nRows ); + + osl_atomic_decrement(&m_refCount); +} + + +void SdrTableObj::init( sal_Int32 nColumns, sal_Int32 nRows ) +{ + m_bClosedObj = true; + + mpImpl = new SdrTableObjImpl; + mpImpl->init( this, nColumns, nRows ); + + // Stuff done from old SetModel: + if( !maLogicRect.IsEmpty() ) + { + setRectangle(maLogicRect); + auto aRectangle = getRectangle(); + mpImpl->LayoutTable(aRectangle, false, false); + setRectangle(aRectangle); + } +} + + +SdrTableObj::~SdrTableObj() +{ + mpImpl->dispose(); +} + + +// table stuff + + +Reference< XTable > SdrTableObj::getTable() const +{ + return mpImpl->mxTable; +} + + +bool SdrTableObj::isValid( const CellPos& rPos ) const +{ + return (rPos.mnCol >= 0) && (rPos.mnCol < mpImpl->getColumnCount()) && (rPos.mnRow >= 0) && (rPos.mnRow < mpImpl->getRowCount()); +} + + +CellPos SdrTableObj::getFirstCell() +{ + return CellPos( 0,0 ); +} + + +CellPos SdrTableObj::getLastCell() const +{ + CellPos aPos; + if( mpImpl->mxTable.is() ) + { + aPos.mnCol = mpImpl->getColumnCount()-1; + aPos.mnRow = mpImpl->getRowCount()-1; + } + return aPos; +} + + +CellPos SdrTableObj::getLeftCell( const CellPos& rPos, bool bEdgeTravel ) const +{ + switch( GetWritingMode() ) + { + default: + case WritingMode_LR_TB: + return getPreviousCell( rPos, bEdgeTravel ); + case WritingMode_RL_TB: + return getNextCell( rPos, bEdgeTravel ); + case WritingMode_TB_RL: + return getPreviousRow( rPos, bEdgeTravel ); + } +} + + +CellPos SdrTableObj::getRightCell( const CellPos& rPos, bool bEdgeTravel ) const +{ + switch( GetWritingMode() ) + { + default: + case WritingMode_LR_TB: + return getNextCell( rPos, bEdgeTravel ); + case WritingMode_RL_TB: + return getPreviousCell( rPos, bEdgeTravel ); + case WritingMode_TB_RL: + return getNextRow( rPos, bEdgeTravel ); + } +} + + +CellPos SdrTableObj::getUpCell( const CellPos& rPos, bool bEdgeTravel ) const +{ + switch( GetWritingMode() ) + { + default: + case WritingMode_LR_TB: + case WritingMode_RL_TB: + return getPreviousRow( rPos, bEdgeTravel ); + case WritingMode_TB_RL: + return getPreviousCell( rPos, bEdgeTravel ); + } +} + + +CellPos SdrTableObj::getDownCell( const CellPos& rPos, bool bEdgeTravel ) const +{ + switch( GetWritingMode() ) + { + default: + case WritingMode_LR_TB: + case WritingMode_RL_TB: + return getNextRow( rPos, bEdgeTravel ); + case WritingMode_TB_RL: + return getNextCell( rPos, bEdgeTravel ); + } +} + + +CellPos SdrTableObj::getPreviousCell( const CellPos& rPos, bool bEdgeTravel ) const +{ + CellPos aPos( rPos ); + if( mpImpl.is() ) + { + CellRef xCell( mpImpl->getCell( aPos ) ); + if( xCell.is() && xCell->isMerged() ) + { + sal_Int32 nTemp = 0; + findMergeOrigin( mpImpl->mxTable, aPos.mnCol, aPos.mnRow, aPos.mnCol, nTemp ); + } + + if( aPos.mnCol > 0 ) + { + --aPos.mnCol; + } + + else if( bEdgeTravel && (aPos.mnRow > 0) ) + { + aPos.mnCol = mpImpl->mxTable->getColumnCount()-1; + --aPos.mnRow; + } + } + return aPos; +} + + +CellPos SdrTableObj::getNextCell( const CellPos& rPos, bool bEdgeTravel ) const +{ + CellPos aPos( rPos ); + if( mpImpl.is() ) + { + CellRef xCell( mpImpl->getCell( aPos ) ); + if( xCell.is() ) + { + if( xCell->isMerged() ) + { + findMergeOrigin( mpImpl->mxTable, aPos.mnCol, aPos.mnRow, aPos.mnCol, aPos.mnRow ); + + xCell = mpImpl->getCell(aPos); + + if( xCell.is() ) + { + aPos.mnCol += xCell->getColumnSpan(); + aPos.mnRow = rPos.mnRow; + } + } + else + { + aPos.mnCol += xCell->getColumnSpan(); + } + + if( aPos.mnCol < mpImpl->mxTable->getColumnCount() ) + return aPos; + + if( bEdgeTravel && ((aPos.mnRow + 1) < mpImpl->getRowCount()) ) + { + aPos.mnCol = 0; + aPos.mnRow += 1; + return aPos; + } + } + } + + // last cell reached, no traveling possible + return rPos; +} + + +CellPos SdrTableObj::getPreviousRow( const CellPos& rPos, bool bEdgeTravel ) const +{ + CellPos aPos( rPos ); + if( mpImpl.is() ) + { + CellRef xCell( mpImpl->getCell( aPos ) ); + if( xCell.is() && xCell->isMerged() ) + { + sal_Int32 nTemp = 0; + findMergeOrigin( mpImpl->mxTable, aPos.mnCol, aPos.mnRow, nTemp, aPos.mnRow ); + } + + if( aPos.mnRow > 0 ) + { + --aPos.mnRow; + } + else if( bEdgeTravel && (aPos.mnCol > 0) ) + { + aPos.mnRow = mpImpl->mxTable->getRowCount()-1; + --aPos.mnCol; + } + } + return aPos; +} + + +CellPos SdrTableObj::getNextRow( const CellPos& rPos, bool bEdgeTravel ) const +{ + CellPos aPos( rPos ); + + if( mpImpl.is() ) + { + CellRef xCell( mpImpl->getCell( rPos ) ); + if( xCell.is() ) + { + if( xCell->isMerged() ) + { + findMergeOrigin( mpImpl->mxTable, aPos.mnCol, aPos.mnRow, aPos.mnCol, aPos.mnRow ); + xCell = mpImpl->getCell(aPos); + aPos.mnCol = rPos.mnCol; + } + + if( xCell.is() ) + aPos.mnRow += xCell->getRowSpan(); + + if( aPos.mnRow < mpImpl->mxTable->getRowCount() ) + return aPos; + + if( bEdgeTravel && (aPos.mnCol + 1) < mpImpl->mxTable->getColumnCount() ) + { + aPos.mnRow = 0; + aPos.mnCol += 1; + + while( aPos.mnCol < mpImpl->mxTable->getColumnCount() ) + { + xCell = mpImpl->getCell( aPos ); + if( xCell.is() && !xCell->isMerged() ) + return aPos; + aPos.mnCol += 1; + } + } + } + } + + // last position reached, no more traveling possible + return rPos; +} + + +const TableStyleSettings& SdrTableObj::getTableStyleSettings() const +{ + if( mpImpl.is()) + { + return mpImpl->maTableStyle; + } + else + { + static TableStyleSettings aTmp; + return aTmp; + } +} + + +void SdrTableObj::setTableStyleSettings( const TableStyleSettings& rStyle ) +{ + if( mpImpl.is() ) + { + mpImpl->maTableStyle = rStyle; + mpImpl->update(); + } +} + + +TableHitKind SdrTableObj::CheckTableHit( const Point& rPos, sal_Int32& rnX, sal_Int32& rnY, const sal_uInt16 aTol ) const +{ + if( !mpImpl.is() || !mpImpl->mxTable.is() ) + return TableHitKind::NONE; + + rnX = 0; + rnY = 0; + + const sal_Int32 nColCount = mpImpl->getColumnCount(); + const sal_Int32 nRowCount = mpImpl->getRowCount(); + + sal_Int32 nX = rPos.X() - getRectangle().Left(); + sal_Int32 nY = rPos.Y() - getRectangle().Top(); + + if( (nX < 0) || (nX > getRectangle().GetWidth()) || (nY < 0) || (nY > getRectangle().GetHeight() ) ) + return TableHitKind::NONE; + + // get vertical edge number and check for a hit + const bool bRTL = (GetWritingMode() == WritingMode_RL_TB); + bool bVrtHit = false; + if( !bRTL ) + { + while( rnX <= nColCount ) + { + if( nX - aTol <= 0 ) + { + bVrtHit = true; + break; + } + + if( rnX == nColCount ) + break; + + nX -= mpImpl->mpLayouter->getColumnWidth( rnX ); + if( nX < 0 ) + break; + rnX++; + } + } + else + { + rnX = nColCount; + while( rnX >= 0 ) + { + if( nX - aTol <= 0 ) + { + bVrtHit = true; + break; + } + + if( rnX == 0 ) + break; + + rnX--; + nX -= mpImpl->mpLayouter->getColumnWidth( rnX ); + if( nX < 0 ) + break; + } + } + + // rnX is now the edge number left to the pointer, if it was hit bHrzHit is also true + + // get vertical edge number and check for a hit + bool bHrzHit = false; + while( rnY <= nRowCount ) + { + if( nY - aTol <= 0 ) + { + bHrzHit = true; + break; + } + + if( rnY == nRowCount ) + break; + + nY -= mpImpl->mpLayouter->getRowHeight(rnY); + if( nY < 0 ) + break; + rnY++; + } + + // rnY is now the edge number above the pointer, if it was hit bVrtHit is also true + + if( bVrtHit && mpImpl->mpLayouter->isEdgeVisible( rnX, rnY, false ) ) + return TableHitKind::VerticallBorder; + + if( bHrzHit && mpImpl->mpLayouter->isEdgeVisible( rnX, rnY, true ) ) + return TableHitKind::HorizontalBorder; + + CellRef xCell( mpImpl->getCell( CellPos( rnX, rnY ) ) ); + if( xCell.is() && xCell->isMerged() ) + findMergeOrigin( mpImpl->mxTable, rnX, rnY, rnX, rnY ); + + if( xCell.is() ) + { + nX += mpImpl->mpLayouter->getColumnWidth( rnX ); + //Fix for fdo#62673 : non-editable cell in table on cell merge + sal_Int32 i=0; + while(xCell.is() && xCell->isMerged()) + { + nX += mpImpl->mpLayouter->getColumnWidth( rnX+i ); + i++; + if(rnX+i < nColCount) + xCell=mpImpl->getCell( CellPos( rnX+i, rnY) ); + else + break; + } + + if( nX < xCell->GetTextLeftDistance() ) + return TableHitKind::Cell; + } + + return TableHitKind::CellTextArea; +} + +const SfxItemSet& SdrTableObj::GetActiveCellItemSet() const +{ + return getActiveCell()->GetItemSet(); +} + +void SdrTableObj::setTableStyle( const Reference< XIndexAccess >& xTableStyle ) +{ + if( mpImpl.is() && (mpImpl->mxTableStyle != xTableStyle) ) + { + mpImpl->disconnectTableStyle(); + mpImpl->mxTableStyle = xTableStyle; + mpImpl->connectTableStyle(); + mpImpl->update(); + } +} + + +const Reference< XIndexAccess >& SdrTableObj::getTableStyle() const +{ + if( mpImpl.is() ) + { + return mpImpl->mxTableStyle; + } + else + { + static Reference< XIndexAccess > aTmp; + return aTmp; + } +} + + +// text stuff + + +/** returns the currently active text. */ +SdrText* SdrTableObj::getActiveText() const +{ + return getActiveCell().get(); +} + + +/** returns the nth available text. */ +SdrText* SdrTableObj::getText( sal_Int32 nIndex ) const +{ + if( mpImpl->mxTable.is() ) + { + const sal_Int32 nColCount = mpImpl->getColumnCount(); + if( nColCount ) + { + CellPos aPos( nIndex % nColCount, nIndex / nColCount ); + + CellRef xCell( mpImpl->getCell( aPos ) ); + return xCell.get(); + } + } + return nullptr; +} + + +/** returns the number of texts available for this object. */ +sal_Int32 SdrTableObj::getTextCount() const +{ + if( mpImpl->mxTable.is() ) + { + const sal_Int32 nColCount = mpImpl->getColumnCount(); + const sal_Int32 nRowCount = mpImpl->getRowCount(); + + return nColCount * nRowCount; + } + else + { + return 0; + } +} + + +/** changes the current active text */ +void SdrTableObj::setActiveText( sal_Int32 nIndex ) +{ + if( mpImpl.is() && mpImpl->mxTable.is() ) + { + const sal_Int32 nColCount = mpImpl->mxTable->getColumnCount(); + if( nColCount ) + { + CellPos aPos( nIndex % nColCount, nIndex / nColCount ); + if( isValid( aPos ) ) + setActiveCell( aPos ); + } + } +} + + +/** returns the index of the text that contains the given point or -1 */ +sal_Int32 SdrTableObj::CheckTextHit(const Point& rPnt) const +{ + if( mpImpl.is() && mpImpl->mxTable.is() ) + { + CellPos aPos; + if( CheckTableHit( rPnt, aPos.mnCol, aPos.mnRow ) == TableHitKind::CellTextArea ) + return aPos.mnRow * mpImpl->mxTable->getColumnCount() + aPos.mnCol; + } + + return 0; +} + +SdrOutliner* SdrTableObj::GetCellTextEditOutliner( const Cell& rCell ) const +{ + if( mpImpl.is() && (mpImpl->getCell( mpImpl->maEditPos ).get() == &rCell) ) + return mpEditingOutliner; + else + return nullptr; +} + +const TableLayouter& SdrTableObj::getTableLayouter() const +{ + assert(mpImpl.is() && mpImpl->mpLayouter && "getTableLayouter() error: no mpImpl or mpLayouter (!)"); + return *(mpImpl->mpLayouter); +} + +bool SdrTableObj::IsAutoGrowHeight() const +{ + return true; +} + +bool SdrTableObj::IsAutoGrowWidth() const +{ + return true; +} + +bool SdrTableObj::HasText() const +{ + return true; +} + +bool SdrTableObj::IsTextEditActive( const CellPos& rPos ) +{ + return mpEditingOutliner && mpImpl.is() && (rPos == mpImpl->maEditPos); +} + + +void SdrTableObj::onEditOutlinerStatusEvent( EditStatus* pEditStatus ) +{ + if( (pEditStatus->GetStatusWord() & EditStatusFlags::TextHeightChanged) && mpImpl.is() && mpImpl->mpLayouter ) + { + tools::Rectangle aRect0(getRectangle()); + setRectangle(maLogicRect); + auto aRectangle = getRectangle(); + mpImpl->LayoutTable(aRectangle, false, false); + setRectangle(aRectangle); + SetBoundAndSnapRectsDirty(); + ActionChanged(); + BroadcastObjectChange(); + if (aRect0 != getRectangle()) + SendUserCall(SdrUserCallType::Resize,aRect0); + } +} + + +void SdrTableObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const +{ + rInfo.bResizeFreeAllowed=true; + rInfo.bResizePropAllowed=true; + rInfo.bRotateFreeAllowed=false; + rInfo.bRotate90Allowed =false; + rInfo.bMirrorFreeAllowed=false; + rInfo.bMirror45Allowed =false; + rInfo.bMirror90Allowed =false; + + // allow transparence + rInfo.bTransparenceAllowed = true; + + rInfo.bShearAllowed =false; + rInfo.bEdgeRadiusAllowed=false; + rInfo.bCanConvToPath =false; + rInfo.bCanConvToPoly =false; + rInfo.bCanConvToPathLineToArea=false; + rInfo.bCanConvToPolyLineToArea=false; + rInfo.bCanConvToContour = false; +} + +SdrObjKind SdrTableObj::GetObjIdentifier() const +{ + return SdrObjKind::Table; +} + +void SdrTableObj::TakeTextRect( SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText, tools::Rectangle* pAnchorRect, bool /*bLineWidth*/ ) const +{ + if( mpImpl.is() ) + TakeTextRect( mpImpl->maEditPos, rOutliner, rTextRect, bNoEditText, pAnchorRect ); +} + + +void SdrTableObj::TakeTextRect( const CellPos& rPos, SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText, tools::Rectangle* pAnchorRect ) const +{ + if( !mpImpl.is()) + return; + + CellRef xCell( mpImpl->getCell( rPos ) ); + if( !xCell.is() ) + return; + + tools::Rectangle aAnkRect; + TakeTextAnchorRect( rPos, aAnkRect ); + + SdrTextVertAdjust eVAdj=xCell->GetTextVerticalAdjust(); + + EEControlBits nStat0=rOutliner.GetControlWord(); + nStat0 |= EEControlBits::AUTOPAGESIZE; + rOutliner.SetControlWord(nStat0); + rOutliner.SetMinAutoPaperSize(Size()); + rOutliner.SetMaxAutoPaperSize(aAnkRect.GetSize()); + rOutliner.SetPaperSize(aAnkRect.GetSize()); + + // #103516# New try with _BLOCK for hor and ver after completely + // supporting full width for vertical text. +// if( SDRTEXTHORZADJUST_BLOCK == eHAdj && !IsVerticalWriting()) +// { + rOutliner.SetMinAutoPaperSize(Size(aAnkRect.GetWidth(), 0)); +// } +// else if(SDRTEXTVERTADJUST_BLOCK == eVAdj && IsVerticalWriting()) +// { +// rOutliner.SetMinAutoPaperSize(Size(0, aAnkRect.GetHeight())); +// } + + + // set text at outliner, maybe from edit outliner + std::optional<OutlinerParaObject> pPara; + if (xCell->GetOutlinerParaObject()) + pPara = *xCell->GetOutlinerParaObject(); + if (mpEditingOutliner && !bNoEditText && mpImpl->mxActiveCell == xCell ) + pPara = mpEditingOutliner->CreateParaObject(); + + if (pPara) + { + const bool bHitTest(&getSdrModelFromSdrObject().GetHitTestOutliner() == &rOutliner); + const SdrTextObj* pTestObj(rOutliner.GetTextObj()); + + if( !pTestObj || !bHitTest || (pTestObj != this) || (pTestObj->GetOutlinerParaObject() != xCell->GetOutlinerParaObject()) ) + { + if( bHitTest ) // #i33696# take back fix #i27510# + rOutliner.SetTextObj( this ); + + rOutliner.SetUpdateLayout(true); + rOutliner.SetText(*pPara); + } + } + else + { + rOutliner.SetTextObj( nullptr ); + } + + rOutliner.SetUpdateLayout(true); + rOutliner.SetControlWord(nStat0); + + Point aTextPos(aAnkRect.TopLeft()); + Size aTextSiz(rOutliner.GetPaperSize()); + if (eVAdj==SDRTEXTVERTADJUST_CENTER || eVAdj==SDRTEXTVERTADJUST_BOTTOM) + { + tools::Long nFreeHgt=aAnkRect.GetHeight()-aTextSiz.Height(); + if (eVAdj==SDRTEXTVERTADJUST_CENTER) + aTextPos.AdjustY(nFreeHgt/2 ); + if (eVAdj==SDRTEXTVERTADJUST_BOTTOM) + aTextPos.AdjustY(nFreeHgt ); + } + + if (pAnchorRect) + *pAnchorRect=aAnkRect; + + rTextRect=tools::Rectangle(aTextPos,aTextSiz); +} + +const CellRef& SdrTableObj::getActiveCell() const +{ + if( mpImpl.is() ) + { + if( !mpImpl->mxActiveCell.is() ) + { + CellPos aPos; + const_cast< SdrTableObj* >(this)->setActiveCell( aPos ); + } + return mpImpl->mxActiveCell; + } + else + { + static CellRef xCell; + return xCell; + } +} + + +sal_Int32 SdrTableObj::getColumnCount() const +{ + return mpImpl.is() ? mpImpl->getColumnCount() : 0; +} + +sal_Int32 SdrTableObj::getRowCount() const +{ + return mpImpl.is() ? mpImpl->getRowCount() : 0; +} + +void SdrTableObj::changeEdge(bool bHorizontal, int nEdge, sal_Int32 nOffset) +{ + if (mpImpl.is()) + mpImpl->DragEdge(bHorizontal, nEdge, nOffset); +} + +void SdrTableObj::setActiveCell( const CellPos& rPos ) +{ + if( !(mpImpl.is() && mpImpl->mxTable.is()) ) + return; + + try + { + mpImpl->mxActiveCell.set( dynamic_cast< Cell* >( mpImpl->mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ).get() ) ); + if( mpImpl->mxActiveCell.is() && mpImpl->mxActiveCell->isMerged() ) + { + CellPos aOrigin; + findMergeOrigin( mpImpl->mxTable, rPos.mnCol, rPos.mnRow, aOrigin.mnCol, aOrigin.mnRow ); + mpImpl->mxActiveCell.set( dynamic_cast< Cell* >( mpImpl->mxTable->getCellByPosition( aOrigin.mnCol, aOrigin.mnRow ).get() ) ); + mpImpl->maEditPos = aOrigin; + } + else + { + mpImpl->maEditPos = rPos; + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION("svx.table", ""); + } +} + + +void SdrTableObj::getActiveCellPos( CellPos& rPos ) const +{ + rPos = mpImpl->maEditPos; +} + + +void SdrTableObj::getCellBounds( const CellPos& rPos, ::tools::Rectangle& rCellRect ) +{ + if( mpImpl.is() ) + { + CellRef xCell( mpImpl->getCell( rPos ) ); + if( xCell.is() ) + rCellRect = xCell->getCellRect(); + } +} + + +void SdrTableObj::TakeTextAnchorRect(tools::Rectangle& rAnchorRect) const +{ + if( mpImpl.is() ) + TakeTextAnchorRect( mpImpl->maEditPos, rAnchorRect ); +} + + +void SdrTableObj::TakeTextAnchorRect( const CellPos& rPos, tools::Rectangle& rAnchorRect ) const +{ + tools::Rectangle aAnkRect(getRectangle()); + + if( mpImpl.is() ) + { + CellRef xCell( mpImpl->getCell( rPos ) ); + if( xCell.is() ) + xCell->TakeTextAnchorRect( aAnkRect ); + } + + ImpJustifyRect(aAnkRect); + rAnchorRect=aAnkRect; +} + + +void SdrTableObj::TakeTextEditArea(Size* pPaperMin, Size* pPaperMax, tools::Rectangle* pViewInit, tools::Rectangle* pViewMin) const +{ + if( mpImpl.is() ) + TakeTextEditArea( mpImpl->maEditPos, pPaperMin, pPaperMax, pViewInit, pViewMin ); +} + + +void SdrTableObj::TakeTextEditArea( const CellPos& rPos, Size* pPaperMin, Size* pPaperMax, tools::Rectangle* pViewInit, tools::Rectangle* pViewMin ) const +{ + Size aPaperMin,aPaperMax; + tools::Rectangle aViewInit; + TakeTextAnchorRect( rPos, aViewInit ); + + Size aAnkSiz(aViewInit.GetSize()); + aAnkSiz.AdjustWidth( -1 ); aAnkSiz.AdjustHeight( -1 ); // because GetSize() increments by one + + Size aMaxSiz(aAnkSiz.Width(),1000000); + Size aTmpSiz(getSdrModelFromSdrObject().GetMaxObjSize()); + if (aTmpSiz.Height()!=0) + aMaxSiz.setHeight(aTmpSiz.Height() ); + + CellRef xCell( mpImpl->getCell( rPos ) ); + SdrTextVertAdjust eVAdj = xCell.is() ? xCell->GetTextVerticalAdjust() : SDRTEXTVERTADJUST_TOP; + + aPaperMax=aMaxSiz; + + aPaperMin.setWidth( aAnkSiz.Width() ); + + if (pViewMin!=nullptr) + { + *pViewMin=aViewInit; + tools::Long nYFree=aAnkSiz.Height()-aPaperMin.Height(); + + if (eVAdj==SDRTEXTVERTADJUST_TOP) + { + pViewMin->AdjustBottom( -nYFree ); + } + else if (eVAdj==SDRTEXTVERTADJUST_BOTTOM) + { + pViewMin->AdjustTop(nYFree ); + } + else + { + pViewMin->AdjustTop(nYFree/2 ); + pViewMin->SetBottom(pViewMin->Top()+aPaperMin.Height() ); + } + } + + + if(IsVerticalWriting()) + aPaperMin.setWidth( 0 ); + else + aPaperMin.setHeight( 0 ); + + if (pPaperMin!=nullptr) *pPaperMin=aPaperMin; + if (pPaperMax!=nullptr) *pPaperMax=aPaperMax; + if (pViewInit!=nullptr) *pViewInit=aViewInit; +} + + +EEAnchorMode SdrTableObj::GetOutlinerViewAnchorMode() const +{ + EEAnchorMode eRet=EEAnchorMode::TopLeft; + CellRef xCell( getActiveCell() ); + if( xCell.is() ) + { + SdrTextVertAdjust eV=xCell->GetTextVerticalAdjust(); + + { + if (eV==SDRTEXTVERTADJUST_TOP) + { + eRet=EEAnchorMode::TopLeft; + } + else if (eV==SDRTEXTVERTADJUST_BOTTOM) + { + eRet=EEAnchorMode::BottomLeft; + } + else + { + eRet=EEAnchorMode::VCenterLeft; + } + } + } + return eRet; +} + + +OUString SdrTableObj::TakeObjNameSingul() const +{ + OUString sName(SvxResId(STR_ObjNameSingulTable)); + + OUString aName(GetName()); + if (!aName.isEmpty()) + sName += " '" + aName + "'"; + + return sName; +} + + +OUString SdrTableObj::TakeObjNamePlural() const +{ + return SvxResId(STR_ObjNamePluralTable); +} + + +rtl::Reference<SdrObject> SdrTableObj::CloneSdrObject(SdrModel& rTargetModel) const +{ + return new SdrTableObj(rTargetModel, *this); +} + + +const tools::Rectangle& SdrTableObj::GetSnapRect() const +{ + return getRectangle(); +} + + +void SdrTableObj::NbcSetSnapRect(const tools::Rectangle& rRect) +{ + NbcSetLogicRect( rRect ); +} + + +const tools::Rectangle& SdrTableObj::GetLogicRect() const +{ + return maLogicRect; +} + + +void SdrTableObj::RecalcSnapRect() +{ +} + + +bool SdrTableObj::BegTextEdit(SdrOutliner& rOutl) +{ + if( mpEditingOutliner != nullptr ) + return false; + + mpEditingOutliner=&rOutl; + + mbInEditMode = true; + + rOutl.Init( OutlinerMode::TextObject ); + rOutl.SetRefDevice(getSdrModelFromSdrObject().GetRefDevice()); + + bool bUpdateMode = rOutl.SetUpdateLayout(false); + Size aPaperMin; + Size aPaperMax; + tools::Rectangle aEditArea; + TakeTextEditArea(&aPaperMin,&aPaperMax,&aEditArea,nullptr); + + rOutl.SetMinAutoPaperSize(aPaperMin); + rOutl.SetMaxAutoPaperSize(aPaperMax); + rOutl.SetPaperSize(aPaperMax); + + if (bUpdateMode) rOutl.SetUpdateLayout(true); + + EEControlBits nStat=rOutl.GetControlWord(); + nStat |= EEControlBits::AUTOPAGESIZE; + nStat &=~EEControlBits::STRETCHING; + rOutl.SetControlWord(nStat); + + OutlinerParaObject* pPara = GetOutlinerParaObject(); + if(pPara) + rOutl.SetText(*pPara); + + rOutl.UpdateFields(); + rOutl.ClearModifyFlag(); + + return true; +} + + +void SdrTableObj::EndTextEdit(SdrOutliner& rOutl) +{ + + if (getSdrModelFromSdrObject().IsUndoEnabled() && !mpImpl->maUndos.empty()) + { + // These actions should be on the undo stack after text edit. + for (std::unique_ptr<SdrUndoAction>& pAction : mpImpl->maUndos) + getSdrModelFromSdrObject().AddUndo( std::move(pAction)); + mpImpl->maUndos.clear(); + + getSdrModelFromSdrObject().AddUndo(getSdrModelFromSdrObject().GetSdrUndoFactory().CreateUndoGeoObject(*this)); + } + + if(rOutl.IsModified()) + { + std::optional<OutlinerParaObject> pNewText; + Paragraph* p1stPara = rOutl.GetParagraph( 0 ); + sal_Int32 nParaCnt = rOutl.GetParagraphCount(); + + if(p1stPara) + { + // to remove the grey field background + rOutl.UpdateFields(); + + // create new text object + pNewText = rOutl.CreateParaObject( 0, nParaCnt ); + } + SetOutlinerParaObject(std::move(pNewText)); + } + + mpEditingOutliner = nullptr; + rOutl.Clear(); + EEControlBits nStat = rOutl.GetControlWord(); + nStat &= ~EEControlBits::AUTOPAGESIZE; + rOutl.SetControlWord(nStat); + + mbInEditMode = false; +} + + +OutlinerParaObject* SdrTableObj::GetOutlinerParaObject() const +{ + CellRef xCell( getActiveCell() ); + if( xCell.is() ) + return xCell->GetOutlinerParaObject(); + else + return nullptr; +} + + +void SdrTableObj::NbcSetOutlinerParaObject( std::optional<OutlinerParaObject> pTextObject) +{ + CellRef xCell( getActiveCell() ); + if( !xCell.is() ) + return; + + // Update HitTestOutliner + const SdrTextObj* pTestObj(getSdrModelFromSdrObject().GetHitTestOutliner().GetTextObj()); + + if(pTestObj && pTestObj->GetOutlinerParaObject() == xCell->GetOutlinerParaObject()) + { + getSdrModelFromSdrObject().GetHitTestOutliner().SetTextObj(nullptr); + } + + xCell->SetOutlinerParaObject( std::move(pTextObject) ); + SetTextSizeDirty(); + NbcAdjustTextFrameWidthAndHeight(); +} + + +void SdrTableObj::NbcSetLogicRect(const tools::Rectangle& rRect) +{ + maLogicRect=rRect; + ImpJustifyRect(maLogicRect); + const bool bWidth = maLogicRect.getOpenWidth() != getRectangle().getOpenWidth(); + const bool bHeight = maLogicRect.getOpenHeight() != getRectangle().getOpenHeight(); + setRectangle(maLogicRect); + if (mpImpl->mbSkipChangeLayout) + // Avoid distributing newly available space between existing cells. + NbcAdjustTextFrameWidthAndHeight(); + else + NbcAdjustTextFrameWidthAndHeight(!bHeight, !bWidth); + SetBoundAndSnapRectsDirty(); +} + + +void SdrTableObj::AdjustToMaxRect( const tools::Rectangle& rMaxRect, bool /* bShrinkOnly = false */ ) +{ + tools::Rectangle aAdjustRect( rMaxRect ); + aAdjustRect.setHeight( GetLogicRect().getOpenHeight() ); + SetLogicRect( aAdjustRect ); +} + + +void SdrTableObj::NbcMove(const Size& rSiz) +{ + maLogicRect.Move(rSiz); + SdrTextObj::NbcMove( rSiz ); + if( mpImpl.is() ) + mpImpl->UpdateCells(getRectangle()); +} + + +void SdrTableObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact) +{ + tools::Rectangle aOldRect( maLogicRect ); + ResizeRect(maLogicRect,rRef,xFact,yFact); + + setRectangle(maLogicRect); + NbcAdjustTextFrameWidthAndHeight( maLogicRect.GetHeight() == aOldRect.GetHeight(), maLogicRect.GetWidth() == aOldRect.GetWidth() ); + SetBoundAndSnapRectsDirty(); +} + + +bool SdrTableObj::AdjustTextFrameWidthAndHeight() +{ + tools::Rectangle aNewRect(maLogicRect); + bool bRet=AdjustTextFrameWidthAndHeight(aNewRect); + if (bRet) + { + tools::Rectangle aBoundRect0; + if (m_pUserCall!=nullptr) + aBoundRect0=GetLastBoundRect(); + setRectangle(aNewRect); + SetBoundAndSnapRectsDirty(); + SetChanged(); + BroadcastObjectChange(); + SendUserCall(SdrUserCallType::Resize,aBoundRect0); + } + return bRet; +} + + +bool SdrTableObj::AdjustTextFrameWidthAndHeight(tools::Rectangle& rR, bool bHeight, bool bWidth) const +{ + if(rR.IsEmpty() || !mpImpl.is() || !mpImpl->mxTable.is()) + return false; + + tools::Rectangle aRectangle( rR ); + mpImpl->LayoutTable( aRectangle, !bWidth, !bHeight ); + + if( aRectangle != rR ) + { + rR = aRectangle; + return true; + } + else + { + return false; + } +} + + +void SdrTableObj::NbcReformatText() +{ + NbcAdjustTextFrameWidthAndHeight(); +} + + +bool SdrTableObj::IsVerticalWriting() const +{ + const SvxWritingModeItem& rModeItem = GetObjectItem( SDRATTR_TEXTDIRECTION ); + return rModeItem.GetValue() == css::text::WritingMode_TB_RL; +} + + +void SdrTableObj::SetVerticalWriting(bool bVertical) +{ + if(bVertical != IsVerticalWriting() ) + { + SvxWritingModeItem aModeItem( css::text::WritingMode_LR_TB, SDRATTR_TEXTDIRECTION ); + SetObjectItem( aModeItem ); + } +} + + +WritingMode SdrTableObj::GetWritingMode() const +{ + SfxStyleSheet* pStyle = GetStyleSheet(); + if ( !pStyle ) + return WritingMode_LR_TB; + + WritingMode eWritingMode = WritingMode_LR_TB; + const SfxItemSet &rSet = pStyle->GetItemSet(); + + if ( const SvxWritingModeItem *pItem = rSet.GetItemIfSet( SDRATTR_TEXTDIRECTION )) + eWritingMode = pItem->GetValue(); + + if ( const SvxFrameDirectionItem *pItem; + ( eWritingMode != WritingMode_TB_RL ) && + ( pItem = rSet.GetItemIfSet( EE_PARA_WRITINGDIR, false ) ) ) + { + if ( pItem->GetValue() == SvxFrameDirection::Horizontal_LR_TB ) + eWritingMode = WritingMode_LR_TB; + else + eWritingMode = WritingMode_RL_TB; + } + + return eWritingMode; +} + +void SdrTableObj::AddUndo(SdrUndoAction* pUndo) +{ + mpImpl->maUndos.push_back(std::unique_ptr<SdrUndoAction>(pUndo)); +} + +void SdrTableObj::SetSkipChangeLayout(bool bSkipChangeLayout) +{ + mpImpl->mbSkipChangeLayout = bSkipChangeLayout; +} + +bool SdrTableObj::IsReallyEdited() const +{ + return mpEditingOutliner && mpEditingOutliner->IsModified(); +} + +bool SdrTableObj::IsFontwork() const +{ + return false; +} + +sal_uInt32 SdrTableObj::GetHdlCount() const +{ + sal_uInt32 nCount = SdrTextObj::GetHdlCount(); + const sal_Int32 nRowCount = mpImpl->getRowCount(); + const sal_Int32 nColCount = mpImpl->getColumnCount(); + + if( nRowCount && nColCount ) + nCount += nRowCount + nColCount + 2 + 1; + + return nCount; +} + +void SdrTableObj::AddToHdlList(SdrHdlList& rHdlList) const +{ + const sal_Int32 nRowCount = mpImpl->getRowCount(); + const sal_Int32 nColCount = mpImpl->getColumnCount(); + + // first add row handles + std::vector<TableEdgeHdl*> aRowEdges(nRowCount + 1); + for (auto const & rEdge : mpImpl->mpLayouter->getHorizontalEdges()) + { + Point aPoint(getRectangle().TopLeft()); + aPoint.AdjustY(rEdge.nPosition); + + std::unique_ptr<TableEdgeHdl> pHdl(new TableEdgeHdl(aPoint, true, rEdge.nMin, rEdge.nMax, nColCount + 1)); + pHdl->SetPointNum(rEdge.nIndex); + aRowEdges[rEdge.nIndex] = pHdl.get(); + rHdlList.AddHdl(std::move(pHdl)); + } + + // second add column handles + std::vector<TableEdgeHdl*> aColEdges(nColCount + 1); + for (auto const & rEdge : mpImpl->mpLayouter->getVerticalEdges()) + { + Point aPoint(getRectangle().TopLeft()); + aPoint.AdjustX(rEdge.nPosition); + + std::unique_ptr<TableEdgeHdl> pHdl(new TableEdgeHdl(aPoint, false, rEdge.nMin, rEdge.nMax, nRowCount + 1)); + pHdl->SetPointNum(rEdge.nIndex); + aColEdges[rEdge.nIndex] = pHdl.get(); + rHdlList.AddHdl(std::move(pHdl)); + } + + // now add visible edges to row and column handles + if( mpImpl->mpLayouter ) + { + TableLayouter& rLayouter = *mpImpl->mpLayouter; + + sal_Int32 nY = 0; + + for( sal_Int32 nRow = 0; nRow <= nRowCount; ++nRow ) + { + const sal_Int32 nRowHeight = (nRow == nRowCount) ? 0 : rLayouter.getRowHeight(nRow); + sal_Int32 nX = 0; + + for( sal_Int32 nCol = 0; nCol <= nColCount; ++nCol ) + { + const sal_Int32 nColWidth = (nCol == nColCount) ? 0 : rLayouter.getColumnWidth(nCol); + + if( nRowHeight > 0 ) + { + if( rLayouter.isEdgeVisible( nCol, nRow, false ) ) + aColEdges[nCol]->SetEdge( nRow, nY, nY + nRowHeight, (rLayouter.getBorderLine( nCol, nRow, false ) == nullptr) ? Visible : Invisible); + } + + if( nColWidth > 0 ) + { + if( rLayouter.isEdgeVisible( nCol, nRow, true ) ) + aRowEdges[nRow]->SetEdge( nCol, nX, nX + nColWidth, (rLayouter.getBorderLine( nCol, nRow, true ) == nullptr) ? Visible : Invisible); + } + + nX += nColWidth; + } + + nY += nRowHeight; + } + } + + // add remaining handles + SdrHdlList tempList(nullptr); + auto aRectangle = getRectangle(); + tempList.AddHdl( std::make_unique<TableBorderHdl>(aRectangle, !IsTextEditActive() ) ); + tempList.AddHdl( std::make_unique<SdrHdl>(aRectangle.TopLeft(),SdrHdlKind::UpperLeft) ); + tempList.AddHdl( std::make_unique<SdrHdl>(aRectangle.TopCenter(),SdrHdlKind::Upper) ); + tempList.AddHdl( std::make_unique<SdrHdl>(aRectangle.TopRight(),SdrHdlKind::UpperRight) ); + tempList.AddHdl( std::make_unique<SdrHdl>(aRectangle.LeftCenter(),SdrHdlKind::Left) ); + tempList.AddHdl( std::make_unique<SdrHdl>(aRectangle.RightCenter(),SdrHdlKind::Right) ); + tempList.AddHdl( std::make_unique<SdrHdl>(aRectangle.BottomLeft(),SdrHdlKind::LowerLeft) ); + tempList.AddHdl( std::make_unique<SdrHdl>(aRectangle.BottomCenter(),SdrHdlKind::Lower) ); + tempList.AddHdl( std::make_unique<SdrHdl>(aRectangle.BottomRight(),SdrHdlKind::LowerRight) ); + for( size_t nHdl = 0; nHdl < tempList.GetHdlCount(); ++nHdl ) + tempList.GetHdl(nHdl)->SetMoveOutside(true); + tempList.MoveTo(rHdlList); + + const size_t nHdlCount = rHdlList.GetHdlCount(); + for( size_t nHdl = 0; nHdl < nHdlCount; ++nHdl ) + rHdlList.GetHdl(nHdl)->SetObj(const_cast<SdrTableObj*>(this)); +} + +// Dragging + +bool SdrTableObj::hasSpecialDrag() const +{ + return true; +} + +bool SdrTableObj::beginSpecialDrag(SdrDragStat& rDrag) const +{ + const SdrHdl* pHdl = rDrag.GetHdl(); + const SdrHdlKind eHdl((pHdl == nullptr) ? SdrHdlKind::Move : pHdl->GetKind()); + + switch( eHdl ) + { + case SdrHdlKind::UpperLeft: + case SdrHdlKind::Upper: + case SdrHdlKind::UpperRight: + case SdrHdlKind::Left: + case SdrHdlKind::Right: + case SdrHdlKind::LowerLeft: + case SdrHdlKind::Lower: + case SdrHdlKind::LowerRight: + case SdrHdlKind::Move: + { + break; + } + + case SdrHdlKind::User: + { + rDrag.SetEndDragChangesAttributes(false); + rDrag.SetNoSnap(); + break; + } + + default: + { + return false; + } + } + + return true; +} + +bool SdrTableObj::applySpecialDrag(SdrDragStat& rDrag) +{ + bool bRet(true); + const SdrHdl* pHdl = rDrag.GetHdl(); + const SdrHdlKind eHdl((pHdl == nullptr) ? SdrHdlKind::Move : pHdl->GetKind()); + + switch( eHdl ) + { + case SdrHdlKind::UpperLeft: + case SdrHdlKind::Upper: + case SdrHdlKind::UpperRight: + case SdrHdlKind::Left: + case SdrHdlKind::Right: + case SdrHdlKind::LowerLeft: + case SdrHdlKind::Lower: + case SdrHdlKind::LowerRight: + { + const tools::Rectangle aNewRectangle(ImpDragCalcRect(rDrag)); + + if (aNewRectangle != getRectangle()) + { + NbcSetLogicRect(aNewRectangle); + } + + break; + } + + case SdrHdlKind::Move: + { + NbcMove( Size( rDrag.GetDX(), rDrag.GetDY() ) ); + break; + } + + case SdrHdlKind::User: + { + rDrag.SetEndDragChangesAttributes(false); + rDrag.SetNoSnap(); + const TableEdgeHdl* pEdgeHdl = dynamic_cast< const TableEdgeHdl* >( pHdl ); + + if( pEdgeHdl ) + { + if( IsInserted() ) + { + rDrag.SetEndDragChangesAttributes(true); + rDrag.SetEndDragChangesLayout(true); + } + + mpImpl->DragEdge( pEdgeHdl->IsHorizontalEdge(), pEdgeHdl->GetPointNum(), pEdgeHdl->GetValidDragOffset( rDrag ) ); + } + break; + } + + default: + { + bRet = false; + } + } + + return bRet; +} + +basegfx::B2DPolyPolygon SdrTableObj::getSpecialDragPoly(const SdrDragStat& rDrag) const +{ + basegfx::B2DPolyPolygon aRetval; + const SdrHdl* pHdl = rDrag.GetHdl(); + + if( pHdl && (SdrHdlKind::User == pHdl->GetKind()) ) + { + const TableEdgeHdl* pEdgeHdl = dynamic_cast< const TableEdgeHdl* >( pHdl ); + + if( pEdgeHdl ) + { + aRetval = pEdgeHdl->getSpecialDragPoly( rDrag ); + } + } + + return aRetval; +} + + +// Create + + +bool SdrTableObj::BegCreate(SdrDragStat& rStat) +{ + rStat.SetOrtho4Possible(); + tools::Rectangle aRect1(rStat.GetStart(), rStat.GetNow()); + aRect1.Normalize(); + rStat.SetActionRect(aRect1); + setRectangle(aRect1); + return true; +} + + +bool SdrTableObj::MovCreate(SdrDragStat& rStat) +{ + tools::Rectangle aRect1; + rStat.TakeCreateRect(aRect1); + ImpJustifyRect(aRect1); + rStat.SetActionRect(aRect1); + setRectangle(aRect1); // for ObjName + SetBoundRectDirty(); + m_bSnapRectDirty=true; + return true; +} + + +bool SdrTableObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd) +{ + auto aRectangle = getRectangle(); + rStat.TakeCreateRect(aRectangle); + ImpJustifyRect(aRectangle); + setRectangle(aRectangle); + return (eCmd==SdrCreateCmd::ForceEnd || rStat.GetPointCount()>=2); +} + +void SdrTableObj::BrkCreate(SdrDragStat& /*rStat*/) +{ +} + + +bool SdrTableObj::BckCreate(SdrDragStat& /*rStat*/) +{ + return true; +} + + +basegfx::B2DPolyPolygon SdrTableObj::TakeCreatePoly(const SdrDragStat& rDrag) const +{ + tools::Rectangle aRect1; + rDrag.TakeCreateRect(aRect1); + aRect1.Normalize(); + + basegfx::B2DPolyPolygon aRetval; + const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aRect1); + aRetval.append(basegfx::utils::createPolygonFromRect(aRange)); + return aRetval; +} + + +PointerStyle SdrTableObj::GetCreatePointer() const +{ + return PointerStyle::Cross; +} + + +void SdrTableObj::createCell( CellRef& xNewCell ) +{ + xNewCell = Cell::create( *this ); +} + + +std::unique_ptr<SdrObjGeoData> SdrTableObj::NewGeoData() const +{ + return std::make_unique<TableObjectGeoData>(); +} + + +void SdrTableObj::SaveGeoData(SdrObjGeoData& rGeo) const +{ + DBG_ASSERT( dynamic_cast< TableObjectGeoData* >( &rGeo ), "svx::SdrTableObj::SaveGeoData(), illegal geo data!" ); + SdrTextObj::SaveGeoData (rGeo); + + static_cast<TableObjectGeoData &>(rGeo).maLogicRect = maLogicRect; +} + + +void SdrTableObj::RestoreGeoData(const SdrObjGeoData& rGeo) +{ + DBG_ASSERT( dynamic_cast< const TableObjectGeoData* >( &rGeo ), "svx::SdrTableObj::RestoreGeoData(), illegal geo data!" ); + + maLogicRect = static_cast<const TableObjectGeoData &>(rGeo).maLogicRect; + + SdrTextObj::RestoreGeoData (rGeo); + + if( mpImpl.is() ) + { + auto aRectangle = getRectangle(); + mpImpl->LayoutTable(aRectangle, false, false); + setRectangle(aRectangle); + } + ActionChanged(); +} + +void SdrTableObj::CropTableModelToSelection(const CellPos& rStart, const CellPos& rEnd) +{ + if(!mpImpl.is()) + { + return; + } + + mpImpl->CropTableModelToSelection(rStart, rEnd); +} + +void SdrTableObj::LayoutTableHeight(tools::Rectangle& rArea) +{ + if( mpImpl.is() && mpImpl->mpLayouter) + { + mpImpl->mpLayouter->LayoutTableHeight(rArea, /*bFit*/false); + } +} + +void SdrTableObj::DistributeColumns( sal_Int32 nFirstColumn, sal_Int32 nLastColumn, const bool bOptimize, const bool bMinimize ) +{ + if( mpImpl.is() && mpImpl->mpLayouter ) + { + TableModelNotifyGuard aGuard( mpImpl->mxTable.get() ); + auto aRectangle = getRectangle(); + mpImpl->mpLayouter->DistributeColumns(aRectangle, nFirstColumn, nLastColumn, bOptimize, bMinimize); + setRectangle(aRectangle); + } +} + + +void SdrTableObj::DistributeRows( sal_Int32 nFirstRow, sal_Int32 nLastRow, const bool bOptimize, const bool bMinimize ) +{ + if( mpImpl.is() && mpImpl->mpLayouter ) + { + TableModelNotifyGuard aGuard( mpImpl->mxTable.get() ); + auto aRectangle = getRectangle(); + mpImpl->mpLayouter->DistributeRows(aRectangle, nFirstRow, nLastRow, bOptimize, bMinimize); + setRectangle(aRectangle); + } +} + + +void SdrTableObj::SetChanged() +{ + if( mpImpl.is() ) + { + auto aRectangle = getRectangle(); + mpImpl->LayoutTable(aRectangle, false, false); + setRectangle(aRectangle); + } + + ::SdrTextObj::SetChanged(); +} + + +void SdrTableObj::uno_lock() +{ + if( mpImpl.is() && mpImpl->mxTable.is() ) + mpImpl->mxTable->lockBroadcasts(); +} + + +void SdrTableObj::uno_unlock() +{ + if( mpImpl.is() && mpImpl->mxTable.is() ) + mpImpl->mxTable->unlockBroadcasts(); +} + +void SdrTableObj::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrTableObj")); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + + SdrObject::dumpAsXml(pWriter); + + mpImpl->dumpAsXml(pWriter); + + (void)xmlTextWriterEndElement(pWriter); +} + +bool SdrTableObj::createTableEdgesJson(boost::property_tree::ptree & rJsonRoot) +{ + if (!mpImpl.is() || !mpImpl->mxTable.is()) + return false; + + tools::Rectangle aRect = GetCurrentBoundRect(); + boost::property_tree::ptree aTableColumns; + { + aTableColumns.put("tableOffset", o3tl::toTwips(aRect.Left(), o3tl::Length::mm100)); + + boost::property_tree::ptree aEntries; + auto const & aEdges = mpImpl->mpLayouter->getVerticalEdges(); + for (auto & rEdge : aEdges) + { + if (rEdge.nIndex == 0) + { + aTableColumns.put("left", o3tl::toTwips(rEdge.nPosition, o3tl::Length::mm100)); + } + else if (rEdge.nIndex == sal_Int32(aEdges.size() - 1)) + { + aTableColumns.put("right", o3tl::toTwips(rEdge.nPosition, o3tl::Length::mm100)); + } + else + { + boost::property_tree::ptree aEntry; + aEntry.put("position", o3tl::toTwips(rEdge.nPosition, o3tl::Length::mm100)); + aEntry.put("min", o3tl::toTwips(rEdge.nPosition + rEdge.nMin, o3tl::Length::mm100)); + aEntry.put("max", o3tl::toTwips(rEdge.nPosition + rEdge.nMax, o3tl::Length::mm100)); + aEntry.put("hidden", false); + aEntries.push_back(std::make_pair("", aEntry)); + } + } + aTableColumns.push_back(std::make_pair("entries", aEntries)); + } + rJsonRoot.add_child("columns", aTableColumns); + + boost::property_tree::ptree aTableRows; + { + aTableRows.put("tableOffset", o3tl::toTwips(aRect.Top(), o3tl::Length::mm100)); + + boost::property_tree::ptree aEntries; + auto const & aEdges = mpImpl->mpLayouter->getHorizontalEdges(); + for (auto & rEdge : aEdges) + { + if (rEdge.nIndex == 0) + { + aTableRows.put("left", o3tl::toTwips(rEdge.nPosition, o3tl::Length::mm100)); + } + else if (rEdge.nIndex == sal_Int32(aEdges.size() - 1)) + { + aTableRows.put("right", o3tl::toTwips(rEdge.nPosition, o3tl::Length::mm100)); + } + else + { + boost::property_tree::ptree aEntry; + aEntry.put("position", o3tl::toTwips(rEdge.nPosition, o3tl::Length::mm100)); + aEntry.put("min", o3tl::toTwips(rEdge.nPosition + rEdge.nMin, o3tl::Length::mm100)); + aEntry.put("max", o3tl::toTwips(rEdge.nPosition + rEdge.nMax, o3tl::Length::mm100)); + aEntry.put("hidden", false); + aEntries.push_back(std::make_pair("", aEntry)); + } + } + aTableRows.push_back(std::make_pair("entries", aEntries)); + } + rJsonRoot.add_child("rows", aTableRows); + return true; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |