/* -*- 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 <comphelper/configuration.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) ov
erride;
};
}
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( mxTable->getCell( nCol, nRow ) );
if ( xTargetCell.is() )
xTargetCell->cloneFrom( xOldTable->getCell( rStart.mnCol + nCol, rStart.mnRow + nRow ) );
}
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( u"table" _ustr ), 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 = std::move(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(u"table" _ustr), UNO_QUERY_THROW);
xDefaultStyle.set(xTableFamily->getByName(u"default" _ustr), 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 = mxTable->getCell( rPos.mnCol, rPos.mnRow );
}
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 (comphelper::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;
}
const rtl::Reference< TableModel > & SdrTableObj::getUnoTable() 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 = mpImpl->mxTable->getCell( rPos.mnCol, rPos.mnRow );
if ( mpImpl->mxActiveCell.is() && mpImpl->mxActiveCell->isMerged() )
{
CellPos aOrigin;
findMergeOrigin( mpImpl->mxTable, rPos.mnCol, rPos.mnRow, aOrigin.mnCol, aOrigin.mnRow );
mpImpl->mxActiveCell = mpImpl->mxTable->getCell( aOrigin.mnCol, aOrigin.mnRow );
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, bool bAdjustTextFrameWidthAndHeight )
{
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();
if (bAdjustTextFrameWidthAndHeight)
NbcAdjustTextFrameWidthAndHeight();
}
void SdrTableObj::NbcSetLogicRect(const tools::Rectangle& rRect, bool /*bAdaptTextMinSize*/)
{
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);
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=96 H=96 G=95
¤ Dauer der Verarbeitung: 0.27 Sekunden
¤
*© Formatika GbR, Deutschland