summaryrefslogtreecommitdiffstats
path: root/svx/source/table
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--svx/source/table/accessiblecell.cxx600
-rw-r--r--svx/source/table/accessiblecell.hxx132
-rw-r--r--svx/source/table/accessibletableshape.cxx1324
-rw-r--r--svx/source/table/cell.cxx1712
-rw-r--r--svx/source/table/cellcursor.cxx546
-rw-r--r--svx/source/table/cellcursor.hxx71
-rw-r--r--svx/source/table/cellrange.cxx116
-rw-r--r--svx/source/table/cellrange.hxx61
-rw-r--r--svx/source/table/propertyset.cxx206
-rw-r--r--svx/source/table/propertyset.hxx96
-rw-r--r--svx/source/table/svdotable.cxx2534
-rw-r--r--svx/source/table/tablecolumn.cxx289
-rw-r--r--svx/source/table/tablecolumn.hxx83
-rw-r--r--svx/source/table/tablecolumns.cxx121
-rw-r--r--svx/source/table/tablecolumns.hxx61
-rw-r--r--svx/source/table/tablecontroller.cxx3363
-rw-r--r--svx/source/table/tabledesign.cxx705
-rw-r--r--svx/source/table/tablehandles.cxx313
-rw-r--r--svx/source/table/tablehandles.hxx89
-rw-r--r--svx/source/table/tablelayouter.cxx1311
-rw-r--r--svx/source/table/tablelayouter.hxx172
-rw-r--r--svx/source/table/tablemodel.cxx1127
-rw-r--r--svx/source/table/tablerow.cxx351
-rw-r--r--svx/source/table/tablerow.hxx84
-rw-r--r--svx/source/table/tablerows.cxx114
-rw-r--r--svx/source/table/tablerows.hxx59
-rw-r--r--svx/source/table/tablertfexporter.cxx235
-rw-r--r--svx/source/table/tablertfimporter.cxx503
-rw-r--r--svx/source/table/tableundo.cxx516
-rw-r--r--svx/source/table/tableundo.hxx258
-rw-r--r--svx/source/table/viewcontactoftableobj.cxx547
-rw-r--r--svx/source/table/viewcontactoftableobj.hxx45
32 files changed, 17744 insertions, 0 deletions
diff --git a/svx/source/table/accessiblecell.cxx b/svx/source/table/accessiblecell.cxx
new file mode 100644
index 000000000..1052ab5eb
--- /dev/null
+++ b/svx/source/table/accessiblecell.cxx
@@ -0,0 +1,600 @@
+/* -*- 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 <sal/config.h>
+#include <sal/log.hxx>
+
+#include <memory>
+
+#include "accessiblecell.hxx"
+#include <cell.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+
+#include <editeng/unoedsrc.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+
+#include <unotools/accessiblestatesethelper.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/sequence.hxx>
+#include <svx/IAccessibleViewForwarder.hxx>
+#include <svx/unoshtxt.hxx>
+#include <svx/svdotext.hxx>
+#include <tools/debug.hxx>
+
+using namespace sdr::table;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+
+namespace accessibility {
+
+AccessibleCell::AccessibleCell( const css::uno::Reference< css::accessibility::XAccessible>& rxParent, const sdr::table::CellRef& rCell, sal_Int32 nIndex, const AccessibleShapeTreeInfo& rShapeTreeInfo )
+: AccessibleCellBase( rxParent, AccessibleRole::TABLE_CELL )
+, maShapeTreeInfo( rShapeTreeInfo )
+, mnIndexInParent( nIndex )
+, mxCell( rCell )
+{
+ //Init the pAccTable var
+ pAccTable = dynamic_cast <AccessibleTableShape *> (rxParent.get());
+}
+
+
+AccessibleCell::~AccessibleCell()
+{
+ DBG_ASSERT( mpText == nullptr, "svx::AccessibleCell::~AccessibleCell(), not disposed!?" );
+}
+
+
+void AccessibleCell::Init()
+{
+ SdrView* pView = maShapeTreeInfo.GetSdrView();
+ const vcl::Window* pWindow = maShapeTreeInfo.GetWindow ();
+ if( !((pView != nullptr) && (pWindow != nullptr) && mxCell.is()))
+ return;
+
+ // create AccessibleTextHelper to handle this shape's text
+ if( mxCell->CanCreateEditOutlinerParaObject() || mxCell->GetOutlinerParaObject() != nullptr )
+ {
+ // non-empty text -> use full-fledged edit source right away
+
+ mpText.reset( new AccessibleTextHelper( std::make_unique<SvxTextEditSource>(mxCell->GetObject(), mxCell.get(), *pView, *pWindow->GetOutDev()) ) );
+ if( mxCell.is() && mxCell->IsActiveCell() )
+ mpText->SetFocus();
+ mpText->SetEventSource(this);
+ }
+}
+
+
+bool AccessibleCell::SetState (sal_Int16 aState)
+{
+ bool bStateHasChanged = false;
+
+ if (aState == AccessibleStateType::FOCUSED && mpText != nullptr)
+ {
+ // Offer FOCUSED state to edit engine and detect whether the state
+ // changes.
+ bool bIsFocused = mpText->HaveFocus ();
+ mpText->SetFocus();
+ bStateHasChanged = (bIsFocused != mpText->HaveFocus ());
+ }
+ else
+ bStateHasChanged = AccessibleContextBase::SetState (aState);
+
+ return bStateHasChanged;
+}
+
+
+bool AccessibleCell::ResetState (sal_Int16 aState)
+{
+ bool bStateHasChanged = false;
+
+ if (aState == AccessibleStateType::FOCUSED && mpText != nullptr)
+ {
+ // Try to remove FOCUSED state from the edit engine and detect
+ // whether the state changes.
+ bool bIsFocused = mpText->HaveFocus ();
+ mpText->SetFocus (false);
+ bStateHasChanged = (bIsFocused != mpText->HaveFocus ());
+ }
+ else
+ bStateHasChanged = AccessibleContextBase::ResetState (aState);
+
+ return bStateHasChanged;
+}
+
+
+// XInterface
+
+
+Any SAL_CALL AccessibleCell::queryInterface( const Type& aType )
+{
+ return AccessibleCellBase::queryInterface( aType );
+}
+
+
+void SAL_CALL AccessibleCell::acquire( ) noexcept
+{
+ AccessibleCellBase::acquire();
+}
+
+
+void SAL_CALL AccessibleCell::release( ) noexcept
+{
+ AccessibleCellBase::release();
+}
+
+
+// XAccessibleContext
+
+
+/** The children of this cell come from the paragraphs of text.
+*/
+sal_Int32 SAL_CALL AccessibleCell::getAccessibleChildCount()
+{
+ SolarMutexGuard aSolarGuard;
+ ThrowIfDisposed ();
+ return mpText != nullptr ? mpText->GetChildCount () : 0;
+}
+
+
+/** Forward the request to the shape. Return the requested shape or throw
+ an exception for a wrong index.
+*/
+Reference<XAccessible> SAL_CALL AccessibleCell::getAccessibleChild (sal_Int32 nIndex)
+{
+ SolarMutexGuard aSolarGuard;
+ ThrowIfDisposed ();
+
+ // todo: does GetChild throw IndexOutOfBoundsException?
+ return mpText->GetChild (nIndex);
+}
+
+
+/** Return a copy of the state set.
+ Possible states are:
+ ENABLED
+ SHOWING
+ VISIBLE
+*/
+Reference<XAccessibleStateSet> SAL_CALL AccessibleCell::getAccessibleStateSet()
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard (m_aMutex);
+ Reference<XAccessibleStateSet> xStateSet;
+
+ if (rBHelper.bDisposed || mpText == nullptr)
+ {
+ // Return a minimal state set that only contains the DEFUNC state.
+ xStateSet = AccessibleContextBase::getAccessibleStateSet ();
+ }
+ else
+ {
+ if(mxStateSet)
+ {
+ // Merge current FOCUSED state from edit engine.
+ if (mpText != nullptr)
+ {
+ if (mpText->HaveFocus())
+ mxStateSet->AddState (AccessibleStateType::FOCUSED);
+ else
+ mxStateSet->RemoveState (AccessibleStateType::FOCUSED);
+ }
+ // Set the invisible state for merged cell
+ if (mxCell.is() && mxCell->isMerged())
+ mxStateSet->RemoveState(AccessibleStateType::VISIBLE);
+ else
+ mxStateSet->AddState(AccessibleStateType::VISIBLE);
+
+
+ //Just when the parent table is not read-only,set states EDITABLE,RESIZABLE,MOVEABLE
+ css::uno::Reference<XAccessible> xTempAcc = getAccessibleParent();
+ if( xTempAcc.is() )
+ {
+ css::uno::Reference<XAccessibleContext>
+ xTempAccContext = xTempAcc->getAccessibleContext();
+ if( xTempAccContext.is() )
+ {
+ css::uno::Reference<XAccessibleStateSet> rState =
+ xTempAccContext->getAccessibleStateSet();
+ if( rState.is() )
+ {
+ const css::uno::Sequence<short> aStates = rState->getStates();
+ if (std::find(aStates.begin(), aStates.end(), AccessibleStateType::EDITABLE) != aStates.end())
+ {
+ mxStateSet->AddState (AccessibleStateType::EDITABLE);
+ mxStateSet->AddState (AccessibleStateType::RESIZABLE);
+ mxStateSet->AddState (AccessibleStateType::MOVEABLE);
+ }
+ }
+ }
+ }
+ // Create a copy of the state set that may be modified by the
+ // caller without affecting the current state set.
+ xStateSet.set(new ::utl::AccessibleStateSetHelper (*mxStateSet));
+ }
+ }
+
+ return xStateSet;
+}
+
+
+// XAccessibleComponent
+
+
+sal_Bool SAL_CALL AccessibleCell::containsPoint( const css::awt::Point& aPoint)
+{
+ return AccessibleComponentBase::containsPoint( aPoint );
+}
+
+/** The implementation below is at the moment straightforward. It iterates
+ over all children (and thereby instances all children which have not
+ been already instantiated) until a child covering the specified point is
+ found.
+ This leaves room for improvement. For instance, first iterate only over
+ the already instantiated children and only if no match is found
+ instantiate the remaining ones.
+*/
+Reference<XAccessible > SAL_CALL AccessibleCell::getAccessibleAtPoint ( const css::awt::Point& aPoint)
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ sal_Int32 nChildCount = getAccessibleChildCount ();
+ for (sal_Int32 i=0; i<nChildCount; ++i)
+ {
+ Reference<XAccessible> xChild (getAccessibleChild (i));
+ if (xChild.is())
+ {
+ Reference<XAccessibleComponent> xChildComponent (xChild->getAccessibleContext(), uno::UNO_QUERY);
+ if (xChildComponent.is())
+ {
+ awt::Rectangle aBBox (xChildComponent->getBounds());
+ if ( (aPoint.X >= aBBox.X)
+ && (aPoint.Y >= aBBox.Y)
+ && (aPoint.X < aBBox.X+aBBox.Width)
+ && (aPoint.Y < aBBox.Y+aBBox.Height) )
+ return xChild;
+ }
+ }
+ }
+
+ // Have not found a child under the given point. Returning empty
+ // reference to indicate this.
+ return uno::Reference<XAccessible>();
+}
+
+
+css::awt::Rectangle SAL_CALL AccessibleCell::getBounds()
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ ThrowIfDisposed ();
+ css::awt::Rectangle aBoundingBox;
+ if( mxCell.is() )
+ {
+ // Get the cell's bounding box in internal coordinates (in 100th of mm)
+ const ::tools::Rectangle aCellRect( mxCell->getCellRect() );
+
+ // Transform coordinates from internal to pixel.
+ if (maShapeTreeInfo.GetViewForwarder() == nullptr)
+ throw uno::RuntimeException ("AccessibleCell has no valid view forwarder",static_cast<uno::XWeak*>(this));
+
+ ::Size aPixelSize( maShapeTreeInfo.GetViewForwarder()->LogicToPixel(::Size(aCellRect.GetWidth(), aCellRect.GetHeight())) );
+ ::Point aPixelPosition( maShapeTreeInfo.GetViewForwarder()->LogicToPixel( aCellRect.TopLeft() ));
+
+ // Clip the shape's bounding box with the bounding box of its parent.
+ Reference<XAccessibleComponent> xParentComponent ( getAccessibleParent(), uno::UNO_QUERY);
+ if (xParentComponent.is())
+ {
+ // Make the coordinates relative to the parent.
+ awt::Point aParentLocation (xParentComponent->getLocationOnScreen());
+ int x = aPixelPosition.getX() - aParentLocation.X;
+ int y = aPixelPosition.getY() - aParentLocation.Y;
+
+ // Clip with parent (with coordinates relative to itself).
+ ::tools::Rectangle aBBox ( x, y, x + aPixelSize.getWidth(), y + aPixelSize.getHeight());
+ awt::Size aParentSize (xParentComponent->getSize());
+ ::tools::Rectangle aParentBBox (0,0, aParentSize.Width, aParentSize.Height);
+ aBBox = aBBox.GetIntersection (aParentBBox);
+ aBoundingBox = awt::Rectangle ( aBBox.Left(), aBBox.Top(), aBBox.getWidth(), aBBox.getHeight());
+ }
+ else
+ {
+ SAL_INFO("svx", "parent does not support component");
+ aBoundingBox = awt::Rectangle (aPixelPosition.getX(), aPixelPosition.getY(),aPixelSize.getWidth(), aPixelSize.getHeight());
+ }
+ }
+
+ return aBoundingBox;
+}
+
+
+css::awt::Point SAL_CALL AccessibleCell::getLocation()
+{
+ ThrowIfDisposed ();
+ css::awt::Rectangle aBoundingBox(getBounds());
+ return css::awt::Point(aBoundingBox.X, aBoundingBox.Y);
+}
+
+
+css::awt::Point SAL_CALL AccessibleCell::getLocationOnScreen()
+{
+ ThrowIfDisposed ();
+
+ // Get relative position...
+ css::awt::Point aLocation(getLocation ());
+
+ // ... and add absolute position of the parent.
+ Reference<XAccessibleComponent> xParentComponent( getAccessibleParent(), uno::UNO_QUERY);
+ if(xParentComponent.is())
+ {
+ css::awt::Point aParentLocation(xParentComponent->getLocationOnScreen());
+ aLocation.X += aParentLocation.X;
+ aLocation.Y += aParentLocation.Y;
+ }
+ else
+ {
+ SAL_WARN("svx", "parent does not support XAccessibleComponent");
+ }
+
+ return aLocation;
+}
+
+
+awt::Size SAL_CALL AccessibleCell::getSize()
+{
+ ThrowIfDisposed ();
+ awt::Rectangle aBoundingBox (getBounds());
+ return awt::Size (aBoundingBox.Width, aBoundingBox.Height);
+}
+
+
+void SAL_CALL AccessibleCell::grabFocus()
+{
+ AccessibleComponentBase::grabFocus();
+}
+
+
+sal_Int32 SAL_CALL AccessibleCell::getForeground()
+{
+ ThrowIfDisposed ();
+
+ // todo
+ return sal_Int32(0x0ffffffL);
+}
+
+
+sal_Int32 SAL_CALL AccessibleCell::getBackground()
+{
+ ThrowIfDisposed ();
+
+ // todo
+ return 0;
+}
+
+
+// XAccessibleExtendedComponent
+
+
+css::uno::Reference< css::awt::XFont > SAL_CALL AccessibleCell::getFont()
+{
+//todo
+ return AccessibleComponentBase::getFont();
+}
+
+
+OUString SAL_CALL AccessibleCell::getTitledBorderText()
+{
+ return AccessibleComponentBase::getTitledBorderText();
+}
+
+
+OUString SAL_CALL AccessibleCell::getToolTipText()
+{
+ return AccessibleComponentBase::getToolTipText();
+}
+
+
+// XAccessibleEventBroadcaster
+
+
+void SAL_CALL AccessibleCell::addAccessibleEventListener( const Reference<XAccessibleEventListener >& rxListener)
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard (m_aMutex);
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ Reference<XInterface> xSource( static_cast<XComponent *>(this) );
+ lang::EventObject aEventObj(xSource);
+ rxListener->disposing(aEventObj);
+ }
+ else
+ {
+ AccessibleContextBase::addAccessibleEventListener (rxListener);
+ if (mpText != nullptr)
+ mpText->AddEventListener (rxListener);
+ }
+}
+
+
+void SAL_CALL AccessibleCell::removeAccessibleEventListener( const Reference<XAccessibleEventListener >& rxListener)
+{
+ SolarMutexGuard aSolarGuard;
+ AccessibleContextBase::removeAccessibleEventListener(rxListener);
+ if (mpText != nullptr)
+ mpText->RemoveEventListener (rxListener);
+}
+
+
+// XServiceInfo
+
+
+OUString SAL_CALL AccessibleCell::getImplementationName()
+{
+ return "AccessibleCell";
+}
+
+
+Sequence<OUString> SAL_CALL AccessibleCell::getSupportedServiceNames()
+{
+ ThrowIfDisposed ();
+ const css::uno::Sequence<OUString> vals { "com.sun.star.drawing.AccessibleCell" };
+ return comphelper::concatSequences(AccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+
+// IAccessibleViewForwarderListener
+
+
+void AccessibleCell::ViewForwarderChanged()
+{
+ // Inform all listeners that the graphical representation (i.e. size
+ // and/or position) of the shape has changed.
+ CommitChange(AccessibleEventId::VISIBLE_DATA_CHANGED, Any(), Any());
+
+ // update our children that our screen position might have changed
+ if( mpText )
+ mpText->UpdateChildren();
+}
+
+
+// protected
+
+
+void AccessibleCell::disposing()
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ // Make sure to send an event that this object loses the focus in the
+ // case that it has the focus.
+ if (mxStateSet != nullptr)
+ mxStateSet->RemoveState(AccessibleStateType::FOCUSED);
+
+ if (mpText != nullptr)
+ {
+ mpText->Dispose();
+ mpText.reset();
+ }
+
+ // Cleanup. Remove references to objects to allow them to be
+ // destroyed.
+ mxCell.clear();
+ maShapeTreeInfo.dispose();
+
+ // Call base classes.
+ AccessibleContextBase::dispose ();
+}
+
+sal_Int32 SAL_CALL AccessibleCell::getAccessibleIndexInParent()
+{
+ ThrowIfDisposed ();
+ return mnIndexInParent;
+}
+
+
+OUString AccessibleCell::getCellName( sal_Int32 nCol, sal_Int32 nRow )
+{
+ OUStringBuffer aBuf;
+
+ if (nCol < 26*26)
+ {
+ if (nCol < 26)
+ aBuf.append( static_cast<sal_Unicode>( 'A' +
+ static_cast<sal_uInt16>(nCol)));
+ else
+ {
+ aBuf.append( static_cast<sal_Unicode>( 'A' +
+ (static_cast<sal_uInt16>(nCol) / 26) - 1));
+ aBuf.append( static_cast<sal_Unicode>( 'A' +
+ (static_cast<sal_uInt16>(nCol) % 26)));
+ }
+ }
+ else
+ {
+ OUStringBuffer aStr;
+ while (nCol >= 26)
+ {
+ sal_Int32 nC = nCol % 26;
+ aStr.append(static_cast<sal_Unicode>( 'A' +
+ static_cast<sal_uInt16>(nC)));
+ nCol = nCol - nC;
+ nCol = nCol / 26 - 1;
+ }
+ aStr.append(static_cast<sal_Unicode>( 'A' +
+ static_cast<sal_uInt16>(nCol)));
+ aBuf.append(comphelper::string::reverseString(aStr));
+ }
+ aBuf.append(nRow+1);
+ return aBuf.makeStringAndClear();
+}
+
+OUString SAL_CALL AccessibleCell::getAccessibleName()
+{
+ ThrowIfDisposed ();
+ SolarMutexGuard aSolarGuard;
+
+ if( pAccTable )
+ {
+ try
+ {
+ sal_Int32 nRow = 0, nCol = 0;
+ pAccTable->getColumnAndRow(mnIndexInParent, nCol, nRow);
+ return getCellName( nCol, nRow );
+ }
+ catch(const Exception&)
+ {
+ }
+ }
+
+ return AccessibleCellBase::getAccessibleName();
+}
+
+void AccessibleCell::UpdateChildren()
+{
+ if (mpText)
+ mpText->UpdateChildren();
+}
+
+/* MT: Above getAccessibleName was introduced with IA2 CWS, while below was introduce in 3.3 meanwhile. Check which one is correct
++If this is correct, we also don't need sdr::table::CellRef getCellRef(), UpdateChildren(), getCellName( sal_Int32 nCol, sal_Int32 nRow ) above
++
+
+OUString SAL_CALL AccessibleCell::getAccessibleName() throw (css::uno::RuntimeException)
+{
+ ThrowIfDisposed ();
+ SolarMutexGuard aSolarGuard;
+
+ if( mxCell.is() )
+ return mxCell->getName();
+
+ return AccessibleCellBase::getAccessibleName();
+}
+*/
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/accessiblecell.hxx b/svx/source/table/accessiblecell.hxx
new file mode 100644
index 000000000..65b9665ab
--- /dev/null
+++ b/svx/source/table/accessiblecell.hxx
@@ -0,0 +1,132 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_TABLE_ACCESSIBLECELL_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_ACCESSIBLECELL_HXX
+
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/XAccessibleExtendedComponent.hpp>
+
+#include <editeng/AccessibleContextBase.hxx>
+#include <editeng/AccessibleComponentBase.hxx>
+#include <svx/IAccessibleViewForwarderListener.hxx>
+#include <svx/AccessibleTextHelper.hxx>
+#include <svx/AccessibleShapeTreeInfo.hxx>
+#include <AccessibleTableShape.hxx>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <celltypes.hxx>
+
+
+namespace accessibility
+{
+
+class AccessibleShapeTreeInfo;
+
+typedef ::cppu::ImplInheritanceHelper< AccessibleContextBase, css::accessibility::XAccessibleExtendedComponent > AccessibleCellBase;
+
+class AccessibleCell : public AccessibleCellBase
+ , public AccessibleComponentBase
+ , public IAccessibleViewForwarderListener
+{
+public:
+ AccessibleCell( const css::uno::Reference< css::accessibility::XAccessible>& rxParent, const sdr::table::CellRef& rCell, sal_Int32 nIndex, const AccessibleShapeTreeInfo& rShapeTreeInfo);
+ virtual ~AccessibleCell() override;
+ AccessibleCell(const AccessibleCell&) = delete;
+ AccessibleCell& operator=(const AccessibleCell&) = delete;
+
+ void Init();
+
+ virtual bool SetState (sal_Int16 aState) override;
+ virtual bool ResetState (sal_Int16 aState) override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override;
+ virtual void SAL_CALL acquire( ) noexcept override;
+ virtual void SAL_CALL release( ) noexcept override;
+
+ // XAccessibleContext
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount() override;
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL getAccessibleChild(sal_Int32 nIndex) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleStateSet> SAL_CALL getAccessibleStateSet() override;
+ virtual sal_Int32 SAL_CALL getAccessibleIndexInParent() override;
+ virtual OUString SAL_CALL getAccessibleName() override;
+ const sdr::table::CellRef& getCellRef() const { return mxCell;}
+ void UpdateChildren();
+ static OUString getCellName( sal_Int32 nCol, sal_Int32 nRow );
+
+ // XAccessibleComponent
+ virtual sal_Bool SAL_CALL containsPoint( const css::awt::Point& aPoint) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint(const css::awt::Point& aPoint) override;
+ virtual css::awt::Rectangle SAL_CALL getBounds() override;
+ virtual css::awt::Point SAL_CALL getLocation() override;
+ virtual css::awt::Point SAL_CALL getLocationOnScreen() override;
+ virtual css::awt::Size SAL_CALL getSize() override;
+ virtual void SAL_CALL grabFocus() override;
+ virtual sal_Int32 SAL_CALL getForeground() override;
+ virtual sal_Int32 SAL_CALL getBackground() override;
+
+ // XAccessibleExtendedComponent
+ virtual css::uno::Reference< css::awt::XFont > SAL_CALL getFont() override;
+ virtual OUString SAL_CALL getTitledBorderText() override;
+ virtual OUString SAL_CALL getToolTipText() override;
+
+ // XAccessibleEventBroadcaster
+ virtual void SAL_CALL addAccessibleEventListener( const css::uno::Reference< css::accessibility::XAccessibleEventListener >& rxListener) override;
+ virtual void SAL_CALL removeAccessibleEventListener( const css::uno::Reference< css::accessibility::XAccessibleEventListener >& rxListener) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // IAccessibleViewForwarderListener
+ virtual void ViewForwarderChanged() override;
+
+ // Misc
+
+ /** set the index _nIndex at the accessible cell param _nIndex The new index in parent.
+ */
+ void setIndexInParent(sal_Int32 _nIndex) { mnIndexInParent = _nIndex; }
+
+ //Get the parent table
+ AccessibleTableShape* GetParentTable() { return pAccTable; }
+protected:
+ /// Bundle of information passed to all shapes in a document tree.
+ AccessibleShapeTreeInfo maShapeTreeInfo;
+
+ /// the index in parent.
+ sal_Int32 mnIndexInParent;
+
+ /// The accessible text engine. May be NULL if it can not be created.
+ std::unique_ptr<AccessibleTextHelper> mpText;
+
+ sdr::table::CellRef mxCell;
+
+ /// This method is called from the component helper base class while disposing.
+ virtual void SAL_CALL disposing() override;
+
+ AccessibleTableShape *pAccTable;
+};
+
+} // end of namespace accessibility
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/accessibletableshape.cxx b/svx/source/table/accessibletableshape.cxx
new file mode 100644
index 000000000..540ae3116
--- /dev/null
+++ b/svx/source/table/accessibletableshape.cxx
@@ -0,0 +1,1324 @@
+/* -*- 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 <com/sun/star/table/XMergeableCell.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+#include <tools/diagnose_ex.h>
+#include <vcl/svapp.hxx>
+
+#include <AccessibleTableShape.hxx>
+#include <svx/sdr/table/tablecontroller.hxx>
+#include "accessiblecell.hxx"
+#include <cell.hxx>
+
+#include <algorithm>
+#include <unordered_map>
+
+#include <cppuhelper/implbase.hxx>
+#include <svx/svdotable.hxx>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+
+using namespace ::accessibility;
+using namespace sdr::table;
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::container;
+
+namespace accessibility
+{
+
+typedef std::unordered_map< Reference< XCell >, rtl::Reference< AccessibleCell > > AccessibleCellMap;
+
+class AccessibleTableShapeImpl : public cppu::WeakImplHelper< XModifyListener >
+{
+public:
+ explicit AccessibleTableShapeImpl( AccessibleShapeTreeInfo& rShapeTreeInfo );
+
+ void init( const Reference< XAccessible>& xAccessible, const Reference< XTable >& xTable );
+ void dispose();
+
+ /// @throws IndexOutOfBoundsException
+ /// @throws RuntimeException
+ Reference< XAccessible > getAccessibleChild(sal_Int32 i);
+ /// @throws IndexOutOfBoundsException
+ void getColumnAndRow( sal_Int32 nChildIndex, sal_Int32& rnColumn, sal_Int32& rnRow );
+
+ // XModifyListener
+ virtual void SAL_CALL modified( const EventObject& aEvent ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const EventObject& Source ) override;
+
+ AccessibleShapeTreeInfo& mrShapeTreeInfo;
+ Reference< XTable > mxTable;
+ AccessibleCellMap maChildMap;
+ Reference< XAccessible> mxAccessible;
+ sal_Int32 mRowCount, mColCount;
+ //get the cached AccessibleCell from XCell
+ rtl::Reference< AccessibleCell > getAccessibleCell (const Reference< XCell >& xCell);
+ /// @throws IndexOutOfBoundsException
+ /// @throws RuntimeException
+ rtl::Reference< AccessibleCell > getAccessibleCell (sal_Int32 nRow, sal_Int32 nColumn);
+};
+
+
+AccessibleTableShapeImpl::AccessibleTableShapeImpl( AccessibleShapeTreeInfo& rShapeTreeInfo )
+: mrShapeTreeInfo( rShapeTreeInfo )
+, mRowCount(0)
+, mColCount(0)
+{
+}
+
+
+void AccessibleTableShapeImpl::init( const Reference< XAccessible>& xAccessible, const Reference< XTable >& xTable )
+{
+ mxAccessible = xAccessible;
+ mxTable = xTable;
+
+ if( mxTable.is() )
+ {
+ Reference< XModifyListener > xListener( this );
+ mxTable->addModifyListener( xListener );
+ //register the listener with table model
+ Reference< css::view::XSelectionSupplier > xSelSupplier(xTable, UNO_QUERY);
+ Reference< css::view::XSelectionChangeListener > xSelListener( xAccessible, UNO_QUERY );
+ if (xSelSupplier.is())
+ xSelSupplier->addSelectionChangeListener(xSelListener);
+ mRowCount = mxTable->getRowCount();
+ mColCount = mxTable->getColumnCount();
+ }
+}
+
+
+void AccessibleTableShapeImpl::dispose()
+{
+ if( mxTable.is() )
+ {
+ //remove all the cell's acc object in table's dispose.
+ for( auto& rEntry : maChildMap )
+ {
+ rEntry.second->dispose();
+ }
+ maChildMap.clear();
+ Reference< XModifyListener > xListener( this );
+ mxTable->removeModifyListener( xListener );
+ mxTable.clear();
+ }
+ mxAccessible.clear();
+}
+
+
+//get the cached AccessibleCell from XCell
+rtl::Reference< AccessibleCell > AccessibleTableShapeImpl::getAccessibleCell (const Reference< XCell >& xCell)
+{
+ AccessibleCellMap::iterator iter( maChildMap.find( xCell ) );
+
+ if( iter != maChildMap.end() )
+ {
+ rtl::Reference< AccessibleCell > xChild( (*iter).second );
+ return xChild;
+ }
+ return rtl::Reference< AccessibleCell >();
+}
+
+rtl::Reference< AccessibleCell > AccessibleTableShapeImpl::getAccessibleCell (sal_Int32 nRow, sal_Int32 nColumn)
+{
+ Reference< XCell > xCell( mxTable->getCellByPosition( nColumn, nRow ) );
+ rtl::Reference< AccessibleCell > xChild = getAccessibleCell( xCell );
+
+ if( !xChild.is() && mxTable.is() )
+ {
+ sal_Int32 nChildIndex = mxTable->getColumnCount() * nRow + nColumn;
+ CellRef xCellRef( dynamic_cast< Cell* >( xCell.get() ) );
+
+ rtl::Reference< AccessibleCell > xAccessibleCell( new AccessibleCell( mxAccessible, xCellRef, nChildIndex, mrShapeTreeInfo ) );
+
+ xAccessibleCell->Init();
+ maChildMap[xCell] = xAccessibleCell;
+
+ xChild = xAccessibleCell;
+ }
+ return xChild;
+}
+
+
+Reference< XAccessible > AccessibleTableShapeImpl::getAccessibleChild(sal_Int32 nChildIndex)
+{
+ sal_Int32 nColumn = 0, nRow = 0;
+ getColumnAndRow( nChildIndex, nColumn, nRow );
+
+ Reference< XCell > xCell( mxTable->getCellByPosition( nColumn, nRow ) );
+ AccessibleCellMap::iterator iter( maChildMap.find( xCell ) );
+
+ if( iter != maChildMap.end() )
+ {
+ Reference< XAccessible > xChild( (*iter).second );
+ return xChild;
+ }
+ else
+ {
+ CellRef xCellRef( dynamic_cast< Cell* >( xCell.get() ) );
+
+ rtl::Reference< AccessibleCell > xAccessibleCell( new AccessibleCell( mxAccessible, xCellRef, nChildIndex, mrShapeTreeInfo ) );
+
+ xAccessibleCell->Init();
+ maChildMap[xCell] = xAccessibleCell;
+
+ return xAccessibleCell;
+ }
+}
+
+
+void AccessibleTableShapeImpl::getColumnAndRow( sal_Int32 nChildIndex, sal_Int32& rnColumn, sal_Int32& rnRow )
+{
+ rnRow = 0;
+ rnColumn = nChildIndex;
+
+ if( mxTable.is() )
+ {
+ const sal_Int32 nColumnCount = mxTable->getColumnCount();
+ while( rnColumn >= nColumnCount )
+ {
+ rnRow++;
+ rnColumn -= nColumnCount;
+ }
+
+ if( rnRow < mxTable->getRowCount() )
+ return;
+ }
+
+ throw IndexOutOfBoundsException();
+}
+
+// XModifyListener
+void SAL_CALL AccessibleTableShapeImpl::modified( const EventObject& /*aEvent*/ )
+{
+ if( !mxTable.is() )
+ return;
+
+ try
+ {
+ // structural changes may have happened to the table, validate all accessible cell instances
+ AccessibleCellMap aTempChildMap;
+ aTempChildMap.swap( maChildMap );
+
+ // first move all still existing cells to maChildMap again and update their index
+
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+
+ bool bRowOrColumnChanged = false;
+ if (mRowCount != nRowCount || mColCount != nColCount )
+ {
+ bRowOrColumnChanged = true;
+ mRowCount = nRowCount;
+ mColCount = nColCount;
+ }
+ sal_Int32 nChildIndex = 0;
+
+ for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol )
+ {
+ Reference< XCell > xCell( mxTable->getCellByPosition( nCol, nRow ) );
+ AccessibleCellMap::iterator iter( aTempChildMap.find( xCell ) );
+
+ if( iter != aTempChildMap.end() )
+ {
+ rtl::Reference< AccessibleCell > xAccessibleCell( (*iter).second );
+ xAccessibleCell->setIndexInParent( nChildIndex );
+ xAccessibleCell->UpdateChildren();
+ // If row or column count is changed, there is split or merge, so all cell's acc name should be updated
+ if (bRowOrColumnChanged)
+ {
+ xAccessibleCell->SetAccessibleName(xAccessibleCell->getAccessibleName(), AccessibleContextBase::ManuallySet);
+ }
+ // For merged cell, add invisible & disabled state.
+ Reference< XMergeableCell > xMergedCell( mxTable->getCellByPosition( nCol, nRow ), UNO_QUERY );
+ if (xMergedCell.is() && xMergedCell->isMerged())
+ {
+ xAccessibleCell->ResetState(AccessibleStateType::VISIBLE);
+ xAccessibleCell->ResetState(AccessibleStateType::ENABLED);
+ // IA2 CWS. MT: OFFSCREEN == !SHOWING, should stay consistent
+ // xAccessibleCell->SetState(AccessibleStateType::OFFSCREEN);
+ xAccessibleCell->ResetState(AccessibleStateType::SHOWING);
+ }
+ else
+ {
+ xAccessibleCell->SetState(AccessibleStateType::VISIBLE);
+ xAccessibleCell->SetState(AccessibleStateType::ENABLED);
+ // IA2 CWS. MT: OFFSCREEN == !SHOWING, should stay consistent
+ // xAccessibleCell->ResetState(AccessibleStateType::OFFSCREEN);
+ xAccessibleCell->SetState(AccessibleStateType::SHOWING);
+ }
+
+ // move still existing cell from temporary child map to our child map
+ maChildMap[xCell] = xAccessibleCell;
+ aTempChildMap.erase( iter );
+ }
+ else
+ {
+ CellRef xCellRef( dynamic_cast< Cell* >( xCell.get() ) );
+
+ rtl::Reference< AccessibleCell > xAccessibleCell( new AccessibleCell( mxAccessible, xCellRef, nChildIndex, mrShapeTreeInfo ) );
+
+ xAccessibleCell->Init();
+ maChildMap[xCell] = xAccessibleCell;
+ }
+
+ ++nChildIndex;
+ }
+ }
+
+ // all accessible cell instances still left in aTempChildMap must be disposed
+ // as they are no longer part of the table
+
+ for( auto& rEntry : aTempChildMap )
+ {
+ rEntry.second->dispose();
+ }
+ //notify bridge to update the acc cache.
+ AccessibleTableShape *pAccTable = dynamic_cast <AccessibleTableShape *> (mxAccessible.get());
+ if (pAccTable)
+ pAccTable->CommitChange(AccessibleEventId::INVALIDATE_ALL_CHILDREN, Any(), Any());
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+}
+
+// XEventListener
+void SAL_CALL AccessibleTableShapeImpl::disposing( const EventObject& /*Source*/ )
+{
+}
+
+AccessibleTableShape::AccessibleTableShape( const AccessibleShapeInfo& rShapeInfo, const AccessibleShapeTreeInfo& rShapeTreeInfo)
+: AccessibleTableShape_Base(rShapeInfo, rShapeTreeInfo)
+, mnPreviousSelectionCount(0)
+, mxImpl( new AccessibleTableShapeImpl( maShapeTreeInfo ) )
+{
+}
+
+
+AccessibleTableShape::~AccessibleTableShape()
+{
+}
+
+
+void AccessibleTableShape::Init()
+{
+ try
+ {
+ Reference< XPropertySet > xSet( mxShape, UNO_QUERY_THROW );
+ Reference< XTable > xTable( xSet->getPropertyValue("Model"), UNO_QUERY_THROW );
+
+ mxImpl->init( this, xTable );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+
+ AccessibleTableShape_Base::Init();
+}
+
+
+SvxTableController* AccessibleTableShape::getTableController()
+{
+ SdrView* pView = maShapeTreeInfo.GetSdrView ();
+ if( pView )
+ return dynamic_cast< SvxTableController* >( pView->getSelectionController().get() );
+ else
+ return nullptr;
+}
+
+
+// XInterface
+
+
+Any SAL_CALL AccessibleTableShape::queryInterface( const Type& aType )
+{
+ if ( aType == cppu::UnoType<XAccessibleTableSelection>::get())
+ {
+ Reference<XAccessibleTableSelection> xThis( this );
+ Any aRet;
+ aRet <<= xThis;
+ return aRet;
+ }
+ else
+ return AccessibleTableShape_Base::queryInterface( aType );
+}
+
+
+void SAL_CALL AccessibleTableShape::acquire( ) noexcept
+{
+ AccessibleTableShape_Base::acquire();
+}
+
+
+void SAL_CALL AccessibleTableShape::release( ) noexcept
+{
+ AccessibleTableShape_Base::release();
+}
+
+
+// XAccessible
+
+
+OUString SAL_CALL AccessibleTableShape::getImplementationName()
+{
+ return "com.sun.star.comp.accessibility.AccessibleTableShape";
+}
+
+
+OUString AccessibleTableShape::CreateAccessibleBaseName()
+{
+ return "TableShape";
+}
+
+
+sal_Int32 SAL_CALL AccessibleTableShape::getAccessibleChildCount( )
+{
+ SolarMutexGuard aSolarGuard;
+ return mxImpl->mxTable.is() ? mxImpl->mxTable->getRowCount() * mxImpl->mxTable->getColumnCount() : 0;
+}
+
+
+Reference< XAccessible > SAL_CALL AccessibleTableShape::getAccessibleChild( sal_Int32 i )
+{
+ SolarMutexGuard aSolarGuard;
+ ThrowIfDisposed();
+
+ return mxImpl->getAccessibleChild( i );
+}
+
+
+sal_Int16 SAL_CALL AccessibleTableShape::getAccessibleRole()
+{
+ return AccessibleRole::TABLE;
+}
+
+
+void SAL_CALL AccessibleTableShape::disposing()
+{
+ mxImpl->dispose();
+
+ // let the base do its stuff
+ AccessibleShape::disposing();
+}
+
+
+// XAccessibleTable
+
+
+sal_Int32 SAL_CALL AccessibleTableShape::getAccessibleRowCount()
+{
+ SolarMutexGuard aSolarGuard;
+ return mxImpl->mxTable.is() ? mxImpl->mxTable->getRowCount() : 0;
+}
+
+
+sal_Int32 SAL_CALL AccessibleTableShape::getAccessibleColumnCount( )
+{
+ SolarMutexGuard aSolarGuard;
+ return mxImpl->mxTable.is() ? mxImpl->mxTable->getColumnCount() : 0;
+}
+
+
+OUString SAL_CALL AccessibleTableShape::getAccessibleRowDescription( sal_Int32 nRow )
+{
+ checkCellPosition( 0, nRow );
+ return OUString();
+}
+
+
+OUString SAL_CALL AccessibleTableShape::getAccessibleColumnDescription( sal_Int32 nColumn )
+{
+ SolarMutexGuard aSolarGuard;
+ checkCellPosition( nColumn, 0 );
+ return OUString();
+}
+
+
+sal_Int32 SAL_CALL AccessibleTableShape::getAccessibleRowExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aSolarGuard;
+ checkCellPosition( nColumn, nRow );
+ if( mxImpl->mxTable.is() )
+ {
+ Reference< XMergeableCell > xCell( mxImpl->mxTable->getCellByPosition( nColumn, nRow ), UNO_QUERY );
+ if( xCell.is() )
+ return xCell->getRowSpan();
+ }
+ return 1;
+}
+
+
+sal_Int32 SAL_CALL AccessibleTableShape::getAccessibleColumnExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aSolarGuard;
+ checkCellPosition( nColumn, nRow );
+ if( mxImpl->mxTable.is() )
+ {
+ Reference< XMergeableCell > xCell( mxImpl->mxTable->getCellByPosition( nColumn, nRow ), UNO_QUERY );
+ if( xCell.is() )
+ return xCell->getColumnSpan();
+ }
+ return 1;
+}
+
+
+Reference< XAccessibleTable > SAL_CALL AccessibleTableShape::getAccessibleRowHeaders( )
+{
+ Reference< XAccessibleTable > xRet;
+ SvxTableController* pController = getTableController();
+ if( pController )
+ {
+ if( pController->isRowHeader() )
+ {
+ xRet = new AccessibleTableHeaderShape( this, true );
+ }
+ }
+ return xRet;
+}
+
+
+Reference< XAccessibleTable > SAL_CALL AccessibleTableShape::getAccessibleColumnHeaders( )
+{
+ Reference< XAccessibleTable > xRet;
+ SvxTableController* pController = getTableController();
+ if( pController )
+ {
+ if( pController->isColumnHeader() )
+ {
+ xRet = new AccessibleTableHeaderShape( this, false );
+ }
+ }
+ return xRet;
+}
+
+
+Sequence< sal_Int32 > SAL_CALL AccessibleTableShape::getSelectedAccessibleRows( )
+{
+ sal_Int32 nRow = getAccessibleRowCount();
+ ::std::vector<bool> aSelected( nRow, true );
+ sal_Int32 nCount = nRow;
+ for( sal_Int32 i = 0; i < nRow; i++ )
+ {
+ try
+ {
+ aSelected[i] = isAccessibleRowSelected( i );
+ }
+ catch( ... )
+ {
+ return Sequence< sal_Int32 >();
+ }
+
+ if( !aSelected[i] )
+ nCount--;
+ }
+ Sequence < sal_Int32 > aRet( nCount );
+ sal_Int32 *pRet = aRet.getArray();
+ sal_Int32 nPos = 0;
+ size_t nSize = aSelected.size();
+ for( size_t i=0; i < nSize && nPos < nCount; i++ )
+ {
+ if( aSelected[i] )
+ {
+ *pRet++ = i;
+ nPos++;
+ }
+ }
+
+ return aRet;
+}
+
+
+Sequence< sal_Int32 > SAL_CALL AccessibleTableShape::getSelectedAccessibleColumns( )
+{
+ sal_Int32 nColumn = getAccessibleColumnCount();
+ ::std::vector<bool> aSelected( nColumn, true );
+ sal_Int32 nCount = nColumn;
+ for( sal_Int32 i = 0; i < nColumn; i++ )
+ {
+ try
+ {
+ aSelected[i] = isAccessibleColumnSelected( i );
+ }
+ catch( ... )
+ {
+ return Sequence< sal_Int32 >();
+ }
+
+ if( !aSelected[i] )
+ nCount--;
+ }
+ Sequence < sal_Int32 > aRet( nCount );
+ sal_Int32 *pRet = aRet.getArray();
+ sal_Int32 nPos = 0;
+ size_t nSize = aSelected.size();
+ for( size_t i=0; i < nSize && nPos < nCount; i++ )
+ {
+ if( aSelected[i] )
+ {
+ *pRet++ = i;
+ nPos++;
+ }
+ }
+
+ return aRet;
+}
+
+
+sal_Bool SAL_CALL AccessibleTableShape::isAccessibleRowSelected( sal_Int32 nRow )
+{
+ SolarMutexGuard aSolarGuard;
+ checkCellPosition( 0, nRow );
+ SvxTableController* pController = getTableController();
+ if( pController )
+ {
+ return pController->isRowSelected( nRow );
+ }
+ return false;
+}
+
+
+sal_Bool SAL_CALL AccessibleTableShape::isAccessibleColumnSelected( sal_Int32 nColumn )
+{
+ SolarMutexGuard aSolarGuard;
+ checkCellPosition( nColumn, 0 );
+ SvxTableController* pController = getTableController();
+ if( pController )
+ {
+ return pController->isColumnSelected( nColumn );
+ }
+ return false;
+}
+
+
+Reference< XAccessible > SAL_CALL AccessibleTableShape::getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aSolarGuard;
+ checkCellPosition( nColumn, nRow );
+
+ sal_Int32 nChildIndex = 0;
+ if( mxImpl->mxTable.is() )
+ nChildIndex = mxImpl->mxTable->getColumnCount() * nRow + nColumn;
+
+ return getAccessibleChild( nChildIndex );
+}
+
+
+Reference< XAccessible > SAL_CALL AccessibleTableShape::getAccessibleCaption( )
+{
+ Reference< XAccessible > xRet;
+ return xRet;
+}
+
+
+Reference< XAccessible > SAL_CALL AccessibleTableShape::getAccessibleSummary( )
+{
+ Reference< XAccessible > xRet;
+ return xRet;
+}
+
+
+sal_Bool SAL_CALL AccessibleTableShape::isAccessibleSelected( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aSolarGuard;
+ checkCellPosition( nColumn, nRow );
+
+ SvxTableController* pController = getTableController();
+ if( pController && pController->hasSelectedCells() )
+ {
+ CellPos aFirstPos, aLastPos;
+ pController->getSelectedCells( aFirstPos, aLastPos );
+ if( (aFirstPos.mnRow <= nRow) && (aFirstPos.mnCol <= nColumn) && (nRow <= aLastPos.mnRow) && (nColumn <= aLastPos.mnCol) )
+ return true;
+ }
+
+ return false;
+}
+
+
+sal_Int32 SAL_CALL AccessibleTableShape::getAccessibleIndex( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aSolarGuard;
+ checkCellPosition( nColumn, nRow );
+ return mxImpl->mxTable.is() ? (nRow * mxImpl->mxTable->getColumnCount() + nColumn) : 0;
+}
+
+
+sal_Int32 SAL_CALL AccessibleTableShape::getAccessibleRow( sal_Int32 nChildIndex )
+{
+ SolarMutexGuard aSolarGuard;
+ sal_Int32 nColumn = 0, nRow = 0;
+ mxImpl->getColumnAndRow( nChildIndex, nColumn, nRow );
+ return nRow;
+}
+
+
+sal_Int32 SAL_CALL AccessibleTableShape::getAccessibleColumn( sal_Int32 nChildIndex )
+{
+ SolarMutexGuard aSolarGuard;
+ sal_Int32 nColumn = 0, nRow = 0;
+ mxImpl->getColumnAndRow( nChildIndex, nColumn, nRow );
+ return nColumn;
+}
+
+
+// XAccessibleSelection
+
+
+void SAL_CALL AccessibleTableShape::selectAccessibleChild( sal_Int32 nChildIndex )
+{
+ SolarMutexGuard aSolarGuard;
+ CellPos aPos;
+ mxImpl->getColumnAndRow( nChildIndex, aPos.mnCol, aPos.mnRow );
+
+ // todo, select table shape?!?
+ SvxTableController* pController = getTableController();
+ if( !pController )
+ return;
+
+ CellPos aFirstPos( aPos ), aLastPos( aPos );
+ if( pController->hasSelectedCells() )
+ {
+ pController->getSelectedCells( aFirstPos, aLastPos );
+
+ aFirstPos.mnRow = std::min( aFirstPos.mnRow, aPos.mnRow );
+ aFirstPos.mnCol = std::min( aFirstPos.mnCol, aPos.mnCol );
+ aLastPos.mnRow = std::max( aLastPos.mnRow, aPos.mnRow );
+ aLastPos.mnCol = std::max( aLastPos.mnCol, aPos.mnCol );
+ }
+ pController->setSelectedCells( aFirstPos, aLastPos );
+}
+
+
+sal_Bool SAL_CALL AccessibleTableShape::isAccessibleChildSelected( sal_Int32 nChildIndex )
+{
+ SolarMutexGuard aSolarGuard;
+ CellPos aPos;
+ mxImpl->getColumnAndRow( nChildIndex, aPos.mnCol, aPos.mnRow );
+
+ return isAccessibleSelected(aPos.mnRow, aPos.mnCol);
+}
+
+
+void SAL_CALL AccessibleTableShape::clearAccessibleSelection()
+{
+ SolarMutexGuard aSolarGuard;
+
+ SvxTableController* pController = getTableController();
+ if( pController )
+ pController->clearSelection();
+}
+
+
+void SAL_CALL AccessibleTableShape::selectAllAccessibleChildren()
+{
+ SolarMutexGuard aSolarGuard;
+
+ // todo: force selection of shape?
+ SvxTableController* pController = getTableController();
+ if( pController )
+ pController->selectAll();
+}
+
+
+sal_Int32 SAL_CALL AccessibleTableShape::getSelectedAccessibleChildCount()
+{
+ SolarMutexGuard aSolarGuard;
+
+ SvxTableController* pController = getTableController();
+ if( pController && pController->hasSelectedCells() )
+ {
+ CellPos aFirstPos, aLastPos;
+ pController->getSelectedCells( aFirstPos, aLastPos );
+
+ const sal_Int32 nSelectedColumns = std::max( sal_Int32(0), aLastPos.mnCol - aFirstPos.mnCol ) + 1;
+ const sal_Int32 nSelectedRows = std::max( sal_Int32(0), aLastPos.mnRow - aFirstPos.mnRow ) + 1;
+ return nSelectedRows * nSelectedColumns;
+ }
+
+ return 0;
+}
+
+
+Reference< XAccessible > SAL_CALL AccessibleTableShape::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex )
+{
+ SolarMutexGuard aSolarGuard;
+
+ if( nSelectedChildIndex < 0 )
+ throw IndexOutOfBoundsException();
+
+ sal_Int32 nChildIndex = GetIndexOfSelectedChild( nSelectedChildIndex );
+
+ if( nChildIndex < 0 )
+ throw IndexOutOfBoundsException();
+
+ if ( nChildIndex >= getAccessibleChildCount() )
+ {
+ throw IndexOutOfBoundsException();
+ }
+
+ return getAccessibleChild( nChildIndex );
+}
+
+
+void SAL_CALL AccessibleTableShape::deselectAccessibleChild( sal_Int32 nChildIndex )
+{
+ SolarMutexGuard aSolarGuard;
+ CellPos aPos;
+ mxImpl->getColumnAndRow( nChildIndex, aPos.mnCol, aPos.mnRow );
+
+ // todo, select table shape?!?
+ SvxTableController* pController = getTableController();
+ if( !(pController && pController->hasSelectedCells()) )
+ return;
+
+ CellPos aFirstPos, aLastPos;
+ pController->getSelectedCells( aFirstPos, aLastPos );
+
+ // create a selection where aPos is not part of anymore
+ aFirstPos.mnRow = std::min( aFirstPos.mnRow, aPos.mnRow+1 );
+ aFirstPos.mnCol = std::min( aFirstPos.mnCol, aPos.mnCol+1 );
+ aLastPos.mnRow = std::max( aLastPos.mnRow, aPos.mnRow-1 );
+ aLastPos.mnCol = std::max( aLastPos.mnCol, aPos.mnCol-1 );
+
+ // new selection may be invalid (child to deselect is not at a border of the selection but in between)
+ if( (aFirstPos.mnRow > aLastPos.mnRow) || (aFirstPos.mnCol > aLastPos.mnCol) )
+ pController->clearSelection(); // if selection is invalid, clear all
+ else
+ pController->setSelectedCells( aFirstPos, aLastPos );
+}
+
+// XAccessibleTableSelection
+sal_Bool SAL_CALL AccessibleTableShape::selectRow( sal_Int32 row )
+{
+ SolarMutexGuard aSolarGuard;
+ SvxTableController* pController = getTableController();
+ if( !pController )
+ return false;
+ return pController->selectRow( row );
+}
+
+sal_Bool SAL_CALL AccessibleTableShape::selectColumn( sal_Int32 column )
+{
+ SolarMutexGuard aSolarGuard;
+ SvxTableController* pController = getTableController();
+ if( !pController )
+ return false;
+ return pController->selectColumn( column );
+}
+
+sal_Bool SAL_CALL AccessibleTableShape::unselectRow( sal_Int32 row )
+{
+ SolarMutexGuard aSolarGuard;
+ SvxTableController* pController = getTableController();
+ if( !pController )
+ return false;
+ return pController->deselectRow( row );
+}
+
+sal_Bool SAL_CALL AccessibleTableShape::unselectColumn( sal_Int32 column )
+{
+ SolarMutexGuard aSolarGuard;
+ SvxTableController* pController = getTableController();
+ if( !pController )
+ return false;
+ return pController->deselectColumn( column );
+}
+
+sal_Int32 AccessibleTableShape::GetIndexOfSelectedChild(
+ sal_Int32 nSelectedChildIndex ) const
+{
+ sal_Int32 nChildren = const_cast<AccessibleTableShape*>(this)->getAccessibleChildCount();
+
+ if( nSelectedChildIndex >= nChildren )
+ return -1;
+
+ sal_Int32 n = 0;
+ while( n < nChildren )
+ {
+ if( const_cast<AccessibleTableShape*>(this)->isAccessibleChildSelected( n ) )
+ {
+ if( 0 == nSelectedChildIndex )
+ break;
+ else
+ --nSelectedChildIndex;
+ }
+ ++n;
+ }
+
+ return n < nChildren ? n : -1;
+}
+void AccessibleTableShape::getColumnAndRow( sal_Int32 nChildIndex, sal_Int32& rnColumn, sal_Int32& rnRow )
+{
+ mxImpl->getColumnAndRow(nChildIndex, rnColumn, rnRow);
+}
+
+// XSelectionChangeListener
+void SAL_CALL
+ AccessibleTableShape::disposing (const EventObject& aEvent)
+{
+ AccessibleShape::disposing(aEvent);
+}
+void SAL_CALL AccessibleTableShape::selectionChanged (const EventObject& rEvent)
+{
+ //sdr::table::CellRef xCellRef = static_cast< sdr::table::CellRef > (rEvent.Source);
+ Reference< XCell > xCell(rEvent.Source, UNO_QUERY);
+ if (!xCell.is())
+ return;
+
+ rtl::Reference< AccessibleCell > xAccCell = mxImpl->getAccessibleCell( xCell );
+ if (!xAccCell.is())
+ return;
+
+ sal_Int32 nIndex = xAccCell->getAccessibleIndexInParent(),
+ nCount = getSelectedAccessibleChildCount();
+ bool bSelected = isAccessibleChildSelected(nIndex);
+ if (mnPreviousSelectionCount == 0 && nCount > 0 && bSelected)
+ {
+ xAccCell->SetState(AccessibleStateType::SELECTED);
+ xAccCell->CommitChange(AccessibleEventId::SELECTION_CHANGED, Any(), Any());
+ }
+ else if (bSelected)
+ {
+ xAccCell->SetState(AccessibleStateType::SELECTED);
+ xAccCell->CommitChange(AccessibleEventId::SELECTION_CHANGED_ADD, Any(), Any());
+ }
+ else
+ {
+ xAccCell->ResetState(AccessibleStateType::SELECTED);
+ xAccCell->CommitChange(AccessibleEventId::SELECTION_CHANGED_REMOVE, Any(), Any());
+ }
+ mnPreviousSelectionCount = nCount;
+}
+// Get the currently active cell which is text editing
+AccessibleCell* AccessibleTableShape::GetActiveAccessibleCell()
+{
+ rtl::Reference< AccessibleCell > xAccCell;
+ AccessibleCell* pAccCell = nullptr;
+ SvxTableController* pController = getTableController();
+ if (pController)
+ {
+ sdr::table::SdrTableObj* pTableObj = pController->GetTableObj();
+ if ( pTableObj )
+ {
+ const sdr::table::CellRef& xCellRef (pTableObj->getActiveCell());
+ if ( xCellRef.is() )
+ {
+ try
+ {
+ CellPos rPos;
+ pTableObj->getActiveCellPos( rPos );
+ xAccCell = mxImpl->getAccessibleCell( rPos.mnRow, rPos.mnCol );
+ if ( xAccCell.is() )
+ pAccCell = xAccCell.get();
+ }
+ catch ( IndexOutOfBoundsException& ) {}
+ }
+ }
+ }
+ return pAccCell;
+}
+
+//If current active cell is in editing, the focus state should be set to internal text
+bool AccessibleTableShape::SetState (sal_Int16 aState)
+{
+ if( aState == AccessibleStateType::FOCUSED )
+ {
+ AccessibleCell* pActiveAccessibleCell = GetActiveAccessibleCell();
+ if( pActiveAccessibleCell != nullptr )
+ return pActiveAccessibleCell->SetState(aState);
+ }
+
+ return AccessibleShape::SetState (aState);
+}
+
+//If current active cell is in editing, the focus state should be reset to internal text
+bool AccessibleTableShape::ResetState (sal_Int16 aState)
+{
+ if( aState == AccessibleStateType::FOCUSED )
+ {
+ AccessibleCell* pActiveAccessibleCell = GetActiveAccessibleCell();
+ if( pActiveAccessibleCell != nullptr )
+ return pActiveAccessibleCell->ResetState(aState);
+ }
+
+ return AccessibleShape::ResetState (aState);
+}
+
+bool AccessibleTableShape::SetStateDirectly (sal_Int16 aState)
+{
+ return AccessibleContextBase::SetState (aState);
+}
+
+bool AccessibleTableShape::ResetStateDirectly (sal_Int16 aState)
+{
+ return AccessibleContextBase::ResetState (aState);
+}
+
+void AccessibleTableShape::checkCellPosition( sal_Int32 nCol, sal_Int32 nRow )
+{
+ if( (nCol >= 0) && (nRow >= 0) && mxImpl->mxTable.is() && (nCol < mxImpl->mxTable->getColumnCount()) && (nRow < mxImpl->mxTable->getRowCount()) )
+ return;
+
+ throw IndexOutOfBoundsException();
+}
+
+AccessibleTableHeaderShape::AccessibleTableHeaderShape( AccessibleTableShape* pTable, bool bRow )
+{
+ mpTable = pTable;
+ mbRow = bRow;
+}
+
+AccessibleTableHeaderShape::~AccessibleTableHeaderShape()
+{
+ mpTable = nullptr;
+}
+
+// XAccessible
+Reference< XAccessibleContext > SAL_CALL AccessibleTableHeaderShape::getAccessibleContext()
+{
+ return this;
+}
+
+// XAccessibleContext
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getAccessibleChildCount( )
+{
+ return getAccessibleRowCount() * getAccessibleColumnCount();
+}
+
+Reference< XAccessible > SAL_CALL AccessibleTableHeaderShape::getAccessibleChild( sal_Int32 i )
+{
+ return mpTable->getAccessibleChild( i );
+}
+
+Reference< XAccessible > SAL_CALL AccessibleTableHeaderShape::getAccessibleParent()
+{
+ Reference< XAccessible > XParent;
+ return XParent;
+}
+
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getAccessibleIndexInParent()
+{
+ return -1;
+}
+
+sal_Int16 SAL_CALL AccessibleTableHeaderShape::getAccessibleRole()
+{
+ return mpTable->getAccessibleRole();
+}
+
+OUString SAL_CALL AccessibleTableHeaderShape::getAccessibleDescription()
+{
+ return mpTable->getAccessibleDescription();
+}
+
+OUString SAL_CALL AccessibleTableHeaderShape::getAccessibleName()
+{
+ return mpTable->getAccessibleName();
+}
+
+Reference< XAccessibleStateSet > SAL_CALL AccessibleTableHeaderShape::getAccessibleStateSet()
+{
+ return mpTable->getAccessibleStateSet();
+}
+
+Reference< XAccessibleRelationSet > SAL_CALL AccessibleTableHeaderShape::getAccessibleRelationSet()
+{
+ return mpTable->getAccessibleRelationSet();
+}
+
+Locale SAL_CALL AccessibleTableHeaderShape::getLocale()
+{
+ return mpTable->getLocale();
+}
+
+//XAccessibleComponent
+sal_Bool SAL_CALL AccessibleTableHeaderShape::containsPoint ( const css::awt::Point& aPoint )
+{
+ return mpTable->containsPoint( aPoint );
+}
+
+Reference< XAccessible > SAL_CALL AccessibleTableHeaderShape::getAccessibleAtPoint ( const css::awt::Point& aPoint)
+{
+ return mpTable->getAccessibleAtPoint( aPoint );
+}
+
+css::awt::Rectangle SAL_CALL AccessibleTableHeaderShape::getBounds()
+{
+ return mpTable->getBounds();
+}
+
+css::awt::Point SAL_CALL AccessibleTableHeaderShape::getLocation()
+{
+ return mpTable->getLocation();
+}
+
+css::awt::Point SAL_CALL AccessibleTableHeaderShape::getLocationOnScreen()
+{
+ return mpTable->getLocationOnScreen();
+}
+
+css::awt::Size SAL_CALL AccessibleTableHeaderShape::getSize()
+{
+ return mpTable->getSize();
+}
+
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getForeground()
+{
+ return mpTable->getForeground();
+}
+
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getBackground()
+{
+ return mpTable->getBackground();
+}
+
+void SAL_CALL AccessibleTableHeaderShape::grabFocus()
+{
+ mpTable->grabFocus();
+}
+// XAccessibleTable
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getAccessibleRowCount()
+{
+ return mbRow ? 1 : mpTable->getAccessibleRowCount();
+}
+
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getAccessibleColumnCount()
+{
+ return !mbRow ? 1 : mpTable->getAccessibleColumnCount();
+}
+
+OUString SAL_CALL AccessibleTableHeaderShape::getAccessibleRowDescription( sal_Int32 nRow )
+{
+ return mpTable->getAccessibleRowDescription( nRow );
+}
+
+OUString SAL_CALL AccessibleTableHeaderShape::getAccessibleColumnDescription( sal_Int32 nColumn )
+{
+ return mpTable->getAccessibleColumnDescription( nColumn );
+}
+
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getAccessibleRowExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ return mpTable->getAccessibleRowExtentAt( nRow, nColumn );
+}
+
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getAccessibleColumnExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ return mpTable->getAccessibleColumnExtentAt( nRow, nColumn );
+}
+
+Reference< XAccessibleTable > SAL_CALL AccessibleTableHeaderShape::getAccessibleRowHeaders( )
+{
+ Reference< XAccessibleTable > xRet;
+ return xRet;
+}
+
+Reference< XAccessibleTable > SAL_CALL AccessibleTableHeaderShape::getAccessibleColumnHeaders( )
+{
+ Reference< XAccessibleTable > xRet;
+ return xRet;
+}
+
+Sequence< sal_Int32 > SAL_CALL AccessibleTableHeaderShape::getSelectedAccessibleRows( )
+{
+ sal_Int32 nRow = getAccessibleRowCount();
+ ::std::vector<bool> aSelected( nRow, true );
+ sal_Int32 nCount = nRow;
+ for( sal_Int32 i = 0; i < nRow; i++ )
+ {
+ try
+ {
+ aSelected[i] = isAccessibleRowSelected( i );
+ }
+ catch( ... )
+ {
+ return Sequence< sal_Int32 >();
+ }
+
+ if( !aSelected[i] )
+ nCount--;
+ }
+ Sequence < sal_Int32 > aRet( nCount );
+ sal_Int32 *pRet = aRet.getArray();
+ sal_Int32 nPos = 0;
+ size_t nSize = aSelected.size();
+ for( size_t i=0; i < nSize && nPos < nCount; i++ )
+ {
+ if( aSelected[i] )
+ {
+ *pRet++ = i;
+ nPos++;
+ }
+ }
+
+ return aRet;
+}
+
+Sequence< sal_Int32 > SAL_CALL AccessibleTableHeaderShape::getSelectedAccessibleColumns( )
+{
+ sal_Int32 nColumn = getAccessibleColumnCount();
+ ::std::vector<bool> aSelected( nColumn, true );
+ sal_Int32 nCount = nColumn;
+ for( sal_Int32 i = 0; i < nColumn; i++ )
+ {
+ try
+ {
+ aSelected[i] = isAccessibleColumnSelected( i );
+ }
+ catch( ... )
+ {
+ return Sequence< sal_Int32 >();
+ }
+
+ if( !aSelected[i] )
+ nCount--;
+ }
+ Sequence < sal_Int32 > aRet( nCount );
+ sal_Int32 *pRet = aRet.getArray();
+ sal_Int32 nPos = 0;
+ size_t nSize = aSelected.size();
+ for( size_t i=0; i < nSize && nPos < nCount; i++ )
+ {
+ if( aSelected[i] )
+ {
+ *pRet++ = i;
+ nPos++;
+ }
+ }
+
+ return aRet;
+}
+
+sal_Bool SAL_CALL AccessibleTableHeaderShape::isAccessibleRowSelected( sal_Int32 nRow )
+{
+ return mpTable->isAccessibleRowSelected( nRow );
+}
+
+sal_Bool SAL_CALL AccessibleTableHeaderShape::isAccessibleColumnSelected( sal_Int32 nColumn )
+{
+ return mpTable->isAccessibleColumnSelected( nColumn );
+}
+
+Reference< XAccessible > SAL_CALL AccessibleTableHeaderShape::getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ return mpTable->getAccessibleCellAt( nRow, nColumn );
+}
+
+Reference< XAccessible > SAL_CALL AccessibleTableHeaderShape::getAccessibleCaption( )
+{
+ return mpTable->getAccessibleCaption();
+}
+
+Reference< XAccessible > SAL_CALL AccessibleTableHeaderShape::getAccessibleSummary( )
+{
+ return mpTable->getAccessibleSummary();
+}
+
+sal_Bool SAL_CALL AccessibleTableHeaderShape::isAccessibleSelected( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ return mpTable->isAccessibleSelected( nRow, nColumn );
+}
+
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getAccessibleIndex( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ return mpTable->getAccessibleIndex( nRow, nColumn );
+}
+
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getAccessibleRow( sal_Int32 nChildIndex )
+{
+ return mpTable->getAccessibleRow( nChildIndex );
+}
+
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getAccessibleColumn( sal_Int32 nChildIndex )
+{
+ return mpTable->getAccessibleColumn( nChildIndex );
+}
+
+// XAccessibleTableSelection
+sal_Bool SAL_CALL AccessibleTableHeaderShape::selectRow( sal_Int32 row )
+{
+ if( mbRow )
+ return mpTable->selectRow( row );
+ else
+ {
+ mpTable->clearAccessibleSelection();
+ sal_Int32 nIndex = mpTable->getAccessibleIndex( row, 0 );
+ mpTable->selectAccessibleChild( nIndex );
+ return true;
+ }
+}
+
+sal_Bool SAL_CALL AccessibleTableHeaderShape::selectColumn( sal_Int32 column )
+{
+ if( !mbRow )
+ return mpTable->selectColumn( column );
+ else
+ {
+ mpTable->clearAccessibleSelection();
+ sal_Int32 nIndex = mpTable->getAccessibleIndex( 0, column );
+ mpTable->selectAccessibleChild( nIndex );
+ return true;
+ }
+}
+
+sal_Bool SAL_CALL AccessibleTableHeaderShape::unselectRow( sal_Int32 row )
+{
+ if( mbRow )
+ return mpTable->unselectRow( row );
+ else
+ {
+ sal_Int32 nIndex = mpTable->getAccessibleIndex( row, 0 );
+ mpTable->deselectAccessibleChild( nIndex );
+ return true;
+ }
+}
+
+sal_Bool SAL_CALL AccessibleTableHeaderShape::unselectColumn( sal_Int32 column )
+{
+ if( !mbRow )
+ return mpTable->unselectColumn( column );
+ else
+ {
+ sal_Int32 nIndex = mpTable->getAccessibleIndex( 0, column );
+ mpTable->deselectAccessibleChild( nIndex );
+ return true;
+ }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/cell.cxx b/svx/source/table/cell.cxx
new file mode 100644
index 000000000..ba335ca27
--- /dev/null
+++ b/svx/source/table/cell.cxx
@@ -0,0 +1,1712 @@
+/* -*- 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 <com/sun/star/drawing/BitmapMode.hpp>
+#include <com/sun/star/style/XStyle.hpp>
+#include <com/sun/star/text/WritingMode.hpp>
+#include <com/sun/star/table/TableBorder.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <o3tl/any.hxx>
+#include <svl/style.hxx>
+#include <svl/itemset.hxx>
+
+#include <vcl/svapp.hxx>
+#include <libxml/xmlwriter.h>
+
+#include <sdr/properties/textproperties.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/writingmodeitem.hxx>
+#include <svx/svdotable.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/unoshtxt.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/sdooitm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/sdmetitm.hxx>
+#include <svx/xit.hxx>
+#include <getallcharpropids.hxx>
+#include "tableundo.hxx"
+#include <cell.hxx>
+#include <svx/unoshprp.hxx>
+#include <svx/unoshape.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <svx/xflbstit.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/svdpool.hxx>
+#include <svx/xflclit.hxx>
+#include <tools/diagnose_ex.h>
+
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::style;
+using namespace ::com::sun::star::container;
+
+
+static const SvxItemPropertySet* ImplGetSvxCellPropertySet()
+{
+ // property map for an outliner text
+ static const SfxItemPropertyMapEntry aSvxCellPropertyMap[] =
+ {
+ FILL_PROPERTIES
+// { "HasLevels", OWN_ATTR_HASLEVELS, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"Style", OWN_ATTR_STYLE, cppu::UnoType< css::style::XStyle >::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { UNO_NAME_TEXT_WRITINGMODE, SDRATTR_TEXTDIRECTION, cppu::UnoType<css::text::WritingMode>::get(), 0, 0},
+ { UNO_NAME_TEXT_HORZADJUST, SDRATTR_TEXT_HORZADJUST, cppu::UnoType<css::drawing::TextHorizontalAdjust>::get(), 0, 0},
+ { UNO_NAME_TEXT_LEFTDIST, SDRATTR_TEXT_LEFTDIST, cppu::UnoType<sal_Int32>::get(), 0, 0, PropertyMoreFlags::METRIC_ITEM},
+ { UNO_NAME_TEXT_LOWERDIST, SDRATTR_TEXT_LOWERDIST, cppu::UnoType<sal_Int32>::get(), 0, 0, PropertyMoreFlags::METRIC_ITEM},
+ { UNO_NAME_TEXT_RIGHTDIST, SDRATTR_TEXT_RIGHTDIST, cppu::UnoType<sal_Int32>::get(), 0, 0, PropertyMoreFlags::METRIC_ITEM},
+ { UNO_NAME_TEXT_UPPERDIST, SDRATTR_TEXT_UPPERDIST, cppu::UnoType<sal_Int32>::get(), 0, 0, PropertyMoreFlags::METRIC_ITEM},
+ { UNO_NAME_TEXT_VERTADJUST, SDRATTR_TEXT_VERTADJUST, cppu::UnoType<css::drawing::TextVerticalAdjust>::get(), 0, 0},
+ { UNO_NAME_TEXT_WORDWRAP, SDRATTR_TEXT_WORDWRAP, cppu::UnoType<bool>::get(), 0, 0},
+
+ { u"TableBorder", OWN_ATTR_TABLEBORDER, cppu::UnoType<TableBorder>::get(), 0, 0 },
+ { u"TopBorder", SDRATTR_TABLE_BORDER, cppu::UnoType<BorderLine>::get(), 0, TOP_BORDER },
+ { u"BottomBorder", SDRATTR_TABLE_BORDER, cppu::UnoType<BorderLine>::get(), 0, BOTTOM_BORDER },
+ { u"LeftBorder", SDRATTR_TABLE_BORDER, cppu::UnoType<BorderLine>::get(), 0, LEFT_BORDER },
+ { u"RightBorder", SDRATTR_TABLE_BORDER, cppu::UnoType<BorderLine>::get(), 0, RIGHT_BORDER },
+ { u"RotateAngle", SDRATTR_TABLE_TEXT_ROTATION, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+
+ SVX_UNOEDIT_OUTLINER_PROPERTIES,
+ SVX_UNOEDIT_CHAR_PROPERTIES,
+ SVX_UNOEDIT_PARA_PROPERTIES,
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ static SvxItemPropertySet aSvxCellPropertySet( aSvxCellPropertyMap, SdrObject::GetGlobalDrawObjectItemPool() );
+ return &aSvxCellPropertySet;
+}
+
+namespace
+{
+
+class CellTextProvider : public svx::ITextProvider
+{
+public:
+ explicit CellTextProvider(const sdr::table::CellRef& rCell);
+ virtual ~CellTextProvider();
+
+private:
+ virtual sal_Int32 getTextCount() const override;
+ virtual SdrText* getText(sal_Int32 nIndex) const override;
+
+private:
+ const sdr::table::CellRef m_xCell;
+};
+
+CellTextProvider::CellTextProvider(const sdr::table::CellRef& rCell)
+ : m_xCell(rCell)
+{
+}
+
+CellTextProvider::~CellTextProvider()
+{
+}
+
+sal_Int32 CellTextProvider::getTextCount() const
+{
+ return 1;
+}
+
+SdrText* CellTextProvider::getText(sal_Int32 nIndex) const
+{
+ (void) nIndex;
+ assert(nIndex == 0);
+ return m_xCell.get();
+}
+
+}
+
+namespace sdr::properties
+{
+ class CellProperties : public TextProperties
+ {
+ protected:
+ // create a new itemset
+ SfxItemSet CreateObjectSpecificItemSet(SfxItemPool& rPool) override;
+
+ const svx::ITextProvider& getTextProvider() const override;
+
+ public:
+ // basic constructor
+ CellProperties(SdrObject& rObj, sdr::table::Cell* pCell );
+
+ // constructor for copying, but using new object
+ CellProperties(const CellProperties& rProps, SdrObject& rObj, sdr::table::Cell* pCell);
+
+ // Clone() operator, normally just calls the local copy constructor
+ std::unique_ptr<BaseProperties> Clone(SdrObject& rObj) const override;
+
+ void ForceDefaultAttributes() override;
+
+ void ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich) override;
+
+ void ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem = nullptr) override;
+
+ sdr::table::CellRef mxCell;
+
+ private:
+ const CellTextProvider maTextProvider;
+ };
+
+ // create a new itemset
+ SfxItemSet CellProperties::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>);
+ }
+
+ const svx::ITextProvider& CellProperties::getTextProvider() const
+ {
+ return maTextProvider;
+ }
+
+ CellProperties::CellProperties(SdrObject& rObj, sdr::table::Cell* pCell)
+ : TextProperties(rObj)
+ , mxCell(pCell)
+ , maTextProvider(mxCell)
+ {
+ }
+
+ CellProperties::CellProperties(const CellProperties& rProps, SdrObject& rObj, sdr::table::Cell* pCell)
+ : TextProperties(rProps, rObj)
+ , mxCell( pCell )
+ , maTextProvider(mxCell)
+ {
+ }
+
+ std::unique_ptr<BaseProperties> CellProperties::Clone(SdrObject& rObj) const
+ {
+ OSL_FAIL("CellProperties::Clone(), does not work yet!");
+ return std::unique_ptr<BaseProperties>(new CellProperties(*this, rObj,nullptr));
+ }
+
+ void CellProperties::ForceDefaultAttributes()
+ {
+ }
+
+ void CellProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich)
+ {
+ SdrTextObj& rObj = static_cast<SdrTextObj&>(GetSdrObject());
+
+ if( mxCell.is() )
+ {
+ std::optional<OutlinerParaObject> pParaObj = mxCell->CreateEditOutlinerParaObject();
+
+ if( !pParaObj && mxCell->GetOutlinerParaObject())
+ pParaObj = *mxCell->GetOutlinerParaObject();
+
+ if(pParaObj)
+ {
+ // handle outliner attributes
+ Outliner* pOutliner = nullptr;
+
+ if(mxCell->IsTextEditActive())
+ {
+ pOutliner = rObj.GetTextEditOutliner();
+ }
+ else
+ {
+ pOutliner = &rObj.ImpGetDrawOutliner();
+ pOutliner->SetText(*pParaObj);
+ }
+
+ sal_Int32 nParaCount(pOutliner->GetParagraphCount());
+
+ // if the user sets character attributes to the complete
+ // cell we want to remove all hard set character attributes
+ // with same which ids from the text
+ std::vector<sal_uInt16> aCharWhichIds(GetAllCharPropIds(aChangedItems));
+
+ for(sal_Int32 nPara = 0; nPara < nParaCount; nPara++)
+ {
+ SfxItemSet aSet(pOutliner->GetParaAttribs(nPara));
+ for (const SfxPoolItem* pItem : aChangedItems)
+ aSet.Put(*pItem);
+ if (nDeletedWhich)
+ aSet.ClearItem(nDeletedWhich);
+
+ for (const auto& rWhichId : aCharWhichIds)
+ {
+ pOutliner->RemoveCharAttribs(nPara, rWhichId);
+ }
+
+ pOutliner->SetParaAttribs(nPara, aSet);
+ }
+
+ if(!mxCell->IsTextEditActive())
+ {
+ if(nParaCount)
+ {
+ // force ItemSet
+ GetObjectItemSet();
+
+ SfxItemSet aNewSet(pOutliner->GetParaAttribs(0));
+ mxItemSet->Put(aNewSet);
+ }
+
+ std::optional<OutlinerParaObject> pTemp = pOutliner->CreateParaObject(0, nParaCount);
+ pOutliner->Clear();
+ mxCell->SetOutlinerParaObject(std::move(pTemp));
+ }
+
+ }
+ }
+
+ // call parent
+ AttributeProperties::ItemSetChanged(aChangedItems, nDeletedWhich);
+
+ if( mxCell.is() )
+ mxCell->notifyModified();
+ }
+
+ void CellProperties::ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem)
+ {
+ if(pNewItem && (SDRATTR_TEXTDIRECTION == nWhich))
+ {
+ bool bVertical(css::text::WritingMode_TB_RL == static_cast<const SvxWritingModeItem*>(pNewItem)->GetValue());
+
+ sdr::table::SdrTableObj& rObj = static_cast<sdr::table::SdrTableObj&>(GetSdrObject());
+ rObj.SetVerticalWriting(bVertical);
+
+ // Set a cell vertical property
+ std::optional<OutlinerParaObject> pEditParaObj = mxCell->CreateEditOutlinerParaObject();
+
+ if( !pEditParaObj && mxCell->GetOutlinerParaObject() )
+ {
+ OutlinerParaObject* pParaObj = mxCell->GetOutlinerParaObject();
+ if(pParaObj)
+ pParaObj->SetVertical(bVertical);
+ }
+ }
+
+ if (pNewItem && (SDRATTR_TABLE_TEXT_ROTATION == nWhich))
+ {
+ const SvxTextRotateItem* pRotateItem = static_cast<const SvxTextRotateItem*>(pNewItem);
+
+ // Set a cell vertical property
+ std::optional<OutlinerParaObject> pEditParaObj = mxCell->CreateEditOutlinerParaObject();
+
+ if (!pEditParaObj && mxCell->GetOutlinerParaObject())
+ {
+ OutlinerParaObject* pParaObj = mxCell->GetOutlinerParaObject();
+ if (pParaObj)
+ {
+ if(pRotateItem->IsVertical() && pRotateItem->IsTopToBottom())
+ pParaObj->SetRotation(TextRotation::TOPTOBOTTOM);
+ else if (pRotateItem->IsVertical())
+ pParaObj->SetRotation(TextRotation::BOTTOMTOTOP);
+ else
+ pParaObj->SetRotation(TextRotation::NONE);
+ }
+ }
+
+ // Change autogrow direction
+ SdrTextObj& rObj = static_cast<SdrTextObj&>(GetSdrObject());
+
+ // rescue object size
+ tools::Rectangle aObjectRect = rObj.GetSnapRect();
+
+ const SfxItemSet& rSet = rObj.GetObjectItemSet();
+ bool bAutoGrowWidth = rSet.Get(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue();
+ bool bAutoGrowHeight = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
+
+ // prepare ItemSet to set exchanged width and height items
+ SfxItemSetFixed<SDRATTR_TEXT_AUTOGROWHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT> aNewSet(*rSet.GetPool());
+
+ aNewSet.Put(rSet);
+ aNewSet.Put(makeSdrTextAutoGrowWidthItem(bAutoGrowHeight));
+ aNewSet.Put(makeSdrTextAutoGrowHeightItem(bAutoGrowWidth));
+ rObj.SetObjectItemSet(aNewSet);
+
+ // restore object size
+ rObj.SetSnapRect(aObjectRect);
+ }
+
+ // call parent
+ AttributeProperties::ItemChange( nWhich, pNewItem );
+ }
+
+} // end of namespace sdr::properties
+
+namespace sdr::table {
+
+
+// Cell
+
+
+rtl::Reference< Cell > Cell::create( SdrTableObj& rTableObj )
+{
+ rtl::Reference< Cell > xCell( new Cell( rTableObj ) );
+ if( xCell->mxTable.is() )
+ {
+ xCell->mxTable->addEventListener( xCell );
+ }
+ return xCell;
+}
+
+
+Cell::Cell(
+ SdrTableObj& rTableObj)
+: SdrText(rTableObj)
+ ,SvxUnoTextBase( ImplGetSvxUnoOutlinerTextCursorSvxPropertySet() )
+ ,mpPropSet( ImplGetSvxCellPropertySet() )
+ ,mpProperties( new sdr::properties::CellProperties( rTableObj, this ) )
+ ,mnCellContentType( CellContentType_EMPTY )
+ ,mfValue( 0.0 )
+ ,mnError( 0 )
+ ,mbMerged( false )
+ ,mnRowSpan( 1 )
+ ,mnColSpan( 1 )
+ ,mxTable( rTableObj.getTable() )
+{
+ // Caution: Old SetModel() indirectly did a very necessary thing here,
+ // it created a valid SvxTextEditSource which is needed to bind contained
+ // Text to the UNO API and thus to save/load and more. Added version without
+ // model change.
+ // Also done was (not needed, for reference):
+ // SetStyleSheet( nullptr, true );
+ // ForceOutlinerParaObject( OutlinerMode::TextObject );
+ if(nullptr == GetEditSource())
+ {
+ SetEditSource(new SvxTextEditSource(&GetObject(), this));
+ }
+}
+
+Cell::~Cell() COVERITY_NOEXCEPT_FALSE
+{
+ dispose();
+}
+
+void Cell::dispose()
+{
+ if( mxTable.is() )
+ {
+ try
+ {
+ Reference< XEventListener > xThis( this );
+ mxTable->removeEventListener( xThis );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+ mxTable.clear();
+ }
+
+ // tdf#118199 avoid double dispose, detect by using mpProperties
+ // as indicator. Only use SetOutlinerParaObject once
+ if( mpProperties )
+ {
+ mpProperties.reset();
+ SetOutlinerParaObject( std::nullopt );
+ }
+}
+
+void Cell::merge( sal_Int32 nColumnSpan, sal_Int32 nRowSpan )
+{
+ if ((mnColSpan != nColumnSpan) || (mnRowSpan != nRowSpan) || mbMerged)
+ {
+ mnColSpan = nColumnSpan;
+ mnRowSpan = nRowSpan;
+ mbMerged = false;
+ notifyModified();
+ }
+}
+
+
+void Cell::mergeContent( const CellRef& xSourceCell )
+{
+ SdrTableObj& rTableObj = dynamic_cast< SdrTableObj& >( GetObject() );
+
+ if( !xSourceCell->hasText() )
+ return;
+
+ SdrOutliner& rOutliner=rTableObj.ImpGetDrawOutliner();
+ rOutliner.SetUpdateLayout(true);
+
+ if( hasText() )
+ {
+ rOutliner.SetText(*GetOutlinerParaObject());
+ rOutliner.AddText(*xSourceCell->GetOutlinerParaObject());
+ }
+ else
+ {
+ rOutliner.SetText(*xSourceCell->GetOutlinerParaObject());
+ }
+
+ SetOutlinerParaObject( rOutliner.CreateParaObject() );
+ rOutliner.Clear();
+ xSourceCell->SetOutlinerParaObject(rOutliner.CreateParaObject());
+ rOutliner.Clear();
+ SetStyleSheet( GetStyleSheet(), true );
+}
+
+
+void Cell::cloneFrom( const CellRef& xCell )
+{
+ if( xCell.is() )
+ {
+ replaceContentAndFormatting( xCell );
+
+ mnCellContentType = xCell->mnCellContentType;
+
+ msFormula = xCell->msFormula;
+ mfValue = xCell->mfValue;
+ mnError = xCell->mnError;
+
+ mbMerged = xCell->mbMerged;
+ mnRowSpan = xCell->mnRowSpan;
+ mnColSpan = xCell->mnColSpan;
+
+ }
+ notifyModified();
+}
+
+void Cell::replaceContentAndFormatting( const CellRef& xSourceCell )
+{
+ if( !(xSourceCell.is() && mpProperties) )
+ return;
+
+ mpProperties->SetMergedItemSet( xSourceCell->GetObjectItemSet() );
+
+ // tdf#118354 OutlinerParaObject may be nullptr, do not dereference when
+ // not set (!)
+ if(xSourceCell->GetOutlinerParaObject())
+ {
+ SetOutlinerParaObject( *xSourceCell->GetOutlinerParaObject() );
+ }
+
+ SdrTableObj& rTableObj = dynamic_cast< SdrTableObj& >( GetObject() );
+ SdrTableObj& rSourceTableObj = dynamic_cast< SdrTableObj& >( xSourceCell->GetObject() );
+
+ if(&rSourceTableObj.getSdrModelFromSdrObject() != &rTableObj.getSdrModelFromSdrObject())
+ {
+ // TTTT should not happen - if, then a clone may be needed
+ // Maybe add an assertion here later
+ SetStyleSheet( nullptr, true );
+ }
+}
+
+
+void Cell::setMerged()
+{
+ if( !mbMerged )
+ {
+ mbMerged = true;
+ notifyModified();
+ }
+}
+
+
+void Cell::copyFormatFrom( const CellRef& xSourceCell )
+{
+ if( !(xSourceCell.is() && mpProperties) )
+ return;
+
+ mpProperties->SetMergedItemSet( xSourceCell->GetObjectItemSet() );
+ SdrTableObj& rTableObj = dynamic_cast< SdrTableObj& >( GetObject() );
+ SdrTableObj& rSourceTableObj = dynamic_cast< SdrTableObj& >( xSourceCell->GetObject() );
+
+ if(&rSourceTableObj.getSdrModelFromSdrObject() != &rTableObj.getSdrModelFromSdrObject())
+ {
+ // TTTT should not happen - if, then a clone may be needed
+ // Maybe add an assertion here later
+ SetStyleSheet( nullptr, true );
+ }
+
+ notifyModified();
+}
+
+
+void Cell::notifyModified()
+{
+ if( mxTable.is() )
+ mxTable->setModified( true );
+}
+
+
+// SdrTextShape proxy
+
+
+bool Cell::IsActiveCell() const
+{
+ bool isActive = false;
+ SdrTableObj& rTableObj = dynamic_cast< SdrTableObj& >( GetObject() );
+ if( rTableObj.getActiveCell().get() == this )
+ isActive = true;
+
+ return isActive;
+}
+
+bool Cell::IsTextEditActive() const
+{
+ bool isActive = false;
+ SdrTableObj& rTableObj = dynamic_cast< SdrTableObj& >( GetObject() );
+ if(rTableObj.getActiveCell().get() == this )
+ {
+ if( rTableObj.CanCreateEditOutlinerParaObject() )
+ {
+ isActive = true;
+ }
+ }
+ return isActive;
+}
+
+
+bool Cell::hasText() const
+{
+ const OutlinerParaObject* pParaObj = GetOutlinerParaObject();
+ if( pParaObj )
+ {
+ const EditTextObject& rTextObj = pParaObj->GetTextObject();
+ if( rTextObj.GetParagraphCount() >= 1 )
+ {
+ if( rTextObj.GetParagraphCount() == 1 )
+ {
+ if( rTextObj.GetText(0).isEmpty() )
+ return false;
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool Cell::CanCreateEditOutlinerParaObject() const
+{
+ SdrTableObj& rTableObj = dynamic_cast< SdrTableObj& >( GetObject() );
+ if( rTableObj.getActiveCell().get() == this )
+ return rTableObj.CanCreateEditOutlinerParaObject();
+ return false;
+}
+
+std::optional<OutlinerParaObject> Cell::CreateEditOutlinerParaObject() const
+{
+ SdrTableObj& rTableObj = dynamic_cast< SdrTableObj& >( GetObject() );
+ if( rTableObj.getActiveCell().get() == this )
+ return rTableObj.CreateEditOutlinerParaObject();
+ return std::nullopt;
+}
+
+
+void Cell::SetStyleSheet( SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr )
+{
+ // only allow cell styles for cells
+ if( pStyleSheet && pStyleSheet->GetFamily() != SfxStyleFamily::Frame )
+ return;
+
+ if( mpProperties && (mpProperties->GetStyleSheet() != pStyleSheet) )
+ {
+ mpProperties->SetStyleSheet( pStyleSheet, bDontRemoveHardAttr, true );
+ }
+}
+
+
+const SfxItemSet& Cell::GetObjectItemSet()
+{
+ if( mpProperties )
+ {
+ return mpProperties->GetObjectItemSet();
+ }
+ else
+ {
+ OSL_FAIL("Cell::GetObjectItemSet(), called without properties!");
+ return GetObject().GetObjectItemSet();
+ }
+}
+
+void Cell::SetObjectItem(const SfxPoolItem& rItem)
+{
+ if( mpProperties )
+ {
+ mpProperties->SetObjectItem( rItem );
+ notifyModified();
+ }
+}
+
+void Cell::SetMergedItem(const SfxPoolItem& rItem)
+{
+ SetObjectItem(rItem);
+}
+
+SfxStyleSheet* Cell::GetStyleSheet() const
+{
+ if( mpProperties )
+ return mpProperties->GetStyleSheet();
+ else
+ return nullptr;
+}
+
+void Cell::TakeTextAnchorRect(tools::Rectangle& rAnchorRect) const
+{
+ rAnchorRect.SetLeft( maCellRect.Left() + GetTextLeftDistance() );
+ rAnchorRect.SetRight( maCellRect.Right() - GetTextRightDistance() );
+ rAnchorRect.SetTop( maCellRect.Top() + GetTextUpperDistance() );
+ rAnchorRect.SetBottom( maCellRect.Bottom() - GetTextLowerDistance() );
+}
+
+
+void Cell::SetMergedItemSetAndBroadcast(const SfxItemSet& rSet, bool bClearAllItems)
+{
+ if( mpProperties )
+ {
+ mpProperties->SetMergedItemSetAndBroadcast(rSet, bClearAllItems);
+ notifyModified();
+ }
+}
+
+
+sal_Int32 Cell::calcPreferredWidth( const Size aSize )
+{
+ if ( !hasText() )
+ return getMinimumWidth();
+
+ Outliner& rOutliner=static_cast< SdrTableObj& >( GetObject() ).ImpGetDrawOutliner();
+ rOutliner.SetPaperSize(aSize);
+ rOutliner.SetUpdateLayout(true);
+ ForceOutlinerParaObject( OutlinerMode::TextObject );
+
+ if( GetOutlinerParaObject() )
+ rOutliner.SetText(*GetOutlinerParaObject());
+
+ sal_Int32 nPreferredWidth = const_cast<EditEngine&>(rOutliner.GetEditEngine()).CalcTextWidth();
+ rOutliner.Clear();
+
+ return GetTextLeftDistance() + GetTextRightDistance() + nPreferredWidth;
+}
+
+sal_Int32 Cell::getMinimumWidth() const
+{
+ return GetTextLeftDistance() + GetTextRightDistance() + 100;
+}
+
+
+sal_Int32 Cell::getMinimumHeight()
+{
+ if( !mpProperties )
+ return 0;
+
+ SdrTableObj& rTableObj = dynamic_cast< SdrTableObj& >( GetObject() );
+ sal_Int32 nMinimumHeight = 0;
+
+ tools::Rectangle aTextRect;
+ TakeTextAnchorRect( aTextRect );
+ Size aSize( aTextRect.GetSize() );
+ aSize.setHeight(0x0FFFFFFF );
+
+ SdrOutliner* pEditOutliner = rTableObj.GetCellTextEditOutliner( *this );
+ if(pEditOutliner)
+ {
+ pEditOutliner->SetMaxAutoPaperSize(aSize);
+ nMinimumHeight = pEditOutliner->GetTextHeight()+1;
+ }
+ else
+ {
+ Outliner& rOutliner=rTableObj.ImpGetDrawOutliner();
+ rOutliner.SetPaperSize(aSize);
+ rOutliner.SetUpdateLayout(true);
+ ForceOutlinerParaObject( OutlinerMode::TextObject );
+
+ if( GetOutlinerParaObject() )
+ {
+ rOutliner.SetText(*GetOutlinerParaObject());
+ }
+ nMinimumHeight=rOutliner.GetTextHeight()+1;
+ rOutliner.Clear();
+ }
+
+ nMinimumHeight += GetTextUpperDistance() + GetTextLowerDistance();
+ return nMinimumHeight;
+}
+
+
+tools::Long Cell::GetTextLeftDistance() const
+{
+ return GetItemSet().Get(SDRATTR_TEXT_LEFTDIST).GetValue();
+}
+
+
+tools::Long Cell::GetTextRightDistance() const
+{
+ return GetItemSet().Get(SDRATTR_TEXT_RIGHTDIST).GetValue();
+}
+
+
+tools::Long Cell::GetTextUpperDistance() const
+{
+ return GetItemSet().Get(SDRATTR_TEXT_UPPERDIST).GetValue();
+}
+
+
+tools::Long Cell::GetTextLowerDistance() const
+{
+ return GetItemSet().Get(SDRATTR_TEXT_LOWERDIST).GetValue();
+}
+
+
+SdrTextVertAdjust Cell::GetTextVerticalAdjust() const
+{
+ return GetItemSet().Get(SDRATTR_TEXT_VERTADJUST).GetValue();
+}
+
+
+SdrTextHorzAdjust Cell::GetTextHorizontalAdjust() const
+{
+ return GetItemSet().Get(SDRATTR_TEXT_HORZADJUST).GetValue();
+}
+
+
+void Cell::SetOutlinerParaObject( std::optional<OutlinerParaObject> pTextObject )
+{
+ bool bNullTextObject = !pTextObject;
+ SdrText::SetOutlinerParaObject( std::move(pTextObject) );
+ maSelection.nStartPara = EE_PARA_MAX_COUNT;
+
+ if( bNullTextObject )
+ ForceOutlinerParaObject( OutlinerMode::TextObject );
+}
+
+
+void Cell::AddUndo()
+{
+ SdrObject& rObj = GetObject();
+
+ if( rObj.IsInserted() && rObj.getSdrModelFromSdrObject().IsUndoEnabled() )
+ {
+ CellRef xCell( this );
+ rObj.getSdrModelFromSdrObject().AddUndo( std::make_unique<CellUndo>( &rObj, xCell ) );
+
+ // Undo action for the after-text-edit-ended stack.
+ SdrTableObj* pTableObj = dynamic_cast<sdr::table::SdrTableObj*>(&rObj);
+ if (pTableObj && pTableObj->IsTextEditActive())
+ pTableObj->AddUndo(new CellUndo(pTableObj, xCell));
+ }
+}
+
+
+sdr::properties::TextProperties* Cell::CloneProperties( sdr::properties::TextProperties const * pProperties, SdrObject& rNewObj, Cell& rNewCell )
+{
+ if( pProperties )
+ return new sdr::properties::CellProperties( *static_cast<sdr::properties::CellProperties const *>(pProperties), rNewObj, &rNewCell );
+ else
+ return nullptr;
+}
+
+
+sdr::properties::TextProperties* Cell::CloneProperties( SdrObject& rNewObj, Cell& rNewCell )
+{
+ return CloneProperties(mpProperties.get(),rNewObj,rNewCell);
+}
+
+
+// XInterface
+
+
+Any SAL_CALL Cell::queryInterface( const Type & rType )
+{
+ if( rType == cppu::UnoType<XMergeableCell>::get() )
+ return Any( Reference< XMergeableCell >( this ) );
+
+ if( rType == cppu::UnoType<XCell>::get() )
+ return Any( Reference< XCell >( this ) );
+
+ if( rType == cppu::UnoType<XLayoutConstrains>::get() )
+ return Any( Reference< XLayoutConstrains >( this ) );
+
+ if( rType == cppu::UnoType<XEventListener>::get() )
+ return Any( Reference< XEventListener >( this ) );
+
+ Any aRet( SvxUnoTextBase::queryAggregation( rType ) );
+ if( aRet.hasValue() )
+ return aRet;
+
+ return ::cppu::OWeakObject::queryInterface( rType );
+}
+
+
+void SAL_CALL Cell::acquire() noexcept
+{
+ ::cppu::OWeakObject::acquire();
+}
+
+
+void SAL_CALL Cell::release() noexcept
+{
+ ::cppu::OWeakObject::release();
+}
+
+
+// XTypeProvider
+
+
+Sequence< Type > SAL_CALL Cell::getTypes( )
+{
+ return comphelper::concatSequences( SvxUnoTextBase::getTypes(),
+ Sequence {
+ cppu::UnoType<XMergeableCell>::get(),
+ cppu::UnoType<XLayoutConstrains>::get() });
+}
+
+
+Sequence< sal_Int8 > SAL_CALL Cell::getImplementationId( )
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XLayoutConstrains
+css::awt::Size SAL_CALL Cell::getMinimumSize()
+{
+ return css::awt::Size( getMinimumWidth(), getMinimumHeight() );
+}
+
+
+css::awt::Size SAL_CALL Cell::getPreferredSize()
+{
+ return getMinimumSize();
+}
+
+
+css::awt::Size SAL_CALL Cell::calcAdjustedSize( const css::awt::Size& aNewSize )
+{
+ return aNewSize;
+}
+
+
+// XMergeableCell
+
+
+sal_Int32 SAL_CALL Cell::getRowSpan()
+{
+ return mnRowSpan;
+}
+
+
+sal_Int32 SAL_CALL Cell::getColumnSpan()
+{
+ return mnColSpan;
+}
+
+
+sal_Bool SAL_CALL Cell::isMerged()
+{
+ return mbMerged;
+}
+
+
+// XCell
+
+
+OUString SAL_CALL Cell::getFormula( )
+{
+ return msFormula;
+}
+
+
+void SAL_CALL Cell::setFormula( const OUString& aFormula )
+{
+ if( msFormula != aFormula )
+ {
+ msFormula = aFormula;
+ }
+}
+
+
+double SAL_CALL Cell::getValue( )
+{
+ return mfValue;
+}
+
+
+void SAL_CALL Cell::setValue( double nValue )
+{
+ if( mfValue != nValue )
+ {
+ mfValue = nValue;
+ mnCellContentType = CellContentType_VALUE;
+ }
+}
+
+
+CellContentType SAL_CALL Cell::getType()
+{
+ return mnCellContentType;
+}
+
+
+sal_Int32 SAL_CALL Cell::getError( )
+{
+ return mnError;
+}
+
+
+// XPropertySet
+
+
+Any Cell::GetAnyForItem( SfxItemSet const & aSet, const SfxItemPropertyMapEntry* pMap )
+{
+ Any aAny( SvxItemPropertySet_getPropertyValue( pMap, aSet ) );
+
+ if( pMap->aType != aAny.getValueType() )
+ {
+ // since the sfx uint16 item now exports a sal_Int32, we may have to fix this here
+ if( ( pMap->aType == ::cppu::UnoType<sal_Int16>::get()) && aAny.getValueType() == ::cppu::UnoType<sal_Int32>::get() )
+ {
+ sal_Int32 nValue = 0;
+ aAny >>= nValue;
+ aAny <<= static_cast<sal_Int16>(nValue);
+ }
+ else
+ {
+ OSL_FAIL("GetAnyForItem() Returnvalue has wrong Type!" );
+ }
+ }
+
+ return aAny;
+}
+
+Reference< XPropertySetInfo > SAL_CALL Cell::getPropertySetInfo()
+{
+ return mpPropSet->getPropertySetInfo();
+}
+
+
+void SAL_CALL Cell::setPropertyValue( const OUString& rPropertyName, const Any& rValue )
+{
+ ::SolarMutexGuard aGuard;
+
+ if(mpProperties == nullptr)
+ throw DisposedException();
+
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry(rPropertyName);
+ if( pMap )
+ {
+ if( (pMap->nFlags & PropertyAttribute::READONLY ) != 0 )
+ throw PropertyVetoException();
+
+ switch( pMap->nWID )
+ {
+ case OWN_ATTR_STYLE:
+ {
+ Reference< XStyle > xStyle;
+ if( !( rValue >>= xStyle ) )
+ throw IllegalArgumentException();
+
+ SfxUnoStyleSheet* pStyle = SfxUnoStyleSheet::getUnoStyleSheet(xStyle);
+ SetStyleSheet( pStyle, true );
+ return;
+ }
+ case OWN_ATTR_TABLEBORDER:
+ {
+ auto pBorder = o3tl::tryAccess<TableBorder>(rValue);
+ if(!pBorder)
+ break;
+
+ SvxBoxItem aBox( SDRATTR_TABLE_BORDER );
+ SvxBoxInfoItem aBoxInfo( SDRATTR_TABLE_BORDER_INNER );
+ SvxBorderLine aLine;
+
+ bool bSet = SvxBoxItem::LineToSvxLine(pBorder->TopLine, aLine, false);
+ aBox.SetLine(bSet ? &aLine : nullptr, SvxBoxItemLine::TOP);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::TOP, pBorder->IsTopLineValid);
+
+ bSet = SvxBoxItem::LineToSvxLine(pBorder->BottomLine, aLine, false);
+ aBox.SetLine(bSet ? &aLine : nullptr, SvxBoxItemLine::BOTTOM);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::BOTTOM, pBorder->IsBottomLineValid);
+
+ bSet = SvxBoxItem::LineToSvxLine(pBorder->LeftLine, aLine, false);
+ aBox.SetLine(bSet ? &aLine : nullptr, SvxBoxItemLine::LEFT);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::LEFT, pBorder->IsLeftLineValid);
+
+ bSet = SvxBoxItem::LineToSvxLine(pBorder->RightLine, aLine, false);
+ aBox.SetLine(bSet ? &aLine : nullptr, SvxBoxItemLine::RIGHT);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::RIGHT, pBorder->IsRightLineValid);
+
+ bSet = SvxBoxItem::LineToSvxLine(pBorder->HorizontalLine, aLine, false);
+ aBoxInfo.SetLine(bSet ? &aLine : nullptr, SvxBoxInfoItemLine::HORI);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI, pBorder->IsHorizontalLineValid);
+
+ bSet = SvxBoxItem::LineToSvxLine(pBorder->VerticalLine, aLine, false);
+ aBoxInfo.SetLine(bSet ? &aLine : nullptr, SvxBoxInfoItemLine::VERT);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::VERT, pBorder->IsVerticalLineValid);
+
+ aBox.SetAllDistances(pBorder->Distance); //TODO
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::DISTANCE, pBorder->IsDistanceValid);
+
+ mpProperties->SetObjectItem(aBox);
+ mpProperties->SetObjectItem(aBoxInfo);
+ return;
+ }
+ case OWN_ATTR_FILLBMP_MODE:
+ {
+ BitmapMode eMode;
+ if(!(rValue >>= eMode) )
+ {
+ sal_Int32 nMode = 0;
+ if(!(rValue >>= nMode))
+ throw IllegalArgumentException();
+
+ eMode = static_cast<BitmapMode>(nMode);
+ }
+
+ mpProperties->SetObjectItem( XFillBmpStretchItem( eMode == BitmapMode_STRETCH ) );
+ mpProperties->SetObjectItem( XFillBmpTileItem( eMode == BitmapMode_REPEAT ) );
+ return;
+ }
+ case SDRATTR_TABLE_TEXT_ROTATION:
+ {
+ sal_Int32 nRotVal = 0;
+ if (!(rValue >>= nRotVal))
+ throw IllegalArgumentException();
+
+ if (nRotVal != 27000 && nRotVal != 9000 && nRotVal != 0)
+ throw IllegalArgumentException();
+
+ mpProperties->SetObjectItem(SvxTextRotateItem(Degree10(nRotVal/10), SDRATTR_TABLE_TEXT_ROTATION));
+ return;
+ }
+ default:
+ {
+ SfxItemSet aSet(GetObject().getSdrModelFromSdrObject().GetItemPool(), pMap->nWID, pMap->nWID);
+ aSet.Put(mpProperties->GetItem(pMap->nWID));
+
+ bool bSpecial = false;
+
+ switch( pMap->nWID )
+ {
+ case XATTR_FILLBITMAP:
+ case XATTR_FILLGRADIENT:
+ case XATTR_FILLHATCH:
+ case XATTR_FILLFLOATTRANSPARENCE:
+ case XATTR_LINEEND:
+ case XATTR_LINESTART:
+ case XATTR_LINEDASH:
+ {
+ if( pMap->nMemberId == MID_NAME )
+ {
+ OUString aApiName;
+ if( rValue >>= aApiName )
+ {
+ if(SvxShape::SetFillAttribute(pMap->nWID, aApiName, aSet, &GetObject().getSdrModelFromSdrObject()))
+ bSpecial = true;
+ }
+ }
+ }
+ break;
+ }
+
+ if( !bSpecial )
+ {
+
+ if( !SvxUnoTextRangeBase::SetPropertyValueHelper( pMap, rValue, aSet ))
+ {
+ if( aSet.GetItemState( pMap->nWID ) != SfxItemState::SET )
+ {
+ // fetch the default from ItemPool
+ if(SfxItemPool::IsWhich(pMap->nWID))
+ aSet.Put(GetObject().getSdrModelFromSdrObject().GetItemPool().GetDefaultItem(pMap->nWID));
+ }
+
+ if( aSet.GetItemState( pMap->nWID ) == SfxItemState::SET )
+ {
+ SvxItemPropertySet_setPropertyValue( pMap, rValue, aSet );
+ }
+ }
+ }
+
+ GetObject().getSdrModelFromSdrObject().SetChanged();
+ mpProperties->SetMergedItemSetAndBroadcast( aSet );
+ return;
+ }
+ }
+ }
+ throw UnknownPropertyException( rPropertyName, static_cast<cppu::OWeakObject*>(this));
+}
+
+
+Any SAL_CALL Cell::getPropertyValue( const OUString& PropertyName )
+{
+ ::SolarMutexGuard aGuard;
+
+ if(mpProperties == nullptr)
+ throw DisposedException();
+
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry(PropertyName);
+ if( pMap )
+ {
+ switch( pMap->nWID )
+ {
+ case OWN_ATTR_STYLE:
+ {
+ return Any( Reference< XStyle >( dynamic_cast< SfxUnoStyleSheet* >( GetStyleSheet() ) ) );
+ }
+ case OWN_ATTR_TABLEBORDER:
+ {
+ const SvxBoxInfoItem& rBoxInfoItem = mpProperties->GetItem(SDRATTR_TABLE_BORDER_INNER);
+ const SvxBoxItem& rBox = mpProperties->GetItem(SDRATTR_TABLE_BORDER);
+
+ TableBorder aTableBorder;
+ aTableBorder.TopLine = SvxBoxItem::SvxLineToLine(rBox.GetTop(), false);
+ aTableBorder.IsTopLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::TOP);
+ aTableBorder.BottomLine = SvxBoxItem::SvxLineToLine(rBox.GetBottom(), false);
+ aTableBorder.IsBottomLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::BOTTOM);
+ aTableBorder.LeftLine = SvxBoxItem::SvxLineToLine(rBox.GetLeft(), false);
+ aTableBorder.IsLeftLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::LEFT);
+ aTableBorder.RightLine = SvxBoxItem::SvxLineToLine(rBox.GetRight(), false);
+ aTableBorder.IsRightLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::RIGHT );
+ aTableBorder.HorizontalLine = SvxBoxItem::SvxLineToLine(rBoxInfoItem.GetHori(), false);
+ aTableBorder.IsHorizontalLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::HORI);
+ aTableBorder.VerticalLine = SvxBoxItem::SvxLineToLine(rBoxInfoItem.GetVert(), false);
+ aTableBorder.IsVerticalLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::VERT);
+ aTableBorder.Distance = rBox.GetSmallestDistance();
+ aTableBorder.IsDistanceValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::DISTANCE);
+
+ return Any( aTableBorder );
+ }
+ case OWN_ATTR_FILLBMP_MODE:
+ {
+ const XFillBmpStretchItem& rStretchItem = mpProperties->GetItem(XATTR_FILLBMP_STRETCH);
+ const XFillBmpTileItem& rTileItem = mpProperties->GetItem(XATTR_FILLBMP_TILE);
+ if( rTileItem.GetValue() )
+ {
+ return Any( BitmapMode_REPEAT );
+ }
+ else if( rStretchItem.GetValue() )
+ {
+ return Any( BitmapMode_STRETCH );
+ }
+ else
+ {
+ return Any( BitmapMode_NO_REPEAT );
+ }
+ }
+ case SDRATTR_TABLE_TEXT_ROTATION:
+ {
+ const SvxTextRotateItem& rTextRotate = mpProperties->GetItem(SDRATTR_TABLE_TEXT_ROTATION);
+ return Any(sal_Int32(to<Degree100>(rTextRotate.GetValue())));
+ }
+ default:
+ {
+ SfxItemSet aSet(GetObject().getSdrModelFromSdrObject().GetItemPool(), pMap->nWID, pMap->nWID);
+ aSet.Put(mpProperties->GetItem(pMap->nWID));
+
+ Any aAny;
+ if(!SvxUnoTextRangeBase::GetPropertyValueHelper( aSet, pMap, aAny ))
+ {
+ if(!aSet.Count())
+ {
+ // fetch the default from ItemPool
+ if(SfxItemPool::IsWhich(pMap->nWID))
+ aSet.Put(GetObject().getSdrModelFromSdrObject().GetItemPool().GetDefaultItem(pMap->nWID));
+ }
+
+ if( aSet.Count() )
+ aAny = GetAnyForItem( aSet, pMap );
+ }
+
+ return aAny;
+ }
+ }
+ }
+ throw UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this));
+}
+
+
+void SAL_CALL Cell::addPropertyChangeListener( const OUString& /*aPropertyName*/, const Reference< XPropertyChangeListener >& /*xListener*/ )
+{
+}
+
+
+void SAL_CALL Cell::removePropertyChangeListener( const OUString& /*aPropertyName*/, const Reference< XPropertyChangeListener >& /*aListener*/ )
+{
+}
+
+
+void SAL_CALL Cell::addVetoableChangeListener( const OUString& /*PropertyName*/, const Reference< XVetoableChangeListener >& /*aListener*/ )
+{
+}
+
+
+void SAL_CALL Cell::removeVetoableChangeListener( const OUString& /*PropertyName*/, const Reference< XVetoableChangeListener >& /*aListener*/ )
+{
+}
+
+
+// XMultiPropertySet
+
+
+void SAL_CALL Cell::setPropertyValues( const Sequence< OUString >& aPropertyNames, const Sequence< Any >& aValues )
+{
+ ::SolarMutexGuard aSolarGuard;
+
+ if(mpProperties == nullptr)
+ throw DisposedException();
+
+ const sal_Int32 nCount = aPropertyNames.getLength();
+
+ const OUString* pNames = aPropertyNames.getConstArray();
+ const Any* pValues = aValues.getConstArray();
+
+ for( sal_Int32 nIdx = 0; nIdx < nCount; nIdx++, pNames++, pValues++ )
+ {
+ try
+ {
+ setPropertyValue( *pNames, *pValues );
+ }
+ catch( UnknownPropertyException& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "unknown property!");
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+ }
+}
+
+
+Sequence< Any > SAL_CALL Cell::getPropertyValues( const Sequence< OUString >& aPropertyNames )
+{
+ ::SolarMutexGuard aSolarGuard;
+
+ if(mpProperties == nullptr)
+ throw DisposedException();
+
+ const sal_Int32 nCount = aPropertyNames.getLength();
+ Sequence< Any > aRet( nCount );
+ Any* pValue = aRet.getArray();
+
+ for( const OUString& rName : aPropertyNames )
+ {
+ try
+ {
+ *pValue = getPropertyValue( rName );
+ }
+ catch( UnknownPropertyException& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "unknown property!");
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+ pValue++;
+ }
+
+ return aRet;
+}
+
+
+void SAL_CALL Cell::addPropertiesChangeListener( const Sequence< OUString >& /*aPropertyNames*/, const Reference< XPropertiesChangeListener >& /*xListener*/ )
+{
+}
+
+
+void SAL_CALL Cell::removePropertiesChangeListener( const Reference< XPropertiesChangeListener >& /*xListener*/ )
+{
+}
+
+
+void SAL_CALL Cell::firePropertiesChangeEvent( const Sequence< OUString >& /*aPropertyNames*/, const Reference< XPropertiesChangeListener >& /*xListener*/ )
+{
+}
+
+
+// XPropertyState
+
+
+PropertyState SAL_CALL Cell::getPropertyState( const OUString& PropertyName )
+{
+ ::SolarMutexGuard aGuard;
+
+ if(mpProperties == nullptr)
+ throw DisposedException();
+
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry(PropertyName);
+
+ if( pMap )
+ {
+ PropertyState eState;
+ switch( pMap->nWID )
+ {
+ case OWN_ATTR_FILLBMP_MODE:
+ {
+ const SfxItemSet& rSet = mpProperties->GetMergedItemSet();
+
+ const bool bStretch = rSet.GetItemState( XATTR_FILLBMP_STRETCH, false ) == SfxItemState::SET;
+ const bool bTile = rSet.GetItemState( XATTR_FILLBMP_TILE, false ) == SfxItemState::SET;
+ if( bStretch || bTile )
+ {
+ eState = PropertyState_DIRECT_VALUE;
+ }
+ else
+ {
+ eState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ }
+ case OWN_ATTR_STYLE:
+ {
+ return PropertyState_DIRECT_VALUE;
+ }
+ case OWN_ATTR_TABLEBORDER:
+ {
+ const SfxItemSet& rSet = mpProperties->GetMergedItemSet();
+ if( (rSet.GetItemState( SDRATTR_TABLE_BORDER_INNER, false ) == SfxItemState::DEFAULT) && (rSet.GetItemState( SDRATTR_TABLE_BORDER, false ) == SfxItemState::DEFAULT) )
+ return PropertyState_DEFAULT_VALUE;
+
+ return PropertyState_DIRECT_VALUE;
+ }
+ default:
+ {
+ const SfxItemSet& rSet = mpProperties->GetMergedItemSet();
+
+ switch( rSet.GetItemState( pMap->nWID, false ) )
+ {
+ case SfxItemState::SET:
+ eState = PropertyState_DIRECT_VALUE;
+ break;
+ case SfxItemState::DEFAULT:
+ eState = PropertyState_DEFAULT_VALUE;
+ break;
+ default:
+ eState = PropertyState_AMBIGUOUS_VALUE;
+ break;
+ }
+
+ // if an item is set, this doesn't mean we want it :)
+ if( PropertyState_DIRECT_VALUE == eState )
+ {
+ switch( pMap->nWID )
+ {
+ // the following items are disabled by changing the
+ // fill style or the line style. so there is no need
+ // to export items without names which should be empty
+ case XATTR_FILLBITMAP:
+ case XATTR_FILLGRADIENT:
+ case XATTR_FILLHATCH:
+ case XATTR_LINEDASH:
+ {
+ const NameOrIndex* pItem = rSet.GetItem<NameOrIndex>(pMap->nWID);
+ if( ( pItem == nullptr ) || pItem->GetName().isEmpty() )
+ eState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+
+ // #i36115#
+ // If e.g. the LineStart is on NONE and thus the string has length 0, it still
+ // may be a hard attribute covering the set LineStart of the parent (Style).
+ // #i37644#
+ // same is for fill float transparency
+ case XATTR_LINEEND:
+ case XATTR_LINESTART:
+ case XATTR_FILLFLOATTRANSPARENCE:
+ {
+ const NameOrIndex* pItem = rSet.GetItem<NameOrIndex>(pMap->nWID);
+ if( pItem == nullptr )
+ eState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case XATTR_FILLCOLOR:
+ if (pMap->nMemberId == MID_COLOR_THEME_INDEX)
+ {
+ const XFillColorItem* pColor = rSet.GetItem<XFillColorItem>(pMap->nWID);
+ if (pColor->GetThemeColor().GetThemeIndex() == -1)
+ {
+ eState = PropertyState_DEFAULT_VALUE;
+ }
+ }
+ else if (pMap->nMemberId == MID_COLOR_LUM_MOD)
+ {
+ const XFillColorItem* pColor = rSet.GetItem<XFillColorItem>(pMap->nWID);
+ if (pColor->GetThemeColor().GetLumMod() == 10000)
+ {
+ eState = PropertyState_DEFAULT_VALUE;
+ }
+ }
+ else if (pMap->nMemberId == MID_COLOR_LUM_OFF)
+ {
+ const XFillColorItem* pColor = rSet.GetItem<XFillColorItem>(pMap->nWID);
+ if (pColor->GetThemeColor().GetLumOff() == 0)
+ {
+ eState = PropertyState_DEFAULT_VALUE;
+ }
+ }
+ }
+ }
+ }
+ }
+ return eState;
+ }
+ throw UnknownPropertyException(PropertyName);
+}
+
+
+Sequence< PropertyState > SAL_CALL Cell::getPropertyStates( const Sequence< OUString >& aPropertyName )
+{
+ ::SolarMutexGuard aGuard;
+
+ if(mpProperties == nullptr)
+ throw DisposedException();
+
+ const sal_Int32 nCount = aPropertyName.getLength();
+ Sequence< PropertyState > aRet( nCount );
+
+ std::transform(aPropertyName.begin(), aPropertyName.end(), aRet.getArray(),
+ [this](const OUString& rName) -> PropertyState {
+ try
+ {
+ return getPropertyState( rName );
+ }
+ catch( Exception& )
+ {
+ return PropertyState_AMBIGUOUS_VALUE;
+ }
+ });
+
+ return aRet;
+}
+
+
+void SAL_CALL Cell::setPropertyToDefault( const OUString& PropertyName )
+{
+ ::SolarMutexGuard aGuard;
+
+ if(mpProperties == nullptr)
+ throw DisposedException();
+
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry(PropertyName);
+ if( pMap )
+ {
+ switch( pMap->nWID )
+ {
+ case OWN_ATTR_FILLBMP_MODE:
+ {
+ mpProperties->ClearObjectItem( XATTR_FILLBMP_STRETCH );
+ mpProperties->ClearObjectItem( XATTR_FILLBMP_TILE );
+ break;
+ }
+ case OWN_ATTR_STYLE:
+ break;
+
+ case OWN_ATTR_TABLEBORDER:
+ {
+ mpProperties->ClearObjectItem( SDRATTR_TABLE_BORDER_INNER );
+ mpProperties->ClearObjectItem( SDRATTR_TABLE_BORDER );
+ break;
+ }
+
+ default:
+ {
+ mpProperties->ClearObjectItem( pMap->nWID );
+ }
+ }
+
+ GetObject().getSdrModelFromSdrObject().SetChanged();
+ return;
+ }
+ throw UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this));
+}
+
+
+Any SAL_CALL Cell::getPropertyDefault( const OUString& aPropertyName )
+{
+ ::SolarMutexGuard aGuard;
+
+ if(mpProperties == nullptr)
+ throw DisposedException();
+
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry(aPropertyName);
+ if( pMap )
+ {
+ switch( pMap->nWID )
+ {
+ case OWN_ATTR_FILLBMP_MODE:
+ return Any( BitmapMode_NO_REPEAT );
+
+ case OWN_ATTR_STYLE:
+ {
+ Reference< XStyle > xStyle;
+ return Any( xStyle );
+ }
+
+ case OWN_ATTR_TABLEBORDER:
+ {
+ TableBorder aBorder;
+ return Any( aBorder );
+ }
+
+ default:
+ {
+ if( SfxItemPool::IsWhich(pMap->nWID) )
+ {
+ SfxItemSet aSet(GetObject().getSdrModelFromSdrObject().GetItemPool(), pMap->nWID, pMap->nWID);
+ aSet.Put(GetObject().getSdrModelFromSdrObject().GetItemPool().GetDefaultItem(pMap->nWID));
+ return GetAnyForItem( aSet, pMap );
+ }
+ }
+ }
+ }
+ throw UnknownPropertyException( aPropertyName, static_cast<cppu::OWeakObject*>(this));
+}
+
+
+// XMultiPropertyStates
+
+
+void SAL_CALL Cell::setAllPropertiesToDefault()
+{
+ mpProperties.reset(new sdr::properties::CellProperties( static_cast< SdrTableObj& >( GetObject() ), this ));
+
+ SdrOutliner& rOutliner = GetObject().ImpGetDrawOutliner();
+
+ OutlinerParaObject* pParaObj = GetOutlinerParaObject();
+ if( !pParaObj )
+ return;
+
+ rOutliner.SetText(*pParaObj);
+ sal_Int32 nParaCount(rOutliner.GetParagraphCount());
+
+ if(nParaCount)
+ {
+ ESelection aSelection( 0, 0, EE_PARA_ALL, EE_TEXTPOS_ALL);
+ rOutliner.RemoveAttribs(aSelection, true, 0);
+
+ std::optional<OutlinerParaObject> pTemp = rOutliner.CreateParaObject(0, nParaCount);
+ rOutliner.Clear();
+
+ SetOutlinerParaObject(std::move(pTemp));
+ }
+}
+
+
+void SAL_CALL Cell::setPropertiesToDefault( const Sequence< OUString >& aPropertyNames )
+{
+ for(const OUString& rName : aPropertyNames)
+ setPropertyToDefault( rName );
+}
+
+
+Sequence< Any > SAL_CALL Cell::getPropertyDefaults( const Sequence< OUString >& aPropertyNames )
+{
+ sal_Int32 nCount = aPropertyNames.getLength();
+ Sequence< Any > aDefaults( nCount );
+
+ std::transform(aPropertyNames.begin(), aPropertyNames.end(), aDefaults.getArray(),
+ [this](const OUString& rName) -> Any { return getPropertyDefault(rName); });
+
+ return aDefaults;
+}
+
+
+// XText
+
+
+void SAL_CALL Cell::insertTextContent( const Reference< XTextRange >& xRange, const Reference< XTextContent >& xContent, sal_Bool bAbsorb )
+{
+ SvxUnoTextBase::insertTextContent( xRange, xContent, bAbsorb );
+ notifyModified();
+}
+
+
+void SAL_CALL Cell::removeTextContent( const Reference< XTextContent >& xContent )
+{
+ SvxUnoTextBase::removeTextContent( xContent );
+ notifyModified();
+}
+
+
+// XSimpleText
+
+
+void SAL_CALL Cell::insertString( const Reference< XTextRange >& xRange, const OUString& aString, sal_Bool bAbsorb )
+{
+ SvxUnoTextBase::insertString( xRange, aString, bAbsorb );
+ notifyModified();
+}
+
+
+void SAL_CALL Cell::insertControlCharacter( const Reference< XTextRange >& xRange, sal_Int16 nControlCharacter, sal_Bool bAbsorb )
+{
+ SvxUnoTextBase::insertControlCharacter( xRange, nControlCharacter, bAbsorb );
+ notifyModified();
+}
+
+
+// XTextRange
+
+
+OUString SAL_CALL Cell::getString( )
+{
+ maSelection.nStartPara = EE_PARA_MAX_COUNT;
+ return SvxUnoTextBase::getString();
+}
+
+
+void SAL_CALL Cell::setString( const OUString& aString )
+{
+ SvxUnoTextBase::setString( aString );
+ notifyModified();
+}
+
+// XEventListener
+void SAL_CALL Cell::disposing( const EventObject& /*Source*/ )
+{
+ mxTable.clear();
+ dispose();
+}
+
+void Cell::dumpAsXml(xmlTextWriterPtr pWriter, sal_Int32 nRow, sal_Int32 nCol) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("Cell"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("row"), "%" SAL_PRIdINT32, nRow);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("col"), "%" SAL_PRIdINT32, nCol);
+ SdrText::dumpAsXml(pWriter);
+ //SvxUnoTextBase::dumpAsXml(pWriter);
+ //mpPropSet->dumpAsXml(pWriter);
+ mpProperties->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/cellcursor.cxx b/svx/source/table/cellcursor.cxx
new file mode 100644
index 000000000..313ec63df
--- /dev/null
+++ b/svx/source/table/cellcursor.cxx
@@ -0,0 +1,546 @@
+/* -*- 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 <sal/config.h>
+
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <svx/svdotable.hxx>
+#include "cellcursor.hxx"
+#include "tablelayouter.hxx"
+#include <cell.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::table;
+
+
+namespace sdr::table {
+
+CellCursor::CellCursor( const TableModelRef & xTable, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
+: CellCursorBase( xTable, nLeft, nTop, nRight, nBottom )
+{
+}
+
+
+CellCursor::~CellCursor()
+{
+}
+
+
+// XCellCursor
+
+
+Reference< XCell > SAL_CALL CellCursor::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow )
+{
+ return CellRange::getCellByPosition( nColumn, nRow );
+}
+
+
+Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
+{
+ return CellRange::getCellRangeByPosition( nLeft, nTop, nRight, nBottom );
+}
+
+
+Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByName( const OUString& aRange )
+{
+ return CellRange::getCellRangeByName( aRange );
+}
+
+
+// XCellCursor
+
+
+void SAL_CALL CellCursor::gotoStart( )
+{
+ mnRight = mnLeft;
+ mnBottom = mnTop;
+}
+
+
+void SAL_CALL CellCursor::gotoEnd( )
+{
+ mnLeft = mnRight;
+ mnTop = mnBottom;
+}
+
+
+void SAL_CALL CellCursor::gotoNext( )
+{
+ if( mxTable.is() )
+ {
+ mnRight++;
+ if( mnRight >= mxTable->getColumnCount() )
+ {
+ // if we past the last column, try skip to the row line
+ mnTop++;
+ if( mnTop >= mxTable->getRowCount() )
+ {
+ // if we past the last row, do not move cursor at all
+ mnTop--;
+ mnRight--;
+ }
+ else
+ {
+ // restart at the first column on the next row
+ mnRight = 0;
+ }
+ }
+ }
+
+ mnLeft = mnRight;
+ mnTop = mnBottom;
+}
+
+
+void SAL_CALL CellCursor::gotoPrevious( )
+{
+ if( mxTable.is() )
+ {
+ if( mnLeft > 0 )
+ {
+ --mnLeft;
+ }
+ else if( mnTop > 0 )
+ {
+ --mnTop;
+ mnLeft = mxTable->getColumnCount() - 1;
+ }
+ }
+
+ mnRight = mnLeft;
+ mnBottom = mnTop;
+}
+
+
+void SAL_CALL CellCursor::gotoOffset( ::sal_Int32 nColumnOffset, ::sal_Int32 nRowOffset )
+{
+ if( mxTable.is() )
+ {
+ const sal_Int32 nLeft = mnLeft + nColumnOffset;
+ if( (nLeft >= 0) && (nLeft < mxTable->getColumnCount() ) )
+ mnRight = mnLeft = nLeft;
+
+ const sal_Int32 nTop = mnTop + nRowOffset;
+ if( (nTop >= 0) && (nTop < mxTable->getRowCount()) )
+ mnTop = mnBottom = nTop;
+ }
+}
+
+
+// XMergeableCellCursor
+
+
+/** returns true and the merged cell positions if a merge is valid or false if a merge is
+ not valid for that range */
+bool CellCursor::GetMergedSelection( CellPos& rStart, CellPos& rEnd )
+{
+ rStart.mnCol = mnLeft; rStart.mnRow = mnTop;
+ rEnd.mnCol = mnRight; rEnd.mnRow = mnBottom;
+
+ // single cell merge is never valid
+ if( mxTable.is() && ((mnLeft != mnRight) || (mnTop != mnBottom)) ) try
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( mnLeft, mnTop ).get() ) );
+
+ // check if first cell is merged
+ if( xCell.is() && xCell->isMerged() )
+ findMergeOrigin( mxTable, mnLeft, mnTop, rStart.mnCol, rStart.mnRow );
+
+ // check if last cell is merged
+ xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( mnRight, mnBottom ).get() ) );
+ if( xCell.is() )
+ {
+ if( xCell->isMerged() )
+ {
+ findMergeOrigin( mxTable, mnRight, mnBottom, rEnd.mnCol, rEnd.mnRow );
+ // merge not possible if selection is only one cell and all its merges
+ if( rEnd == rStart )
+ return false;
+ xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rEnd.mnCol, rEnd.mnRow ).get() ) );
+ }
+ }
+ if( xCell.is() )
+ {
+ rEnd.mnCol += xCell->getColumnSpan()-1;
+ rEnd.mnRow += xCell->getRowSpan()-1;
+ }
+
+ // now check if everything is inside the given bounds
+ sal_Int32 nRow, nCol;
+ for( nRow = rStart.mnRow; nRow <= rEnd.mnRow; nRow++ )
+ {
+ for( nCol = rStart.mnCol; nCol <= rEnd.mnCol; nCol++ )
+ {
+ xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( !xCell.is() )
+ continue;
+
+ if( xCell->isMerged() )
+ {
+ sal_Int32 nOriginCol, nOriginRow;
+ if( findMergeOrigin( mxTable, nCol, nRow, nOriginCol, nOriginRow ) )
+ {
+ if( (nOriginCol < rStart.mnCol) || (nOriginRow < rStart.mnRow) )
+ return false;
+
+ xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( nOriginCol, nOriginRow ).get() ) );
+ if( xCell.is() )
+ {
+ nOriginCol += xCell->getColumnSpan()-1;
+ nOriginRow += xCell->getRowSpan()-1;
+
+ if( (nOriginCol > rEnd.mnCol) || (nOriginRow > rEnd.mnRow) )
+ return false;
+ }
+ }
+ }
+ else if( ((nCol + xCell->getColumnSpan() - 1) > rEnd.mnCol) || ((nRow + xCell->getRowSpan() - 1 ) > rEnd.mnRow) )
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+ return false;
+}
+
+
+void SAL_CALL CellCursor::merge( )
+{
+ CellPos aStart, aEnd;
+ if( !GetMergedSelection( aStart, aEnd ) )
+ throw NoSupportException();
+
+ if( !mxTable.is() || (mxTable->getSdrTableObj() == nullptr) )
+ throw DisposedException();
+
+ SdrModel& rModel(mxTable->getSdrTableObj()->getSdrModelFromSdrObject());
+ const bool bUndo(mxTable->getSdrTableObj()->IsInserted() && rModel.IsUndoEnabled());
+
+ if( bUndo )
+ rModel.BegUndo( SvxResId(STR_TABLE_MERGE) );
+
+ try
+ {
+ mxTable->merge( aStart.mnCol, aStart.mnRow, aEnd.mnCol - aStart.mnCol + 1, aEnd.mnRow - aStart.mnRow + 1 );
+ mxTable->optimize();
+ mxTable->setModified(true);
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ rModel.SetChanged();
+}
+
+
+void CellCursor::split_column( sal_Int32 nCol, sal_Int32 nColumns, std::vector< sal_Int32 >& rLeftOvers )
+{
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+
+ sal_Int32 nNewCols = 0, nRow;
+
+ // first check how many columns we need to add
+ for( nRow = mnTop; nRow <= mnBottom; ++nRow )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() && !xCell->isMerged() )
+ nNewCols = std::max( nNewCols, nColumns - xCell->getColumnSpan() + 1 - rLeftOvers[nRow] );
+ }
+
+ if( nNewCols > 0 )
+ {
+ static const OUStringLiteral sWidth(u"Width");
+ Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_SET_THROW );
+ Reference< XPropertySet > xRefColumn( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
+ sal_Int32 nWidth = 0;
+ xRefColumn->getPropertyValue( sWidth ) >>= nWidth;
+ const sal_Int32 nNewWidth = nWidth / (nNewCols + 1);
+
+ // reference column gets new width + rounding errors
+ xRefColumn->setPropertyValue( sWidth, Any( nWidth - (nNewWidth * nNewCols) ) );
+
+ xCols->insertByIndex( nCol + 1, nNewCols );
+ mnRight += nNewCols;
+
+ // distribute new width
+ for( sal_Int32 nNewCol = nCol + nNewCols; nNewCol > nCol; --nNewCol )
+ {
+ Reference< XPropertySet > xNewCol( xCols->getByIndex( nNewCol ), UNO_QUERY_THROW );
+ xNewCol->setPropertyValue( sWidth, Any( nNewWidth ) );
+ }
+ }
+
+ for( nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( !xCell.is() || xCell->isMerged() )
+ {
+ if( nNewCols > 0 )
+ {
+ // merged cells are ignored, but newly added columns will be added to leftovers
+ xCell.set( dynamic_cast< Cell* >(mxTable->getCellByPosition( nCol+1, nRow ).get() ) );
+ if( !xCell.is() || !xCell->isMerged() )
+ rLeftOvers[nRow] += nNewCols;
+ }
+ }
+ else
+ {
+ sal_Int32 nRowSpan = xCell->getRowSpan() - 1;
+ sal_Int32 nColSpan = xCell->getColumnSpan() - 1;
+
+ if( (nRow >= mnTop) && (nRow <= mnBottom) )
+ {
+ sal_Int32 nCellsAvailable = 1 + nColSpan + rLeftOvers[nRow];
+ if( nColSpan == 0 )
+ nCellsAvailable += nNewCols;
+
+ DBG_ASSERT( nCellsAvailable > nColumns, "sdr::table::CellCursor::split_column(), somethings wrong" );
+
+ sal_Int32 nSplitSpan = (nCellsAvailable / (nColumns + 1)) - 1;
+
+ sal_Int32 nSplitCol = nCol;
+ sal_Int32 nSplits = nColumns + 1;
+ while( nSplits-- )
+ {
+ // last split eats rounding cells
+ if( nSplits == 0 )
+ nSplitSpan = nCellsAvailable - ((nSplitSpan+1) * nColumns) - 1;
+
+ mxTable->merge( nSplitCol, nRow, nSplitSpan + 1, nRowSpan + 1);
+ if( nSplits > 0 )
+ nSplitCol += nSplitSpan + 1;
+ }
+
+ do
+ {
+ rLeftOvers[nRow++] = 0;
+ }
+ while( nRowSpan-- );
+ --nRow;
+ }
+ else
+ {
+ // cope with outside cells, merge if needed
+ if( nColSpan < (rLeftOvers[nRow] + nNewCols) )
+ mxTable->merge( nCol, nRow, (rLeftOvers[nRow] + nNewCols) + 1, nRowSpan + 1 );
+
+ do
+ {
+ rLeftOvers[nRow++] = 0; // consumed
+ }
+ while( nRowSpan-- );
+ --nRow;
+ }
+ }
+ }
+}
+
+
+void CellCursor::split_horizontal( sal_Int32 nColumns )
+{
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+
+ std::vector< sal_Int32 > aLeftOvers( nRowCount );
+
+ for( sal_Int32 nCol = mnRight; nCol >= mnLeft; --nCol )
+ split_column( nCol, nColumns, aLeftOvers );
+}
+
+
+void CellCursor::split_row( sal_Int32 nRow, sal_Int32 nRows, std::vector< sal_Int32 >& rLeftOvers )
+{
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+
+ sal_Int32 nNewRows = 0, nCol;
+
+ // first check how many columns we need to add
+ for( nCol = mnLeft; nCol <= mnRight; ++nCol )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() && !xCell->isMerged() )
+ nNewRows = std::max( nNewRows, nRows - xCell->getRowSpan() + 1 - rLeftOvers[nCol] );
+ }
+
+ if( nNewRows > 0 )
+ {
+ static const OUStringLiteral sHeight(u"Height");
+ Reference< XTableRows > xRows( mxTable->getRows(), UNO_SET_THROW );
+ Reference< XPropertySet > xRefRow( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
+ sal_Int32 nHeight = 0;
+ xRefRow->getPropertyValue( sHeight ) >>= nHeight;
+ const sal_Int32 nNewHeight = nHeight / (nNewRows + 1);
+
+ // reference row gets new height + rounding errors
+ xRefRow->setPropertyValue( sHeight, Any( nHeight - (nNewHeight * nNewRows) ) );
+
+ xRows->insertByIndex( nRow + 1, nNewRows );
+ mnBottom += nNewRows;
+
+ // distribute new width
+ for( sal_Int32 nNewRow = nRow + nNewRows; nNewRow > nRow; --nNewRow )
+ {
+ Reference< XPropertySet > xNewRow( xRows->getByIndex( nNewRow ), UNO_QUERY_THROW );
+ xNewRow->setPropertyValue( sHeight, Any( nNewHeight ) );
+ }
+ }
+
+ for( nCol = 0; nCol < nColCount; ++nCol )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( !xCell.is() || xCell->isMerged() )
+ {
+ if( nNewRows )
+ {
+ // merged cells are ignored, but newly added columns will be added to leftovers
+ xCell.set( dynamic_cast< Cell* >(mxTable->getCellByPosition( nCol, nRow+1 ).get() ) );
+ if( !xCell.is() || !xCell->isMerged() )
+ rLeftOvers[nCol] += nNewRows;
+ }
+ }
+ else
+ {
+ sal_Int32 nRowSpan = xCell->getRowSpan() - 1;
+ sal_Int32 nColSpan = xCell->getColumnSpan() - 1;
+
+ if( (nCol >= mnLeft) && (nCol <= mnRight) )
+ {
+ sal_Int32 nCellsAvailable = 1 + nRowSpan + rLeftOvers[nCol];
+ if( nRowSpan == 0 )
+ nCellsAvailable += nNewRows;
+
+ DBG_ASSERT( nCellsAvailable > nRows, "sdr::table::CellCursor::split_row(), somethings wrong" );
+
+ sal_Int32 nSplitSpan = (nCellsAvailable / (nRows + 1)) - 1;
+
+ sal_Int32 nSplitRow = nRow;
+ sal_Int32 nSplits = nRows + 1;
+ while( nSplits-- )
+ {
+ // last split eats rounding cells
+ if( nSplits == 0 )
+ nSplitSpan = nCellsAvailable - ((nSplitSpan+1) * nRows) - 1;
+
+ mxTable->merge( nCol, nSplitRow, nColSpan + 1, nSplitSpan + 1 );
+ if( nSplits > 0 )
+ nSplitRow += nSplitSpan + 1;
+ }
+
+ do
+ {
+ rLeftOvers[nCol++] = 0;
+ }
+ while( nColSpan-- );
+ --nCol;
+ }
+ else
+ {
+ // cope with outside cells, merge if needed
+ if( nRowSpan < (rLeftOvers[nCol] + nNewRows) )
+ mxTable->merge( nCol, nRow, nColSpan + 1, (rLeftOvers[nCol] + nNewRows) + 1 );
+
+ do
+ {
+ rLeftOvers[nCol++] = 0; // consumed
+ }
+ while( nColSpan-- );
+ --nCol;
+ }
+ }
+ }
+}
+
+
+void CellCursor::split_vertical( sal_Int32 nRows )
+{
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+
+ std::vector< sal_Int32 > aLeftOvers( nColCount );
+
+ for( sal_Int32 nRow = mnBottom; nRow >= mnTop; --nRow )
+ split_row( nRow, nRows, aLeftOvers );
+}
+
+
+void SAL_CALL CellCursor::split( sal_Int32 nColumns, sal_Int32 nRows )
+{
+ if( (nColumns < 0) || (nRows < 0) )
+ throw IllegalArgumentException();
+
+ if( !mxTable.is() || (mxTable->getSdrTableObj() == nullptr) )
+ throw DisposedException();
+
+ SdrModel& rModel(mxTable->getSdrTableObj()->getSdrModelFromSdrObject());
+ const bool bUndo(mxTable->getSdrTableObj()->IsInserted() && rModel.IsUndoEnabled());
+
+ if( bUndo )
+ rModel.BegUndo( SvxResId(STR_TABLE_SPLIT) );
+
+ try
+ {
+ if( nColumns > 0 )
+ split_horizontal( nColumns );
+
+ if( nRows > 0 )
+ split_vertical( nRows );
+
+ if( nColumns > 0 ||nRows > 0 )
+ mxTable->setModified(true);
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ throw NoSupportException();
+ }
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ rModel.SetChanged();
+}
+
+
+sal_Bool SAL_CALL CellCursor::isMergeable( )
+{
+ CellPos aStart, aEnd;
+ return GetMergedSelection( aStart, aEnd );
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/cellcursor.hxx b/svx/source/table/cellcursor.hxx
new file mode 100644
index 000000000..e6756c926
--- /dev/null
+++ b/svx/source/table/cellcursor.hxx
@@ -0,0 +1,71 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_TABLE_CELLCURSOR_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_CELLCURSOR_HXX
+
+#include <com/sun/star/table/XMergeableCellRange.hpp>
+#include <com/sun/star/table/XCellCursor.hpp>
+#include <cppuhelper/implbase.hxx>
+#include "cellrange.hxx"
+
+
+namespace sdr::table {
+
+struct CellPos;
+
+typedef ::cppu::ImplInheritanceHelper< CellRange, css::table::XCellCursor, css::table::XMergeableCellRange > CellCursorBase;
+
+class CellCursor final : public CellCursorBase
+{
+public:
+ CellCursor( const TableModelRef& xTableModel, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom );
+ virtual ~CellCursor() override;
+
+ // XCellRange
+ virtual css::uno::Reference< css::table::XCell > SAL_CALL getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow ) override;
+ virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) override;
+ virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByName( const OUString& aRange ) override;
+
+ // XCellCursor
+ virtual void SAL_CALL gotoStart( ) override;
+ virtual void SAL_CALL gotoEnd( ) override;
+ virtual void SAL_CALL gotoNext( ) override;
+ virtual void SAL_CALL gotoPrevious( ) override;
+ virtual void SAL_CALL gotoOffset( ::sal_Int32 nColumnOffset, ::sal_Int32 nRowOffset ) override;
+
+ // XMergeableCellRange
+ virtual void SAL_CALL merge( ) override;
+ virtual void SAL_CALL split( ::sal_Int32 Columns, ::sal_Int32 Rows ) override;
+ virtual sal_Bool SAL_CALL isMergeable( ) override;
+
+private:
+ bool GetMergedSelection( CellPos& rStart, CellPos& rEnd );
+
+ void split_column( sal_Int32 nCol, sal_Int32 nColumns, std::vector< sal_Int32 >& rLeftOvers );
+ void split_horizontal( sal_Int32 nColumns );
+ void split_row( sal_Int32 nRow, sal_Int32 nRows, std::vector< sal_Int32 >& rLeftOvers );
+ void split_vertical( sal_Int32 nRows );
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/cellrange.cxx b/svx/source/table/cellrange.cxx
new file mode 100644
index 000000000..f9941fd24
--- /dev/null
+++ b/svx/source/table/cellrange.cxx
@@ -0,0 +1,116 @@
+/* -*- 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 <sal/config.h>
+
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+#include "cellrange.hxx"
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::table;
+
+
+namespace sdr::table {
+
+CellRange::CellRange( const TableModelRef & xTable, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
+: mxTable( xTable )
+, mnLeft(nLeft)
+, mnTop(nTop)
+, mnRight(nRight)
+, mnBottom(nBottom)
+{
+}
+
+
+CellRange::~CellRange()
+{
+}
+
+
+// ICellRange
+
+
+sal_Int32 CellRange::getLeft()
+{
+ return mnLeft;
+}
+
+sal_Int32 CellRange::getTop()
+{
+ return mnTop;
+}
+
+sal_Int32 CellRange::getRight()
+{
+ return mnRight;
+}
+
+sal_Int32 CellRange::getBottom()
+{
+ return mnBottom;
+}
+
+Reference< XTable > CellRange::getTable()
+{
+ return mxTable;
+}
+
+
+// XCellRange
+
+
+Reference< XCell > SAL_CALL CellRange::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow )
+{
+ return mxTable->getCellByPosition( mnLeft + nColumn, mnTop + nRow );
+}
+
+
+Reference< XCellRange > SAL_CALL CellRange::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
+{
+ if( (nLeft >= 0 ) && (nTop >= 0) && (nRight >= nLeft) && (nBottom >= nTop) )
+ {
+ nLeft += mnLeft;
+ nTop += mnTop;
+ nRight += mnLeft;
+ nBottom += mnTop;
+
+ const sal_Int32 nMaxColumns = (mnRight == -1) ? mxTable->getColumnCount() : mnLeft;
+ const sal_Int32 nMaxRows = (mnBottom == -1) ? mxTable->getRowCount() : mnBottom;
+ if( (nLeft < nMaxColumns) && (nRight < nMaxColumns) && (nTop < nMaxRows) && (nBottom < nMaxRows) )
+ {
+ return mxTable->getCellRangeByPosition( nLeft, nTop, nRight, nBottom );
+ }
+ }
+ throw IndexOutOfBoundsException();
+}
+
+
+Reference< XCellRange > SAL_CALL CellRange::getCellRangeByName( const OUString& /*aRange*/ )
+{
+ return Reference< XCellRange >();
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/cellrange.hxx b/svx/source/table/cellrange.hxx
new file mode 100644
index 000000000..b4802eff0
--- /dev/null
+++ b/svx/source/table/cellrange.hxx
@@ -0,0 +1,61 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_TABLE_CELLRANGE_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_CELLRANGE_HXX
+
+#include <com/sun/star/table/XCellRange.hpp>
+#include <cppuhelper/implbase1.hxx>
+
+#include <tablemodel.hxx>
+
+
+namespace sdr::table {
+
+class CellRange : public ::cppu::WeakAggImplHelper1< css::table::XCellRange >, public ICellRange
+{
+public:
+ CellRange( const TableModelRef & xTable, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom );
+ virtual ~CellRange() override;
+
+ // ICellRange
+ virtual sal_Int32 getLeft() override;
+ virtual sal_Int32 getTop() override;
+ virtual sal_Int32 getRight() override;
+ virtual sal_Int32 getBottom() override;
+ virtual css::uno::Reference< css::table::XTable > getTable() override;
+
+ // XCellRange
+ virtual css::uno::Reference< css::table::XCell > SAL_CALL getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow ) override;
+ virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) override;
+ virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByName( const OUString& aRange ) override;
+
+protected:
+ TableModelRef mxTable;
+ sal_Int32 mnLeft;
+ sal_Int32 mnTop;
+ sal_Int32 mnRight;
+ sal_Int32 mnBottom;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/propertyset.cxx b/svx/source/table/propertyset.cxx
new file mode 100644
index 000000000..0dc90358c
--- /dev/null
+++ b/svx/source/table/propertyset.cxx
@@ -0,0 +1,206 @@
+/* -*- 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 "propertyset.hxx"
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+
+namespace sdr::table {
+
+FastPropertySetInfo::FastPropertySetInfo( const PropertyVector& rProps )
+{
+ addProperties( rProps );
+}
+
+
+FastPropertySetInfo::~FastPropertySetInfo()
+{
+}
+
+
+void FastPropertySetInfo::addProperties( const PropertyVector& rProps )
+{
+ sal_uInt32 nIndex = maProperties.size();
+ sal_uInt32 nCount = rProps.size();
+ maProperties.resize( nIndex + nCount );
+ for( const Property& rProperty : rProps )
+ {
+ maProperties[nIndex] = rProperty;
+ maMap[ rProperty.Name ] = nIndex++;
+ }
+}
+
+
+const Property& FastPropertySetInfo::getProperty( const OUString& aName )
+{
+ PropertyMap::iterator aIter( maMap.find( aName ) );
+ if( aIter == maMap.end() )
+ throw UnknownPropertyException( aName, static_cast<cppu::OWeakObject*>(this));
+ return maProperties[(*aIter).second];
+}
+
+
+const Property* FastPropertySetInfo::hasProperty( const OUString& aName )
+{
+ PropertyMap::iterator aIter( maMap.find( aName ) );
+ if( aIter == maMap.end() )
+ return nullptr;
+ else
+ return &maProperties[(*aIter).second];
+}
+
+
+// XPropertySetInfo
+
+
+Sequence< Property > SAL_CALL FastPropertySetInfo::getProperties()
+{
+ return Sequence< Property >( maProperties.data(), maProperties.size() );
+}
+
+
+Property SAL_CALL FastPropertySetInfo::getPropertyByName( const OUString& aName )
+{
+ return getProperty( aName );
+}
+
+
+sal_Bool SAL_CALL FastPropertySetInfo::hasPropertyByName( const OUString& aName )
+{
+ return hasProperty( aName ) != nullptr;
+}
+
+FastPropertySet::FastPropertySet( const rtl::Reference< FastPropertySetInfo >& xInfo )
+: mxInfo( xInfo )
+{
+}
+
+
+FastPropertySet::~FastPropertySet()
+{
+}
+
+
+// XPropertySet
+
+
+Reference< XPropertySetInfo > SAL_CALL FastPropertySet::getPropertySetInfo( )
+{
+ return mxInfo;
+}
+
+
+void SAL_CALL FastPropertySet::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
+{
+ setFastPropertyValue( mxInfo->getProperty( aPropertyName ).Handle, aValue );
+}
+
+
+Any SAL_CALL FastPropertySet::getPropertyValue( const OUString& aPropertyName )
+{
+ return getFastPropertyValue( mxInfo->getProperty( aPropertyName ).Handle );
+}
+
+
+void SAL_CALL FastPropertySet::addPropertyChangeListener( const OUString&, const Reference< XPropertyChangeListener >& )
+{
+}
+
+
+void SAL_CALL FastPropertySet::removePropertyChangeListener( const OUString&, const Reference< XPropertyChangeListener >& )
+{
+}
+
+
+void SAL_CALL FastPropertySet::addVetoableChangeListener( const OUString&, const Reference< XVetoableChangeListener >& )
+{
+}
+
+
+void SAL_CALL FastPropertySet::removeVetoableChangeListener( const OUString&, const Reference< XVetoableChangeListener >& )
+{
+}
+
+
+// XMultiPropertySet
+
+
+void SAL_CALL FastPropertySet::setPropertyValues( const Sequence< OUString >& aPropertyNames, const Sequence< Any >& aValues )
+{
+ if( aPropertyNames.getLength() != aValues.getLength() )
+ throw IllegalArgumentException();
+
+ const Any* pValues = aValues.getConstArray();
+ for( const OUString& rPropertyName : aPropertyNames )
+ {
+ const Property* pProperty = mxInfo->hasProperty( rPropertyName );
+ if( pProperty ) try
+ {
+ setFastPropertyValue( pProperty->Handle, *pValues );
+ }
+ catch( UnknownPropertyException& )
+ {
+ }
+ pValues++;
+ }
+}
+
+
+Sequence< Any > SAL_CALL FastPropertySet::getPropertyValues( const Sequence< OUString >& aPropertyNames )
+{
+ sal_Int32 nCount = aPropertyNames.getLength();
+ Sequence< Any > aValues( nCount );
+
+ Any* pValues = aValues.getArray();
+ for( const OUString& rPropertyName : aPropertyNames )
+ {
+ const Property* pProperty = mxInfo->hasProperty( rPropertyName );
+ if( pProperty ) try
+ {
+ *pValues = getFastPropertyValue( pProperty->Handle );
+ }
+ catch( UnknownPropertyException& )
+ {
+ }
+ pValues++;
+ }
+ return aValues;
+}
+
+
+void SAL_CALL FastPropertySet::addPropertiesChangeListener( const Sequence< OUString >&, const Reference< XPropertiesChangeListener >& )
+{
+}
+
+
+void SAL_CALL FastPropertySet::removePropertiesChangeListener( const Reference< XPropertiesChangeListener >& )
+{
+}
+
+
+void SAL_CALL FastPropertySet::firePropertiesChangeEvent( const Sequence< OUString >&, const Reference< XPropertiesChangeListener >& )
+{
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/propertyset.hxx b/svx/source/table/propertyset.hxx
new file mode 100644
index 000000000..69500afe0
--- /dev/null
+++ b/svx/source/table/propertyset.hxx
@@ -0,0 +1,96 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_TABLE_PROPERTYSET_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_PROPERTYSET_HXX
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/beans/XFastPropertySet.hpp>
+#include <rtl/ref.hxx>
+#include <unordered_map>
+#include <vector>
+
+#include <cppuhelper/implbase1.hxx>
+#include <cppuhelper/implbase3.hxx>
+
+namespace sdr::table {
+
+typedef std::vector< css::beans::Property > PropertyVector;
+typedef std::unordered_map< OUString, ::sal_uInt32 > PropertyMap;
+
+class FastPropertySetInfo : public ::cppu::WeakAggImplHelper1< css::beans::XPropertySetInfo >
+{
+public:
+ explicit FastPropertySetInfo( const PropertyVector& rProps );
+ virtual ~FastPropertySetInfo() override;
+
+ void addProperties( const PropertyVector& rProps );
+
+ /// @throws css::beans::UnknownPropertyException
+ const css::beans::Property& getProperty( const OUString& aName );
+ const css::beans::Property* hasProperty( const OUString& aName );
+
+ // XPropertySetInfo
+ virtual css::uno::Sequence< css::beans::Property > SAL_CALL getProperties( ) override;
+ virtual css::beans::Property SAL_CALL getPropertyByName( const OUString& aName ) override;
+ virtual sal_Bool SAL_CALL hasPropertyByName( const OUString& Name ) override;
+
+private:
+ PropertyVector maProperties;
+ PropertyMap maMap;
+};
+
+
+class FastPropertySet : public ::cppu::WeakAggImplHelper3< css::beans::XPropertySet, css::beans::XMultiPropertySet, css::beans::XFastPropertySet >
+{
+public:
+ explicit FastPropertySet( const rtl::Reference< FastPropertySetInfo >& xInfo );
+ virtual ~FastPropertySet() override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override;
+ virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+ virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+
+ // XMultiPropertySet
+// virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) throw (css::uno::RuntimeException);
+ virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues ) override;
+ virtual css::uno::Sequence< css::uno::Any > SAL_CALL getPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames ) override;
+ virtual void SAL_CALL addPropertiesChangeListener( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override;
+ virtual void SAL_CALL removePropertiesChangeListener( const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override;
+ virtual void SAL_CALL firePropertiesChangeEvent( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override;
+
+ // XFastPropertySet
+ virtual void SAL_CALL setFastPropertyValue( ::sal_Int32 nHandle, const css::uno::Any& aValue ) override = 0;
+ virtual css::uno::Any SAL_CALL getFastPropertyValue( ::sal_Int32 nHandle ) override = 0;
+
+private:
+ rtl::Reference< FastPropertySetInfo > mxInfo;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/svdotable.cxx b/svx/source/table/svdotable.cxx
new file mode 100644
index 000000000..8b46feb29
--- /dev/null
+++ b/svx/source/table/svdotable.cxx
@@ -0,0 +1,2534 @@
+/* -*- 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 <cppuhelper/implbase.hxx>
+#include <libxml/xmlwriter.h>
+#include <tools/diagnose_ex.h>
+
+#include <boost/property_tree/ptree.hpp>
+
+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);
+}
+
+
+class SdrTableObjImpl : public TableDesignUser, public ::cppu::WeakImplHelper< css::util::XModifyListener >
+{
+public:
+ CellRef mxActiveCell;
+ TableModelRef mxTable;
+ SdrTableObj* mpTableObj;
+ std::unique_ptr<TableLayouter> mpLayouter;
+ CellPos maEditPos;
+ TableStyleSettings maTableStyle;
+ Reference< XIndexAccess > mxTableStyle;
+ std::vector<std::unique_ptr<SdrUndoAction>> maUndos;
+ bool mbSkipChangeLayout;
+
+ void CropTableModelToSelection(const CellPos& rStart, const CellPos& rEnd);
+
+ CellRef getCell( const CellPos& rPos ) const;
+ void LayoutTable( tools::Rectangle& rArea, bool bFitWidth, bool bFitHeight );
+
+ void ApplyCellStyles();
+ void UpdateCells( tools::Rectangle const & rArea );
+
+ SdrTableObjImpl();
+ virtual ~SdrTableObjImpl() override;
+
+ void init( SdrTableObj* pTable, sal_Int32 nColumns, sal_Int32 nRows );
+ void dispose();
+
+ sal_Int32 getColumnCount() const;
+ /// Get widths of the columns in the table.
+ std::vector<sal_Int32> getColumnWidths() const;
+ sal_Int32 getRowCount() const;
+
+ void DragEdge( bool mbHorizontal, int nEdge, sal_Int32 nOffset );
+
+ SdrTableObjImpl& operator=( const SdrTableObjImpl& rSource );
+
+ // XModifyListener
+ virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ void update();
+
+ void connectTableStyle();
+ void disconnectTableStyle();
+ virtual bool isInUse() override;
+ void dumpAsXml(xmlTextWriterPtr pWriter) const;
+private:
+ static SdrTableObjImpl* lastLayoutTable;
+ static tools::Rectangle lastLayoutInputRectangle;
+ static tools::Rectangle lastLayoutResultRectangle;
+ static bool lastLayoutFitWidth;
+ static bool lastLayoutFitHeight;
+ static WritingMode lastLayoutMode;
+ static sal_Int32 lastRowCount;
+ static sal_Int32 lastColCount;
+ static std::vector<sal_Int32> lastColWidths;
+ static bool rowSizeChanged;
+};
+
+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 const 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 const 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
+ LayoutTable( mpTableObj->maRect, false, false );
+}
+
+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 ));
+ LayoutTable( mpTableObj->maRect, true, true );
+ mpTableObj->maLogicRect = mpTableObj->maRect;
+}
+
+
+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->maRect = mpTableObj->maLogicRect;
+
+ // layout cloned table
+ LayoutTable( mpTableObj->maRect, false, false );
+
+ // 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 const OUStringLiteral sSize( u"Size" );
+ 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*/ )
+{
+ 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->maRect = mpTableObj->maLogicRect;
+ LayoutTable( mpTableObj->maRect, false, false );
+
+ 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*/ )
+{
+ mxActiveCell.clear();
+ mxTable.clear();
+ mpLayouter.reset();
+ mpTableObj = nullptr;
+}
+
+
+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)
+{
+ init( 1, 1 );
+}
+
+SdrTableObj::SdrTableObj(SdrModel& rSdrModel, SdrTableObj const & rSource)
+: SdrTextObj(rSdrModel, rSource)
+{
+ init( 1, 1 );
+
+ TableModelNotifyGuard aGuard( mpImpl.is() ? mpImpl->mxTable.get() : nullptr );
+
+ maLogicRect = rSource.maLogicRect;
+ maRect = rSource.maRect;
+ 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;
+}
+
+SdrTableObj::SdrTableObj(
+ SdrModel& rSdrModel,
+ const ::tools::Rectangle& rNewRect,
+ sal_Int32 nColumns,
+ sal_Int32 nRows)
+: SdrTextObj(rSdrModel, rNewRect)
+ ,maLogicRect(rNewRect)
+{
+ if( nColumns <= 0 )
+ nColumns = 1;
+
+ if( nRows <= 0 )
+ nRows = 1;
+
+ init( nColumns, nRows );
+}
+
+
+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() )
+ {
+ maRect = maLogicRect;
+ mpImpl->LayoutTable( maRect, false, false );
+ }
+}
+
+
+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() - maRect.Left();
+ sal_Int32 nY = rPos.Y() - maRect.Top();
+
+ if( (nX < 0) || (nX > maRect.GetWidth()) || (nY < 0) || (nY > maRect.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( maRect );
+ maRect = maLogicRect;
+ mpImpl->LayoutTable( maRect, false, false );
+ SetBoundAndSnapRectsDirty();
+ ActionChanged();
+ BroadcastObjectChange();
+ if (aRect0 != maRect)
+ 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(maRect);
+
+ 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);
+}
+
+
+SdrTableObj* SdrTableObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrTableObj(rTargetModel, *this);
+}
+
+
+const tools::Rectangle& SdrTableObj::GetSnapRect() const
+{
+ return maRect;
+}
+
+
+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.getWidth() != maRect.getWidth();
+ const bool bHeight = maLogicRect.getHeight() != maRect.getHeight();
+ maRect = 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().getHeight() );
+ SetLogicRect( aAdjustRect );
+}
+
+
+void SdrTableObj::NbcMove(const Size& rSiz)
+{
+ maLogicRect.Move(rSiz);
+ SdrTextObj::NbcMove( rSiz );
+ if( mpImpl.is() )
+ mpImpl->UpdateCells( maRect );
+}
+
+
+void SdrTableObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ tools::Rectangle aOldRect( maLogicRect );
+ ResizeRect(maLogicRect,rRef,xFact,yFact);
+
+ maRect = 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();
+ maRect = 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(maRect.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(maRect.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);
+ tempList.AddHdl( std::make_unique<TableBorderHdl>( maRect, !IsTextEditActive() ) );
+ tempList.AddHdl( std::make_unique<SdrHdl>(maRect.TopLeft(),SdrHdlKind::UpperLeft) );
+ tempList.AddHdl( std::make_unique<SdrHdl>(maRect.TopCenter(),SdrHdlKind::Upper) );
+ tempList.AddHdl( std::make_unique<SdrHdl>(maRect.TopRight(),SdrHdlKind::UpperRight) );
+ tempList.AddHdl( std::make_unique<SdrHdl>(maRect.LeftCenter(),SdrHdlKind::Left) );
+ tempList.AddHdl( std::make_unique<SdrHdl>(maRect.RightCenter(),SdrHdlKind::Right) );
+ tempList.AddHdl( std::make_unique<SdrHdl>(maRect.BottomLeft(),SdrHdlKind::LowerLeft) );
+ tempList.AddHdl( std::make_unique<SdrHdl>(maRect.BottomCenter(),SdrHdlKind::Lower) );
+ tempList.AddHdl( std::make_unique<SdrHdl>(maRect.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 != maRect)
+ {
+ 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.Justify();
+ rStat.SetActionRect(aRect1);
+ maRect = aRect1;
+ return true;
+}
+
+
+bool SdrTableObj::MovCreate(SdrDragStat& rStat)
+{
+ tools::Rectangle aRect1;
+ rStat.TakeCreateRect(aRect1);
+ ImpJustifyRect(aRect1);
+ rStat.SetActionRect(aRect1);
+ maRect = aRect1; // for ObjName
+ SetBoundRectDirty();
+ m_bSnapRectDirty=true;
+ return true;
+}
+
+
+bool SdrTableObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ rStat.TakeCreateRect(maRect);
+ ImpJustifyRect(maRect);
+ 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.Justify();
+
+ 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::SaveGeoData(), illegal geo data!" );
+
+ maLogicRect = static_cast<const TableObjectGeoData &>(rGeo).maLogicRect;
+
+ SdrTextObj::RestoreGeoData (rGeo);
+
+ if( mpImpl.is() )
+ mpImpl->LayoutTable(maRect, false, false);
+ ActionChanged();
+}
+
+void SdrTableObj::CropTableModelToSelection(const CellPos& rStart, const CellPos& rEnd)
+{
+ if(!mpImpl.is())
+ {
+ return;
+ }
+
+ mpImpl->CropTableModelToSelection(rStart, rEnd);
+}
+
+sal_Int32 SdrTableObj::getHeightWithoutFitting()
+{
+ tools::Rectangle aRect{};
+ if( mpImpl.is() && mpImpl->mpLayouter)
+ {
+ mpImpl->mpLayouter->LayoutTableHeight(aRect, /*bFit=*/false);
+ return aRect.GetHeight();
+ }
+ else
+ return 0;
+}
+
+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() );
+ mpImpl->mpLayouter->DistributeColumns( maRect, nFirstColumn, nLastColumn, bOptimize, bMinimize );
+ }
+}
+
+
+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() );
+ mpImpl->mpLayouter->DistributeRows( maRect, nFirstRow, nLastRow, bOptimize, bMinimize );
+ }
+}
+
+
+void SdrTableObj::SetChanged()
+{
+ if( mpImpl.is() )
+ {
+ mpImpl->LayoutTable( maRect, false, false );
+ }
+
+ ::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: */
diff --git a/svx/source/table/tablecolumn.cxx b/svx/source/table/tablecolumn.cxx
new file mode 100644
index 000000000..d9e33db47
--- /dev/null
+++ b/svx/source/table/tablecolumn.cxx
@@ -0,0 +1,289 @@
+/* -*- 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 <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+#include <tablemodel.hxx>
+#include "tablecolumn.hxx"
+#include "tableundo.hxx"
+#include <svx/svdmodel.hxx>
+#include <svx/svdotable.hxx>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::beans;
+
+
+namespace sdr::table {
+
+const sal_Int32 Property_Width = 0;
+const sal_Int32 Property_OptimalWidth = 1;
+const sal_Int32 Property_IsVisible = 2;
+const sal_Int32 Property_IsStartOfNewPage = 3;
+
+
+// TableRow
+
+
+TableColumn::TableColumn( const TableModelRef& xTableModel, sal_Int32 nColumn )
+: TableColumnBase( getStaticPropertySetInfo() )
+, mxTableModel( xTableModel )
+, mnColumn( nColumn )
+, mnWidth( 0 )
+, mbOptimalWidth( true )
+, mbIsVisible( true )
+, mbIsStartOfNewPage( false )
+{
+}
+
+
+TableColumn::~TableColumn()
+{
+}
+
+
+void TableColumn::dispose()
+{
+ mxTableModel.clear();
+}
+
+
+void TableColumn::throwIfDisposed() const
+{
+ if( !mxTableModel.is() )
+ throw DisposedException();
+}
+
+
+TableColumn& TableColumn::operator=( const TableColumn& r )
+{
+ mnWidth = r.mnWidth;
+ mbOptimalWidth = r.mbOptimalWidth;
+ mbIsVisible = r.mbIsVisible;
+ mbIsStartOfNewPage = r.mbIsStartOfNewPage;
+ maName = r.maName;
+ mnColumn = r.mnColumn;
+
+ return *this;
+}
+
+
+// XCellRange
+
+
+Reference< XCell > SAL_CALL TableColumn::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow )
+{
+ throwIfDisposed();
+ if( nColumn != 0 )
+ throw IndexOutOfBoundsException();
+
+ return mxTableModel->getCellByPosition( mnColumn, nRow );
+}
+
+
+Reference< XCellRange > SAL_CALL TableColumn::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
+{
+ throwIfDisposed();
+ if( (nTop >= 0 ) && (nLeft == 0) && (nBottom >= nTop) && (nRight == 0) )
+ {
+ return mxTableModel->getCellRangeByPosition( mnColumn, nTop, mnColumn, nBottom );
+ }
+ throw IndexOutOfBoundsException();
+}
+
+
+Reference< XCellRange > SAL_CALL TableColumn::getCellRangeByName( const OUString& /*aRange*/ )
+{
+ return Reference< XCellRange >();
+}
+
+
+// XNamed
+
+
+OUString SAL_CALL TableColumn::getName()
+{
+ return maName;
+}
+
+
+void SAL_CALL TableColumn::setName( const OUString& aName )
+{
+ maName = aName;
+}
+
+
+// XFastPropertySet
+
+
+void SAL_CALL TableColumn::setFastPropertyValue( sal_Int32 nHandle, const Any& aValue )
+{
+ bool bOk = false;
+ bool bChange = false;
+
+ SdrModel& rModel(mxTableModel->getSdrTableObj()->getSdrModelFromSdrObject());
+ std::unique_ptr<TableColumnUndo> pUndo;
+
+ if( mxTableModel.is() && mxTableModel->getSdrTableObj() && mxTableModel->getSdrTableObj()->IsInserted() && rModel.IsUndoEnabled() )
+ {
+ TableColumnRef xThis( this );
+ pUndo.reset( new TableColumnUndo( xThis ) );
+ }
+
+ switch( nHandle )
+ {
+ case Property_Width:
+ {
+ sal_Int32 nWidth = mnWidth;
+ bOk = aValue >>= nWidth;
+ if( bOk && (nWidth != mnWidth) )
+ {
+ mnWidth = nWidth;
+ mbOptimalWidth = mnWidth == 0;
+ bChange = true;
+ }
+ break;
+ }
+ case Property_OptimalWidth:
+ {
+ bool bOptimalWidth = mbOptimalWidth;
+ bOk = aValue >>= bOptimalWidth;
+ if( bOk && (mbOptimalWidth != bOptimalWidth) )
+ {
+ mbOptimalWidth = bOptimalWidth;
+ if( bOptimalWidth )
+ mnWidth = 0;
+ bChange = true;
+ }
+ break;
+ }
+ case Property_IsVisible:
+ {
+ bool bIsVisible = mbIsVisible;
+ bOk = aValue >>= bIsVisible;
+ if( bOk && (mbIsVisible != bIsVisible) )
+ {
+ mbIsVisible = bIsVisible;
+ bChange = true;
+ }
+ break;
+ }
+
+ case Property_IsStartOfNewPage:
+ {
+ bool bIsStartOfNewPage = mbIsStartOfNewPage;
+ bOk = aValue >>= bIsStartOfNewPage;
+ if( bOk && (mbIsStartOfNewPage != bIsStartOfNewPage) )
+ {
+ mbIsStartOfNewPage = bIsStartOfNewPage;
+ bChange = true;
+ }
+ break;
+ }
+ default:
+ throw UnknownPropertyException( OUString::number(nHandle), static_cast<cppu::OWeakObject*>(this));
+ }
+ if( !bOk )
+ {
+ throw IllegalArgumentException();
+ }
+
+ if( bChange )
+ {
+ if( pUndo )
+ {
+ rModel.AddUndo( std::move(pUndo) );
+ }
+ mxTableModel->setModified(true);
+ }
+}
+
+
+Any SAL_CALL TableColumn::getFastPropertyValue( sal_Int32 nHandle )
+{
+ switch( nHandle )
+ {
+ case Property_Width: return Any( mnWidth );
+ case Property_OptimalWidth: return Any( mbOptimalWidth );
+ case Property_IsVisible: return Any( mbIsVisible );
+ case Property_IsStartOfNewPage: return Any( mbIsStartOfNewPage );
+ default: throw UnknownPropertyException( OUString::number(nHandle), static_cast<cppu::OWeakObject*>(this));
+ }
+}
+
+
+rtl::Reference< FastPropertySetInfo > TableColumn::getStaticPropertySetInfo()
+{
+ static rtl::Reference<FastPropertySetInfo> xInfo = []() {
+ PropertyVector aProperties(6);
+
+ aProperties[0].Name = "Width";
+ aProperties[0].Handle = Property_Width;
+ aProperties[0].Type = ::cppu::UnoType<sal_Int32>::get();
+ aProperties[0].Attributes = 0;
+
+ aProperties[1].Name = "OptimalWidth";
+ aProperties[1].Handle = Property_OptimalWidth;
+ aProperties[1].Type = cppu::UnoType<bool>::get();
+ aProperties[1].Attributes = 0;
+
+ aProperties[2].Name = "IsVisible";
+ aProperties[2].Handle = Property_IsVisible;
+ aProperties[2].Type = cppu::UnoType<bool>::get();
+ aProperties[2].Attributes = 0;
+
+ aProperties[3].Name = "IsStartOfNewPage";
+ aProperties[3].Handle = Property_IsStartOfNewPage;
+ aProperties[3].Type = cppu::UnoType<bool>::get();
+ aProperties[3].Attributes = 0;
+
+ aProperties[4].Name = "Size";
+ aProperties[4].Handle = Property_Width;
+ aProperties[4].Type = ::cppu::UnoType<sal_Int32>::get();
+ aProperties[4].Attributes = 0;
+
+ aProperties[5].Name = "OptimalSize";
+ aProperties[5].Handle = Property_OptimalWidth;
+ aProperties[5].Type = cppu::UnoType<bool>::get();
+ aProperties[5].Attributes = 0;
+
+ return rtl::Reference<FastPropertySetInfo>(new FastPropertySetInfo(aProperties));
+ }();
+
+ return xInfo;
+}
+
+TableModelRef const & TableColumn::getModel() const
+{
+ return mxTableModel;
+}
+
+sal_Int32 TableColumn::getWidth() const
+{
+ return mnWidth;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablecolumn.hxx b/svx/source/table/tablecolumn.hxx
new file mode 100644
index 000000000..0d1dbb802
--- /dev/null
+++ b/svx/source/table/tablecolumn.hxx
@@ -0,0 +1,83 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_TABLE_TABLECOLUMN_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_TABLECOLUMN_HXX
+
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include "propertyset.hxx"
+#include <celltypes.hxx>
+
+
+namespace sdr::table {
+
+typedef ::cppu::ImplInheritanceHelper< FastPropertySet, css::table::XCellRange, css::container::XNamed > TableColumnBase;
+
+class TableColumn : public TableColumnBase
+{
+ friend class TableColumnUndo;
+ friend class TableModel;
+public:
+ TableColumn( const TableModelRef& xTableModel, sal_Int32 nColumn );
+ virtual ~TableColumn() override;
+
+ void dispose();
+ /// @throws css::uno::RuntimeException
+ void throwIfDisposed() const;
+
+ TableColumn& operator=( const TableColumn& );
+
+ // XCellRange
+ virtual css::uno::Reference< css::table::XCell > SAL_CALL getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow ) override;
+ virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) override;
+ virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByName( const OUString& aRange ) override;
+
+ // XNamed
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName( const OUString& aName ) override;
+
+ // XFastPropertySet
+ virtual void SAL_CALL setFastPropertyValue( ::sal_Int32 nHandle, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getFastPropertyValue( ::sal_Int32 nHandle ) override;
+
+ /// Get the table that owns this column.
+ TableModelRef const & getModel() const;
+ /// Get the width of this column.
+ sal_Int32 getWidth() const;
+
+private:
+ static rtl::Reference< FastPropertySetInfo > getStaticPropertySetInfo();
+
+ TableModelRef mxTableModel;
+ sal_Int32 mnColumn;
+ sal_Int32 mnWidth;
+ bool mbOptimalWidth;
+ bool mbIsVisible;
+ bool mbIsStartOfNewPage;
+ OUString maName;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablecolumns.cxx b/svx/source/table/tablecolumns.cxx
new file mode 100644
index 000000000..c202f3a52
--- /dev/null
+++ b/svx/source/table/tablecolumns.cxx
@@ -0,0 +1,121 @@
+/* -*- 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 <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+#include <tablemodel.hxx>
+#include "tablecolumns.hxx"
+#include "tablecolumn.hxx"
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::table;
+
+
+namespace sdr::table {
+
+TableColumns::TableColumns( const TableModelRef& xTableModel )
+: mxTableModel( xTableModel )
+{
+}
+
+
+TableColumns::~TableColumns()
+{
+ dispose();
+}
+
+
+void TableColumns::dispose()
+{
+ mxTableModel.clear();
+}
+
+
+void TableColumns::throwIfDisposed() const
+{
+ if( !mxTableModel.is() )
+ throw DisposedException();
+}
+
+
+// XTableRows
+
+
+void SAL_CALL TableColumns::insertByIndex( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ throwIfDisposed();
+ mxTableModel->insertColumns( nIndex, nCount );
+}
+
+
+void SAL_CALL TableColumns::removeByIndex( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ throwIfDisposed();
+ mxTableModel->removeColumns( nIndex, nCount );
+}
+
+
+// XIndexAccess
+
+
+sal_Int32 SAL_CALL TableColumns::getCount()
+{
+ throwIfDisposed();
+ return mxTableModel->getColumnCount();
+}
+
+
+Any SAL_CALL TableColumns::getByIndex( sal_Int32 Index )
+{
+ throwIfDisposed();
+
+ if( ( Index < 0 ) || ( Index >= mxTableModel->getColumnCount() ) )
+ throw IndexOutOfBoundsException();
+
+ return Any( Reference< XCellRange >( mxTableModel->getColumn( Index ) ) );
+}
+
+
+// XElementAccess
+
+
+Type SAL_CALL TableColumns::getElementType()
+{
+ throwIfDisposed();
+
+ return cppu::UnoType<XCellRange>::get();
+}
+
+
+sal_Bool SAL_CALL TableColumns::hasElements()
+{
+ throwIfDisposed();
+
+ return mxTableModel->getColumnCount() != 0;
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablecolumns.hxx b/svx/source/table/tablecolumns.hxx
new file mode 100644
index 000000000..696463aac
--- /dev/null
+++ b/svx/source/table/tablecolumns.hxx
@@ -0,0 +1,61 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_TABLE_TABLECOLUMNS_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_TABLECOLUMNS_HXX
+
+#include <com/sun/star/table/XTableColumns.hpp>
+#include <cppuhelper/implbase1.hxx>
+
+#include <celltypes.hxx>
+
+
+namespace sdr::table {
+
+class TableColumns : public ::cppu::WeakAggImplHelper1< css::table::XTableColumns >
+{
+public:
+ explicit TableColumns( const TableModelRef& xTableModel );
+ virtual ~TableColumns() override;
+
+ void dispose();
+ /// @throws css::uno::RuntimeException
+ void throwIfDisposed() const;
+
+ // XTableColumns
+ virtual void SAL_CALL insertByIndex( sal_Int32 nIndex, sal_Int32 nCount ) override;
+ virtual void SAL_CALL removeByIndex( sal_Int32 nIndex, sal_Int32 nCount ) override;
+
+ // XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override;
+ virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override;
+
+ // Methods
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+private:
+ TableModelRef mxTableModel;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablecontroller.cxx b/svx/source/table/tablecontroller.cxx
new file mode 100644
index 000000000..1ceaa5141
--- /dev/null
+++ b/svx/source/table/tablecontroller.cxx
@@ -0,0 +1,3363 @@
+/* -*- 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 <algorithm>
+
+#include <svx/sdr/table/tablecontroller.hxx>
+#include <tablemodel.hxx>
+
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/table/XMergeableCellRange.hpp>
+#include <com/sun/star/table/XMergeableCell.hpp>
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/window.hxx>
+
+#include <svl/whiter.hxx>
+#include <svl/stritem.hxx>
+
+#include <sfx2/request.hxx>
+
+#include <svx/svdotable.hxx>
+#include <sdr/overlay/overlayobjectcell.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/svxids.hrc>
+#include <svx/svdoutl.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdetc.hxx>
+#include <svx/selectioncontroller.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/svxdlg.hxx>
+#include <editeng/boxitem.hxx>
+#include <cell.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/lineitem.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/sdmetitm.hxx>
+#include <svx/sdtditm.hxx>
+#include "tableundo.hxx"
+#include "tablelayouter.hxx"
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <memory>
+#include <o3tl/enumarray.hxx>
+#include <o3tl/enumrange.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/lok.hxx>
+#include <sfx2/viewsh.hxx>
+#include <editeng/editview.hxx>
+#include <tools/UnitConversion.hxx>
+#include <tools/diagnose_ex.h>
+
+using ::editeng::SvxBorderLine;
+using namespace sdr::table;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::style;
+
+namespace {
+
+enum class CellPosFlag // signals the relative position of a cell to a selection
+{
+ NONE = 0x0000, // not set or inside
+ // row
+ Before = 0x0001,
+ Left = 0x0002,
+ Right = 0x0004,
+ After = 0x0008,
+ // column
+ Upper = 0x0010,
+ Top = 0x0020,
+ Bottom = 0x0040,
+ Lower = 0x0080
+};
+
+}
+
+namespace o3tl
+{ template<> struct typed_flags<CellPosFlag> : is_typed_flags<CellPosFlag, 0xff> {}; }
+
+namespace sdr::table {
+
+namespace {
+
+class SvxTableControllerModifyListener : public ::cppu::WeakImplHelper< css::util::XModifyListener >
+{
+public:
+ explicit SvxTableControllerModifyListener( SvxTableController* pController )
+ : mpController( pController ) {}
+
+ // XModifyListener
+ virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ SvxTableController* mpController;
+};
+
+}
+
+// XModifyListener
+
+
+void SAL_CALL SvxTableControllerModifyListener::modified( const css::lang::EventObject& )
+{
+ if( mpController )
+ mpController->onTableModified();
+}
+
+
+// XEventListener
+
+
+void SAL_CALL SvxTableControllerModifyListener::disposing( const css::lang::EventObject& )
+{
+ mpController = nullptr;
+}
+
+
+
+
+rtl::Reference< sdr::SelectionController > CreateTableController(
+ SdrView& rView,
+ const SdrTableObj& rObj,
+ const rtl::Reference< sdr::SelectionController >& xRefController )
+{
+ return SvxTableController::create(rView, rObj, xRefController);
+}
+
+
+rtl::Reference< sdr::SelectionController > SvxTableController::create(
+ SdrView& rView,
+ const SdrTableObj& rObj,
+ const rtl::Reference< sdr::SelectionController >& xRefController )
+{
+ if( xRefController.is() )
+ {
+ SvxTableController* pController = dynamic_cast< SvxTableController* >( xRefController.get() );
+
+ if(pController && (pController->mxTableObj.get() == &rObj) && (&pController->mrView == &rView))
+ {
+ return xRefController;
+ }
+ }
+
+ return new SvxTableController(rView, rObj);
+}
+
+
+SvxTableController::SvxTableController(
+ SdrView& rView,
+ const SdrTableObj& rObj)
+: mbCellSelectionMode(false)
+ ,mbHasJustMerged(false)
+ ,mbLeftButtonDown(false)
+ ,mrView(rView)
+ ,mxTableObj(const_cast< SdrTableObj* >(&rObj))
+ ,mnUpdateEvent( nullptr )
+{
+ mxTableObj->getActiveCellPos( maCursorFirstPos );
+ maCursorLastPos = maCursorFirstPos;
+
+ Reference< XTable > xTable( mxTableObj->getTable() );
+ if( xTable.is() )
+ {
+ mxModifyListener = new SvxTableControllerModifyListener( this );
+ xTable->addModifyListener( mxModifyListener );
+
+ mxTable.set( dynamic_cast< TableModel* >( xTable.get() ) );
+ }
+}
+
+SvxTableController::~SvxTableController()
+{
+ if( mnUpdateEvent )
+ {
+ Application::RemoveUserEvent( mnUpdateEvent );
+ }
+
+ if( mxModifyListener.is() && mxTableObj )
+ {
+ Reference< XTable > xTable( mxTableObj->getTable() );
+ if( xTable.is() )
+ {
+ xTable->removeModifyListener( mxModifyListener );
+ mxModifyListener.clear();
+ }
+ }
+}
+
+bool SvxTableController::onKeyInput(const KeyEvent& rKEvt, vcl::Window* pWindow )
+{
+ if(!checkTableObject())
+ return false;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+
+ // check if we are read only
+ if( rModel.IsReadOnly())
+ {
+ switch( rKEvt.GetKeyCode().GetCode() )
+ {
+ case awt::Key::DOWN:
+ case awt::Key::UP:
+ case awt::Key::LEFT:
+ case awt::Key::RIGHT:
+ case awt::Key::TAB:
+ case awt::Key::HOME:
+ case awt::Key::END:
+ case awt::Key::NUM2:
+ case awt::Key::NUM4:
+ case awt::Key::NUM6:
+ case awt::Key::NUM8:
+ case awt::Key::ESCAPE:
+ case awt::Key::F2:
+ break;
+ default:
+ // tell the view we eat the event, no further processing needed
+ return true;
+ }
+ }
+
+ TblAction nAction = getKeyboardAction(rKEvt);
+
+ return executeAction( nAction, rKEvt.GetKeyCode().IsShift(), pWindow );
+}
+
+namespace {
+
+Point pixelToLogic(const Point& rPoint, vcl::Window const * pWindow)
+{
+ if (!pWindow)
+ return rPoint;
+
+ return pWindow->PixelToLogic(rPoint);
+}
+
+}
+
+bool SvxTableController::onMouseButtonDown(const MouseEvent& rMEvt, vcl::Window* pWindow )
+{
+ if (comphelper::LibreOfficeKit::isActive() && !pWindow)
+ {
+ // Tiled rendering: get the window that has the disabled map mode.
+ if (OutputDevice* pOutputDevice = mrView.GetFirstOutputDevice())
+ {
+ if (pOutputDevice->GetOutDevType() == OUTDEV_WINDOW)
+ pWindow = pOutputDevice->GetOwnerWindow();
+ }
+ }
+
+ if( !pWindow || !checkTableObject() )
+ return false;
+
+ SdrViewEvent aVEvt;
+ if( !rMEvt.IsRight() && mrView.PickAnything(rMEvt,SdrMouseEventKind::BUTTONDOWN, aVEvt) == SdrHitKind::Handle )
+ return false;
+
+ TableHitKind eHit = mxTableObj->CheckTableHit(pixelToLogic(rMEvt.GetPosPixel(), pWindow), maMouseDownPos.mnCol, maMouseDownPos.mnRow);
+
+ mbLeftButtonDown = (rMEvt.GetClicks() == 1) && rMEvt.IsLeft();
+
+ if( eHit == TableHitKind::Cell )
+ {
+ StartSelection( maMouseDownPos );
+ return true;
+ }
+
+ if( rMEvt.IsRight() && eHit != TableHitKind::NONE )
+ return true; // right click will become context menu
+
+ // for cell selection with the mouse remember our first hit
+ if( mbLeftButtonDown )
+ {
+ RemoveSelection();
+
+ SdrHdl* pHdl = mrView.PickHandle(pixelToLogic(rMEvt.GetPosPixel(), pWindow));
+
+ if( pHdl )
+ {
+ mbLeftButtonDown = false;
+ }
+ else
+ {
+ sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
+
+ if (!pTableObj || eHit == TableHitKind::NONE)
+ {
+ mbLeftButtonDown = false;
+ }
+ }
+ }
+
+ if (comphelper::LibreOfficeKit::isActive() && rMEvt.GetClicks() == 2 && rMEvt.IsLeft() && eHit == TableHitKind::CellTextArea)
+ {
+ bool bEmptyOutliner = false;
+ if (Outliner* pOutliner = mrView.GetTextEditOutliner())
+ {
+ if (pOutliner->GetParagraphCount() == 1)
+ {
+ if (Paragraph* pParagraph = pOutliner->GetParagraph(0))
+ bEmptyOutliner = pOutliner->GetText(pParagraph).isEmpty();
+ }
+ }
+ if (bEmptyOutliner)
+ {
+ // Tiled rendering: a left double-click in an empty cell: select it.
+ StartSelection(maMouseDownPos);
+ setSelectedCells(maMouseDownPos, maMouseDownPos);
+ // Update graphic selection, should be hidden now.
+ mrView.AdjustMarkHdl();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool SvxTableController::onMouseButtonUp(const MouseEvent& rMEvt, vcl::Window* /*pWin*/)
+{
+ if( !checkTableObject() )
+ return false;
+
+ mbLeftButtonDown = false;
+
+ return rMEvt.GetClicks() == 2;
+}
+
+
+bool SvxTableController::onMouseMove(const MouseEvent& rMEvt, vcl::Window* pWindow )
+{
+ if( !checkTableObject() )
+ return false;
+
+ SdrTableObj* pTableObj = mxTableObj.get();
+ CellPos aPos;
+ if (mbLeftButtonDown && pTableObj && pTableObj->CheckTableHit(pixelToLogic(rMEvt.GetPosPixel(), pWindow), aPos.mnCol, aPos.mnRow ) != TableHitKind::NONE)
+ {
+ if(aPos != maMouseDownPos)
+ {
+ if( mbCellSelectionMode )
+ {
+ setSelectedCells( maMouseDownPos, aPos );
+ return true;
+ }
+ else
+ {
+ StartSelection( maMouseDownPos );
+ }
+ }
+ else if( mbCellSelectionMode )
+ {
+ UpdateSelection( aPos );
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void SvxTableController::onSelectionHasChanged()
+{
+ bool bSelected = false;
+
+ SdrTableObj* pTableObj = mxTableObj.get();
+ if( pTableObj && pTableObj->IsTextEditActive() )
+ {
+ pTableObj->getActiveCellPos( maCursorFirstPos );
+ maCursorLastPos = maCursorFirstPos;
+ mbCellSelectionMode = false;
+ }
+ else
+ {
+ const SdrMarkList& rMarkList= mrView.GetMarkedObjectList();
+ if( rMarkList.GetMarkCount() == 1 )
+ bSelected = mxTableObj.get() == rMarkList.GetMark(0)->GetMarkedSdrObj();
+ }
+
+ if( bSelected )
+ {
+ updateSelectionOverlay();
+ }
+ else
+ {
+ destroySelectionOverlay();
+ }
+}
+void SvxTableController::onSelectAll()
+{
+ sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
+ if ( pTableObj && !pTableObj->IsTextEditActive())
+ {
+ selectAll();
+ }
+}
+
+
+void SvxTableController::GetState( SfxItemSet& rSet )
+{
+ if(!mxTable.is() || !mxTableObj.is())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ std::optional<SfxItemSet> oSet;
+ bool bVertDone(false);
+
+ // Iterate over all requested items in the set.
+ SfxWhichIter aIter( rSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while (nWhich)
+ {
+ switch (nWhich)
+ {
+ case SID_TABLE_VERT_BOTTOM:
+ case SID_TABLE_VERT_CENTER:
+ case SID_TABLE_VERT_NONE:
+ {
+ if(!bVertDone)
+ {
+ if (!oSet)
+ {
+ oSet.emplace(rModel.GetItemPool());
+ MergeAttrFromSelectedCells(*oSet, false);
+ }
+
+ SdrTextVertAdjust eAdj = SDRTEXTVERTADJUST_BLOCK;
+
+ if (oSet->GetItemState( SDRATTR_TEXT_VERTADJUST ) != SfxItemState::DONTCARE)
+ eAdj = oSet->Get(SDRATTR_TEXT_VERTADJUST).GetValue();
+
+ rSet.Put(SfxBoolItem(SID_TABLE_VERT_BOTTOM, eAdj == SDRTEXTVERTADJUST_BOTTOM));
+ rSet.Put(SfxBoolItem(SID_TABLE_VERT_CENTER, eAdj == SDRTEXTVERTADJUST_CENTER));
+ rSet.Put(SfxBoolItem(SID_TABLE_VERT_NONE, eAdj == SDRTEXTVERTADJUST_TOP));
+ bVertDone = true;
+ }
+ break;
+ }
+ case SID_TABLE_DELETE_ROW:
+ if( !mxTable.is() || !hasSelectedCells() || (!comphelper::LibreOfficeKit::isActive() && mxTable->getRowCount() <= 1) )
+ rSet.DisableItem(SID_TABLE_DELETE_ROW);
+ break;
+ case SID_TABLE_DELETE_COL:
+ if( !mxTable.is() || !hasSelectedCells() || (!comphelper::LibreOfficeKit::isActive() && mxTable->getColumnCount() <= 1) )
+ rSet.DisableItem(SID_TABLE_DELETE_COL);
+ break;
+ case SID_TABLE_DELETE_TABLE:
+ if( !mxTable.is() )
+ rSet.DisableItem(SID_TABLE_DELETE_TABLE);
+ break;
+ case SID_TABLE_MERGE_CELLS:
+ if( !mxTable.is() || !hasSelectedCells() )
+ rSet.DisableItem(SID_TABLE_MERGE_CELLS);
+ break;
+ case SID_TABLE_SPLIT_CELLS:
+ if( !hasSelectedCells() || !mxTable.is() )
+ rSet.DisableItem(SID_TABLE_SPLIT_CELLS);
+ break;
+
+ case SID_TABLE_OPTIMAL_ROW_HEIGHT:
+ case SID_TABLE_DISTRIBUTE_COLUMNS:
+ case SID_TABLE_DISTRIBUTE_ROWS:
+ {
+ bool bDistributeColumns = false;
+ bool bDistributeRows = false;
+ if( mxTable.is() )
+ {
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+
+ bDistributeColumns = aStart.mnCol != aEnd.mnCol;
+ bDistributeRows = aStart.mnRow != aEnd.mnRow;
+ }
+ if( !bDistributeColumns )
+ rSet.DisableItem(SID_TABLE_DISTRIBUTE_COLUMNS);
+ if( !bDistributeRows )
+ {
+ rSet.DisableItem(SID_TABLE_OPTIMAL_ROW_HEIGHT);
+ rSet.DisableItem(SID_TABLE_DISTRIBUTE_ROWS);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+
+void SvxTableController::onInsert( sal_uInt16 nSId, const SfxItemSet* pArgs )
+{
+ if(!checkTableObject())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ bool bInsertAfter = true;
+ sal_uInt16 nCount = 0;
+
+ if( pArgs )
+ {
+ const SfxPoolItem* pItem = nullptr;
+ pArgs->GetItemState(nSId, false, &pItem);
+ if (pItem)
+ {
+ nCount = static_cast<const SfxInt16Item*>(pItem)->GetValue();
+ if(const SfxBoolItem* pItem2 = pArgs->GetItemIfSet(SID_TABLE_PARAM_INSERT_AFTER))
+ bInsertAfter = pItem2->GetValue();
+ }
+ }
+
+ CellPos aStart, aEnd;
+ if( hasSelectedCells() )
+ {
+ getSelectedCells( aStart, aEnd );
+ }
+ else
+ {
+ if( bInsertAfter )
+ {
+ aStart.mnCol = mxTable->getColumnCount() - 1;
+ aStart.mnRow = mxTable->getRowCount() - 1;
+ aEnd = aStart;
+ }
+ }
+
+ if( rTableObj.IsTextEditActive() )
+ mrView.SdrEndTextEdit(true);
+
+ RemoveSelection();
+
+ static const OUStringLiteral sSize( u"Size" );
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ switch( nSId )
+ {
+ case SID_TABLE_INSERT_COL:
+ {
+ TableModelNotifyGuard aGuard( mxTable.get() );
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_TABLE_INSCOL) );
+ rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
+ }
+
+ Reference< XTableColumns > xCols( mxTable->getColumns() );
+ const sal_Int32 nNewColumns = (nCount == 0) ? (aEnd.mnCol - aStart.mnCol + 1) : nCount;
+ const sal_Int32 nNewStartColumn = aEnd.mnCol + (bInsertAfter ? 1 : 0);
+ xCols->insertByIndex( nNewStartColumn, nNewColumns );
+
+ for( sal_Int32 nOffset = 0; nOffset < nNewColumns; nOffset++ )
+ {
+ // Resolves fdo#61540
+ // On Insert before, the reference column whose size is going to be
+ // used for newly created column(s) is wrong. As the new columns are
+ // inserted before the reference column, the reference column moved
+ // to the new position by no., of new columns i.e (earlier+newcolumns).
+ Reference< XPropertySet >(xCols->getByIndex(nNewStartColumn+nOffset), UNO_QUERY_THROW )->
+ setPropertyValue( sSize,
+ Reference< XPropertySet >(xCols->getByIndex( bInsertAfter?nNewStartColumn-1:nNewStartColumn+nNewColumns ), UNO_QUERY_THROW )->
+ getPropertyValue( sSize ) );
+ }
+
+ // Copy cell properties
+ sal_Int32 nPropSrcCol = (bInsertAfter ? aEnd.mnCol : aStart.mnCol + nNewColumns);
+ sal_Int32 nRowSpan = 0;
+ bool bNewSpan = false;
+
+ for( sal_Int32 nRow = 0; nRow < mxTable->getRowCount(); ++nRow )
+ {
+ CellRef xSourceCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nPropSrcCol, nRow ).get() ) );
+
+ // When we insert new COLUMNs, we want to copy ROW spans.
+ if (xSourceCell.is() && nRowSpan == 0)
+ {
+ // we are not in a span yet. Let's find out if the current cell is in a span.
+ sal_Int32 nColSpan = sal_Int32();
+ sal_Int32 nSpanInfoCol = sal_Int32();
+
+ if( xSourceCell->getRowSpan() > 1 )
+ {
+ // The current cell is the top-left cell in a span.
+ // Get the span info and propagate it to the target.
+ nRowSpan = xSourceCell->getRowSpan();
+ nColSpan = xSourceCell->getColumnSpan();
+ nSpanInfoCol = nPropSrcCol;
+ }
+ else if( xSourceCell->isMerged() )
+ {
+ // The current cell is a middle cell in a 2D span.
+ // Look for the top-left cell in the span.
+ for( nSpanInfoCol = nPropSrcCol - 1; nSpanInfoCol >= 0; --nSpanInfoCol )
+ {
+ CellRef xMergeInfoCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nSpanInfoCol, nRow ).get() ) );
+ if (xMergeInfoCell.is() && !xMergeInfoCell->isMerged())
+ {
+ nRowSpan = xMergeInfoCell->getRowSpan();
+ nColSpan = xMergeInfoCell->getColumnSpan();
+ break;
+ }
+ }
+ if( nRowSpan == 1 )
+ nRowSpan = 0;
+ }
+
+ // The target columns are outside the span; Start a new span.
+ if( nRowSpan > 0 && ( nNewStartColumn < nSpanInfoCol || nSpanInfoCol + nColSpan <= nNewStartColumn ) )
+ bNewSpan = true;
+ }
+
+ // Now copy the properties from the source to the targets
+ for( sal_Int32 nOffset = 0; nOffset < nNewColumns; nOffset++ )
+ {
+ CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nNewStartColumn + nOffset, nRow ).get() ) );
+ if( xTargetCell.is() )
+ {
+ if( nRowSpan > 0 )
+ {
+ if( bNewSpan )
+ xTargetCell->merge( 1, nRowSpan );
+ else
+ xTargetCell->setMerged();
+ }
+ xTargetCell->copyFormatFrom( xSourceCell );
+ }
+ }
+
+ if( nRowSpan > 0 )
+ {
+ --nRowSpan;
+ bNewSpan = false;
+ }
+ }
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ aStart.mnCol = nNewStartColumn;
+ aStart.mnRow = 0;
+ aEnd.mnCol = aStart.mnCol + nNewColumns - 1;
+ aEnd.mnRow = mxTable->getRowCount() - 1;
+ break;
+ }
+
+ case SID_TABLE_INSERT_ROW:
+ {
+ TableModelNotifyGuard aGuard( mxTable.get() );
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_TABLE_INSROW ) );
+ rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
+ }
+
+ Reference< XTableRows > xRows( mxTable->getRows() );
+ const sal_Int32 nNewRows = (nCount == 0) ? (aEnd.mnRow - aStart.mnRow + 1) : nCount;
+ const sal_Int32 nNewRowStart = aEnd.mnRow + (bInsertAfter ? 1 : 0);
+ xRows->insertByIndex( nNewRowStart, nNewRows );
+
+ for( sal_Int32 nOffset = 0; nOffset < nNewRows; nOffset++ )
+ {
+ Reference< XPropertySet >( xRows->getByIndex( aEnd.mnRow + nOffset + 1 ), UNO_QUERY_THROW )->
+ setPropertyValue( sSize,
+ Reference< XPropertySet >( xRows->getByIndex( aStart.mnRow + nOffset ), UNO_QUERY_THROW )->
+ getPropertyValue( sSize ) );
+ }
+
+ // Copy the cell properties
+ sal_Int32 nPropSrcRow = (bInsertAfter ? aEnd.mnRow : aStart.mnRow + nNewRows);
+ sal_Int32 nColSpan = 0;
+ bool bNewSpan = false;
+
+ for( sal_Int32 nCol = 0; nCol < mxTable->getColumnCount(); ++nCol )
+ {
+ CellRef xSourceCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nPropSrcRow ).get() ) );
+
+ if (!xSourceCell.is())
+ continue;
+
+ // When we insert new ROWs, we want to copy COLUMN spans.
+ if( nColSpan == 0 )
+ {
+ // we are not in a span yet. Let's find out if the current cell is in a span.
+ sal_Int32 nRowSpan = sal_Int32();
+ sal_Int32 nSpanInfoRow = sal_Int32();
+
+ if( xSourceCell->getColumnSpan() > 1 )
+ {
+ // The current cell is the top-left cell in a span.
+ // Get the span info and propagate it to the target.
+ nColSpan = xSourceCell->getColumnSpan();
+ nRowSpan = xSourceCell->getRowSpan();
+ nSpanInfoRow = nPropSrcRow;
+ }
+ else if( xSourceCell->isMerged() )
+ {
+ // The current cell is a middle cell in a 2D span.
+ // Look for the top-left cell in the span.
+ for( nSpanInfoRow = nPropSrcRow - 1; nSpanInfoRow >= 0; --nSpanInfoRow )
+ {
+ CellRef xMergeInfoCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nSpanInfoRow ).get() ) );
+ if (xMergeInfoCell.is() && !xMergeInfoCell->isMerged())
+ {
+ nColSpan = xMergeInfoCell->getColumnSpan();
+ nRowSpan = xMergeInfoCell->getRowSpan();
+ break;
+ }
+ }
+ if( nColSpan == 1 )
+ nColSpan = 0;
+ }
+
+ // Inserted rows are outside the span; Start a new span.
+ if( nColSpan > 0 && ( nNewRowStart < nSpanInfoRow || nSpanInfoRow + nRowSpan <= nNewRowStart ) )
+ bNewSpan = true;
+ }
+
+ // Now copy the properties from the source to the targets
+ for( sal_Int32 nOffset = 0; nOffset < nNewRows; ++nOffset )
+ {
+ CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nNewRowStart + nOffset ).get() ) );
+ if( xTargetCell.is() )
+ {
+ if( nColSpan > 0 )
+ {
+ if( bNewSpan )
+ xTargetCell->merge( nColSpan, 1 );
+ else
+ xTargetCell->setMerged();
+ }
+ xTargetCell->copyFormatFrom( xSourceCell );
+ }
+ }
+
+ if( nColSpan > 0 )
+ {
+ --nColSpan;
+ bNewSpan = false;
+ }
+ }
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ aStart.mnCol = 0;
+ aStart.mnRow = nNewRowStart;
+ aEnd.mnCol = mxTable->getColumnCount() - 1;
+ aEnd.mnRow = aStart.mnRow + nNewRows - 1;
+ break;
+ }
+ }
+
+ StartSelection( aStart );
+ UpdateSelection( aEnd );
+}
+
+
+void SvxTableController::onDelete( sal_uInt16 nSId )
+{
+ sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
+ if( !pTableObj || !mxTable.is() )
+ return;
+
+ if( nSId == SID_TABLE_DELETE_TABLE )
+ {
+ if( pTableObj->IsTextEditActive() )
+ mrView.SdrEndTextEdit(true);
+
+ mrView.DeleteMarkedObj();
+ }
+ else if( hasSelectedCells() )
+ {
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+
+ if( pTableObj->IsTextEditActive() )
+ mrView.SdrEndTextEdit(true);
+
+ RemoveSelection();
+
+ bool bDeleteTable = false;
+ switch( nSId )
+ {
+ case SID_TABLE_DELETE_COL:
+ {
+ const sal_Int32 nRemovedColumns = aEnd.mnCol - aStart.mnCol + 1;
+ if( nRemovedColumns == mxTable->getColumnCount() )
+ {
+ bDeleteTable = true;
+ }
+ else
+ {
+ Reference< XTableColumns > xCols( mxTable->getColumns() );
+ xCols->removeByIndex( aStart.mnCol, nRemovedColumns );
+ EditCell(aStart, nullptr, TblAction::NONE);
+ }
+ break;
+ }
+
+ case SID_TABLE_DELETE_ROW:
+ {
+ const sal_Int32 nRemovedRows = aEnd.mnRow - aStart.mnRow + 1;
+ if( nRemovedRows == mxTable->getRowCount() )
+ {
+ bDeleteTable = true;
+ }
+ else
+ {
+ Reference< XTableRows > xRows( mxTable->getRows() );
+ xRows->removeByIndex( aStart.mnRow, nRemovedRows );
+ EditCell(aStart, nullptr, TblAction::NONE);
+ }
+ break;
+ }
+ }
+
+ if( bDeleteTable )
+ mrView.DeleteMarkedObj();
+ else
+ UpdateTableShape();
+ }
+}
+
+
+void SvxTableController::onSelect( sal_uInt16 nSId )
+{
+ if( !mxTable.is() )
+ return;
+
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+ if( !(nRowCount && nColCount) )
+ return;
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+
+ switch( nSId )
+ {
+ case SID_TABLE_SELECT_ALL:
+ aEnd.mnCol = 0; aEnd.mnRow = 0;
+ aStart.mnCol = nColCount - 1; aStart.mnRow = nRowCount - 1;
+ break;
+ case SID_TABLE_SELECT_COL:
+ aEnd.mnRow = nRowCount - 1;
+ aStart.mnRow = 0;
+ break;
+ case SID_TABLE_SELECT_ROW:
+ aEnd.mnCol = nColCount - 1;
+ aStart.mnCol = 0;
+ break;
+ }
+
+ StartSelection( aEnd );
+ gotoCell( aStart, true, nullptr );
+}
+
+namespace
+{
+ SvxBoxItem mergeDrawinglayerTextDistancesAndSvxBoxItem(const SfxItemSet& rAttrSet)
+ {
+ // merge drawing layer text distance items into SvxBoxItem used by the dialog
+ SvxBoxItem aBoxItem( rAttrSet.Get( SDRATTR_TABLE_BORDER ) );
+ aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_LEFTDIST).GetValue()), SvxBoxItemLine::LEFT );
+ aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_RIGHTDIST).GetValue()), SvxBoxItemLine::RIGHT );
+ aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_UPPERDIST).GetValue()), SvxBoxItemLine::TOP );
+ aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_LOWERDIST).GetValue()), SvxBoxItemLine::BOTTOM );
+ return aBoxItem;
+ }
+}
+
+void SvxTableController::onFormatTable(const SfxRequest& rReq)
+{
+ if(!mxTableObj.is())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ const SfxItemSet* pArgs = rReq.GetArgs();
+
+ if(pArgs)
+ return;
+
+ SfxItemSet aNewAttr(rModel.GetItemPool());
+
+ // merge drawing layer text distance items into SvxBoxItem used by the dialog
+ SvxBoxItem aBoxItem(mergeDrawinglayerTextDistancesAndSvxBoxItem(aNewAttr));
+
+ SvxBoxInfoItem aBoxInfoItem( aNewAttr.Get( SDRATTR_TABLE_BORDER_INNER ) );
+
+ MergeAttrFromSelectedCells(aNewAttr, false);
+ FillCommonBorderAttrFromSelectedCells( aBoxItem, aBoxInfoItem );
+ aNewAttr.Put( aBoxItem );
+ aNewAttr.Put( aBoxInfoItem );
+
+ // Fill in shadow properties.
+ const SfxItemSet& rTableItemSet = rTableObj.GetMergedItemSet();
+ for (sal_uInt16 nWhich = SDRATTR_SHADOW_FIRST; nWhich <= SDRATTR_SHADOW_LAST; ++nWhich)
+ {
+ if (rTableItemSet.GetItemState(nWhich, false) != SfxItemState::SET)
+ {
+ continue;
+ }
+
+ aNewAttr.Put(rTableItemSet.Get(nWhich));
+ }
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ VclPtr<SfxAbstractTabDialog> xDlg( pFact->CreateSvxFormatCellsDialog(
+ rReq.GetFrameWeld(),
+ &aNewAttr,
+ rModel) );
+
+ // Even Cancel Button is returning positive(101) value,
+ xDlg->StartExecuteAsync([xDlg, this, aBoxItem, aBoxInfoItem](int nResult){
+ if (nResult == RET_OK)
+ {
+ SfxItemSet aNewSet(*(xDlg->GetOutputItemSet()));
+
+ //Only properties that were unchanged by the dialog appear in this
+ //itemset. We had constructed these two properties from other
+ //ones, so if they were not changed, then forcible set them back to
+ //their originals in the new result set so we can decompose that
+ //unchanged state back to their input properties
+ if (aNewSet.GetItemState(SDRATTR_TABLE_BORDER, false) != SfxItemState::SET)
+ {
+ aNewSet.Put(aBoxItem);
+ }
+ if (aNewSet.GetItemState(SDRATTR_TABLE_BORDER_INNER, false) != SfxItemState::SET)
+ {
+ aNewSet.Put(aBoxInfoItem);
+ }
+
+ SvxBoxItem aNewBoxItem( aNewSet.Get( SDRATTR_TABLE_BORDER ) );
+
+ if( aNewBoxItem.GetDistance( SvxBoxItemLine::LEFT ) != aBoxItem.GetDistance( SvxBoxItemLine::LEFT ) )
+ aNewSet.Put(makeSdrTextLeftDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::LEFT ) ) );
+
+ if( aNewBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) != aBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) )
+ aNewSet.Put(makeSdrTextRightDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) ) );
+
+ if( aNewBoxItem.GetDistance( SvxBoxItemLine::TOP ) != aBoxItem.GetDistance( SvxBoxItemLine::TOP ) )
+ aNewSet.Put(makeSdrTextUpperDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::TOP ) ) );
+
+ if( aNewBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) != aBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) )
+ aNewSet.Put(makeSdrTextLowerDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) ) );
+
+ if (checkTableObject() && mxTable.is())
+ {
+ // Create a single undo action when applying the result of the dialog.
+ SdrTableObj& rTableObject(*mxTableObj);
+ SdrModel& rSdrModel(rTableObject.getSdrModelFromSdrObject());
+ bool bUndo = rSdrModel.IsUndoEnabled() && !mrView.IsTextEdit();
+ if (bUndo)
+ {
+ rSdrModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
+ }
+
+ this->SetAttrToSelectedCells(aNewSet, false);
+
+ this->SetAttrToSelectedShape(aNewSet);
+
+ if (bUndo)
+ {
+ rSdrModel.EndUndo();
+ }
+ }
+ }
+
+ xDlg->disposeOnce();
+ });
+}
+
+void SvxTableController::Execute( SfxRequest& rReq )
+{
+ const sal_uInt16 nSId = rReq.GetSlot();
+ switch( nSId )
+ {
+ case SID_TABLE_INSERT_ROW:
+ case SID_TABLE_INSERT_COL:
+ onInsert( nSId, rReq.GetArgs() );
+ break;
+ case SID_TABLE_DELETE_ROW:
+ case SID_TABLE_DELETE_COL:
+ case SID_TABLE_DELETE_TABLE:
+ onDelete( nSId );
+ break;
+ case SID_TABLE_SELECT_ALL:
+ case SID_TABLE_SELECT_COL:
+ case SID_TABLE_SELECT_ROW:
+ onSelect( nSId );
+ break;
+ case SID_FORMAT_TABLE_DLG:
+ onFormatTable( rReq );
+ break;
+
+ case SID_FRAME_LINESTYLE:
+ case SID_FRAME_LINECOLOR:
+ case SID_ATTR_BORDER:
+ {
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if( pArgs )
+ ApplyBorderAttr( *pArgs );
+ }
+ break;
+
+ case SID_ATTR_FILL_STYLE:
+ {
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if( pArgs )
+ SetAttributes( *pArgs, false );
+ }
+ break;
+
+ case SID_TABLE_MERGE_CELLS:
+ MergeMarkedCells();
+ break;
+
+ case SID_TABLE_SPLIT_CELLS:
+ SplitMarkedCells(rReq);
+ break;
+
+ case SID_TABLE_MINIMAL_COLUMN_WIDTH:
+ DistributeColumns(/*bOptimize=*/true, /*bMinimize=*/true);
+ break;
+
+ case SID_TABLE_OPTIMAL_COLUMN_WIDTH:
+ DistributeColumns(/*bOptimize=*/true, /*bMinimize=*/false);
+ break;
+
+ case SID_TABLE_DISTRIBUTE_COLUMNS:
+ DistributeColumns(/*bOptimize=*/false, /*bMinimize=*/false);
+ break;
+
+ case SID_TABLE_MINIMAL_ROW_HEIGHT:
+ DistributeRows(/*bOptimize=*/true, /*bMinimize=*/true);
+ break;
+
+ case SID_TABLE_OPTIMAL_ROW_HEIGHT:
+ DistributeRows(/*bOptimize=*/true, /*bMinimize=*/false);
+ break;
+
+ case SID_TABLE_DISTRIBUTE_ROWS:
+ DistributeRows(/*bOptimize=*/false, /*bMinimize=*/false);
+ break;
+
+ case SID_TABLE_VERT_BOTTOM:
+ case SID_TABLE_VERT_CENTER:
+ case SID_TABLE_VERT_NONE:
+ SetVertical( nSId );
+ break;
+
+ case SID_AUTOFORMAT:
+ case SID_TABLE_SORT_DIALOG:
+ case SID_TABLE_AUTOSUM:
+ default:
+ break;
+
+ case SID_TABLE_STYLE:
+ SetTableStyle( rReq.GetArgs() );
+ break;
+
+ case SID_TABLE_STYLE_SETTINGS:
+ SetTableStyleSettings( rReq.GetArgs() );
+ break;
+ case SID_TABLE_CHANGE_CURRENT_BORDER_POSITION:
+ changeTableEdge(rReq);
+ break;
+ }
+}
+
+void SvxTableController::SetTableStyle( const SfxItemSet* pArgs )
+{
+ if(!checkTableObject())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+
+ if(!pArgs || (SfxItemState::SET != pArgs->GetItemState(SID_TABLE_STYLE, false)))
+ return;
+
+ const SfxStringItem* pArg = dynamic_cast< const SfxStringItem* >( &pArgs->Get( SID_TABLE_STYLE ) );
+ if( !(pArg && mxTable.is()) )
+ return;
+
+ try
+ {
+ Reference< XStyleFamiliesSupplier > xSFS( rModel.getUnoModel(), UNO_QUERY_THROW );
+ Reference< XNameAccess > xFamilyNameAccess( xSFS->getStyleFamilies(), UNO_SET_THROW );
+ Reference< XNameAccess > xTableFamilyAccess( xFamilyNameAccess->getByName( "table" ), UNO_QUERY_THROW );
+
+ if( xTableFamilyAccess->hasByName( pArg->GetValue() ) )
+ {
+ // found table style with the same name
+ Reference< XIndexAccess > xNewTableStyle( xTableFamilyAccess->getByName( pArg->GetValue() ), UNO_QUERY_THROW );
+
+ const bool bUndo = rModel.IsUndoEnabled();
+
+ if( bUndo )
+ {
+ rModel.BegUndo(SvxResId(STR_TABLE_STYLE));
+ rModel.AddUndo(std::make_unique<TableStyleUndo>(rTableObj));
+ }
+
+ rTableObj.setTableStyle( xNewTableStyle );
+
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+ for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
+ {
+ for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ ) try
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() )
+ {
+ SfxItemSet aSet( xCell->GetItemSet() );
+ bool bChanges = false;
+ SfxStyleSheet *pStyleSheet = xCell->GetStyleSheet();
+ SAL_WARN_IF(!pStyleSheet, "svx", "no stylesheet for table cell?");
+ if (pStyleSheet)
+ {
+ const SfxItemSet& rStyleAttribs = pStyleSheet->GetItemSet();
+
+ for ( sal_uInt16 nWhich = SDRATTR_START; nWhich <= SDRATTR_TABLE_LAST; nWhich++ )
+ {
+ if( (rStyleAttribs.GetItemState( nWhich ) == SfxItemState::SET) && (aSet.GetItemState( nWhich ) == SfxItemState::SET) )
+ {
+ aSet.ClearItem( nWhich );
+ bChanges = true;
+ }
+ }
+ }
+
+ if( bChanges )
+ {
+ if( bUndo )
+ xCell->AddUndo();
+
+ xCell->SetMergedItemSetAndBroadcast( aSet, true );
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+ }
+
+ if( bUndo )
+ rModel.EndUndo();
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+}
+
+void SvxTableController::SetTableStyleSettings( const SfxItemSet* pArgs )
+{
+ if(!checkTableObject())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+
+ TableStyleSettings aSettings(rTableObj.getTableStyleSettings() );
+ const SfxBoolItem *pPoolItem=nullptr;
+
+ if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEFIRSTROWSTYLE, false)) )
+ aSettings.mbUseFirstRow = pPoolItem->GetValue();
+
+ if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USELASTROWSTYLE, false)) )
+ aSettings.mbUseLastRow = pPoolItem->GetValue();
+
+ if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEBANDINGROWSTYLE, false)) )
+ aSettings.mbUseRowBanding = pPoolItem->GetValue();
+
+ if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEFIRSTCOLUMNSTYLE, false)) )
+ aSettings.mbUseFirstColumn = pPoolItem->GetValue();
+
+ if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USELASTCOLUMNSTYLE, false)) )
+ aSettings.mbUseLastColumn = pPoolItem->GetValue();
+
+ if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEBANDINGCOLUMNSTYLE, false)) )
+ aSettings.mbUseColumnBanding = pPoolItem->GetValue();
+
+ if( aSettings == rTableObj.getTableStyleSettings() )
+ return;
+
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_TABLE_STYLE_SETTINGS) );
+ rModel.AddUndo(std::make_unique<TableStyleUndo>(rTableObj));
+ }
+
+ rTableObj.setTableStyleSettings( aSettings );
+
+ if( bUndo )
+ rModel.EndUndo();
+}
+
+void SvxTableController::SetVertical( sal_uInt16 nSId )
+{
+ if(!checkTableObject())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+
+ TableModelNotifyGuard aGuard( mxTable.get() );
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ if (bUndo)
+ {
+ rModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
+ rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoAttrObject(rTableObj));
+ }
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+
+ SdrTextVertAdjust eAdj = SDRTEXTVERTADJUST_TOP;
+
+ switch( nSId )
+ {
+ case SID_TABLE_VERT_BOTTOM:
+ eAdj = SDRTEXTVERTADJUST_BOTTOM;
+ break;
+ case SID_TABLE_VERT_CENTER:
+ eAdj = SDRTEXTVERTADJUST_CENTER;
+ break;
+ //case SID_TABLE_VERT_NONE:
+ default:
+ break;
+ }
+
+ SdrTextVertAdjustItem aItem( eAdj );
+
+ for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
+ {
+ for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() )
+ {
+ if (bUndo)
+ xCell->AddUndo();
+ SfxItemSet aSet(xCell->GetItemSet());
+ aSet.Put(aItem);
+ xCell->SetMergedItemSetAndBroadcast(aSet, /*bClearAllItems=*/false);
+ }
+ }
+ }
+
+ UpdateTableShape();
+
+ if (bUndo)
+ rModel.EndUndo();
+}
+
+void SvxTableController::MergeMarkedCells()
+{
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+ SdrTableObj* pTableObj = mxTableObj.get();
+ if( pTableObj )
+ {
+ if( pTableObj->IsTextEditActive() )
+ mrView.SdrEndTextEdit(true);
+
+ TableModelNotifyGuard aGuard( mxTable.get() );
+ MergeRange( aStart.mnCol, aStart.mnRow, aEnd.mnCol, aEnd.mnRow );
+ }
+}
+
+void SvxTableController::SplitMarkedCells(const SfxRequest& rReq)
+{
+ if(!checkTableObject() || !mxTable.is())
+ return;
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ VclPtr<SvxAbstractSplitTableDialog> xDlg(pFact->CreateSvxSplitTableDialog(rReq.GetFrameWeld(), false, 99));
+
+ xDlg->StartExecuteAsync([xDlg, this](int) {
+ const sal_Int32 nCount = xDlg->GetCount() - 1;
+
+ if( nCount < 1 )
+ return;
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+ Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( aStart.mnCol, aStart.mnRow, aEnd.mnCol, aEnd.mnRow ) ), UNO_QUERY_THROW );
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+ SdrTableObj& rTableObj(*mxTableObj);
+
+ if( rTableObj.IsTextEditActive() )
+ mrView.SdrEndTextEdit(true);
+
+ TableModelNotifyGuard aGuard( mxTable.get() );
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_TABLE_SPLIT) );
+ rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
+ }
+
+ if( xDlg->IsHorizontal() )
+ {
+ xRange->split( 0, nCount );
+ }
+ else
+ {
+ xRange->split( nCount, 0 );
+ }
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ aEnd.mnRow += mxTable->getRowCount() - nRowCount;
+ aEnd.mnCol += mxTable->getColumnCount() - nColCount;
+
+ setSelectedCells( aStart, aEnd );
+
+ xDlg->disposeOnce();
+ });
+}
+
+void SvxTableController::DistributeColumns(const bool bOptimize, const bool bMinimize)
+{
+ if(!checkTableObject())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_TABLE_DISTRIBUTE_COLUMNS) );
+ rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
+ }
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+ rTableObj.DistributeColumns( aStart.mnCol, aEnd.mnCol, bOptimize, bMinimize );
+
+ if( bUndo )
+ rModel.EndUndo();
+}
+
+void SvxTableController::DistributeRows(const bool bOptimize, const bool bMinimize)
+{
+ if(!checkTableObject())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_TABLE_DISTRIBUTE_ROWS) );
+ rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
+ }
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+ rTableObj.DistributeRows( aStart.mnRow, aEnd.mnRow, bOptimize, bMinimize );
+
+ if( bUndo )
+ rModel.EndUndo();
+}
+
+bool SvxTableController::HasMarked() const
+{
+ return mbCellSelectionMode && mxTable.is();
+}
+
+bool SvxTableController::DeleteMarked()
+{
+ if(!checkTableObject() || !HasMarked())
+ return false;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ const bool bUndo(rModel.IsUndoEnabled());
+ bool bDeleteTable = false;
+
+ if (bUndo)
+ rModel.BegUndo(SvxResId(STR_TABLE_DELETE_CELL_CONTENTS));
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+ const sal_Int32 nRemovedColumns = aEnd.mnCol - aStart.mnCol + 1;
+ const sal_Int32 nRemovedRows = aEnd.mnRow - aStart.mnRow + 1;
+ if( nRemovedColumns == mxTable->getColumnCount() && nRemovedRows == mxTable->getRowCount())
+ {
+ bDeleteTable = true;
+ }
+ else
+ {
+ for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
+ {
+ for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if (xCell.is() && xCell->hasText())
+ {
+ if (bUndo)
+ xCell->AddUndo();
+ xCell->SetOutlinerParaObject(std::nullopt);
+ }
+ }
+ }
+ }
+
+ if (bDeleteTable)
+ mrView.DeleteMarkedObj();
+
+ if (bUndo)
+ rModel.EndUndo();
+
+ if (!bDeleteTable)
+ UpdateTableShape();
+ return true;
+}
+
+bool SvxTableController::GetStyleSheet( SfxStyleSheet*& rpStyleSheet ) const
+{
+ if( hasSelectedCells() )
+ {
+ rpStyleSheet = nullptr;
+
+ if( mxTable.is() )
+ {
+ SfxStyleSheet* pRet=nullptr;
+ bool b1st=true;
+
+ CellPos aStart, aEnd;
+ const_cast<SvxTableController&>(*this).getSelectedCells( aStart, aEnd );
+
+ for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
+ {
+ for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() )
+ {
+ SfxStyleSheet* pSS=xCell->GetStyleSheet();
+ if(b1st)
+ {
+ pRet=pSS;
+ }
+ else if(pRet != pSS)
+ {
+ return true;
+ }
+ b1st=false;
+ }
+ }
+ }
+ rpStyleSheet = pRet;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SvxTableController::SetStyleSheet( SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr )
+{
+ if( hasSelectedCells() && (!pStyleSheet || pStyleSheet->GetFamily() == SfxStyleFamily::Frame) )
+ {
+ if( mxTable.is() )
+ {
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+
+ for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
+ {
+ for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() )
+ xCell->SetStyleSheet(pStyleSheet,bDontRemoveHardAttr);
+ }
+ }
+
+ UpdateTableShape();
+ return true;
+ }
+ }
+ return false;
+}
+
+void SvxTableController::changeTableEdge(const SfxRequest& rReq)
+{
+ if (!checkTableObject())
+ return;
+
+ const auto* pType = rReq.GetArg<SfxStringItem>(SID_TABLE_BORDER_TYPE);
+ const auto* pIndex = rReq.GetArg<SfxUInt16Item>(SID_TABLE_BORDER_INDEX);
+ const auto* pOffset = rReq.GetArg<SfxInt32Item>(SID_TABLE_BORDER_OFFSET);
+
+ if (!(pType && pIndex && pOffset))
+ return;
+
+ const OUString sType = pType->GetValue();
+ const sal_uInt16 nIndex = pIndex->GetValue();
+ const sal_Int32 nOffset = convertTwipToMm100(pOffset->GetValue());
+
+ SdrTableObj& rTableObj(*mxTableObj);
+
+ sal_Int32 nEdgeIndex = -1;
+ bool bHorizontal = sType.startsWith("row");
+
+ if (sType == "column-left" || sType == "row-left")
+ {
+ nEdgeIndex = 0;
+ }
+ else if (sType == "column-right")
+ {
+ // Number of edges = number of columns + 1
+ nEdgeIndex = rTableObj.getColumnCount();
+ }
+ else if (sType == "row-right")
+ {
+ // Number of edges = number of rows + 1
+ nEdgeIndex = rTableObj.getRowCount();
+ }
+ else if (sType == "column-middle" || sType == "row-middle")
+ {
+ nEdgeIndex = nIndex + 1;
+ }
+
+ if (nEdgeIndex < 0)
+ return;
+
+ TableModelNotifyGuard aGuard(mxTable.get());
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ const bool bUndo(rModel.IsUndoEnabled());
+ if (bUndo)
+ {
+ auto pUndoObject = rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj);
+ rModel.BegUndo(pUndoObject->GetComment());
+
+ auto* pGeoUndo = static_cast<SdrUndoGeoObj*>(pUndoObject.get());
+ if (pGeoUndo)
+ pGeoUndo->SetSkipChangeLayout(true);
+
+ rModel.AddUndo(std::move(pUndoObject));
+ }
+ tools::Rectangle aBoundRect;
+ if (rTableObj.GetUserCall())
+ aBoundRect = rTableObj.GetLastBoundRect();
+ rTableObj.changeEdge(bHorizontal, nEdgeIndex, nOffset);
+ rTableObj.SetChanged();
+ rTableObj.SendUserCall(SdrUserCallType::Resize, aBoundRect);
+ if (bUndo)
+ rModel.EndUndo();
+}
+
+// internals
+
+
+bool SvxTableController::checkTableObject()
+{
+ return mxTableObj.is();
+}
+
+
+SvxTableController::TblAction SvxTableController::getKeyboardAction(const KeyEvent& rKEvt)
+{
+ const bool bMod1 = rKEvt.GetKeyCode().IsMod1(); // ctrl
+ const bool bMod2 = rKEvt.GetKeyCode().IsMod2(); // Alt
+ const bool bTextEdit = mrView.IsTextEdit();
+
+ TblAction nAction = TblAction::HandledByView;
+
+ sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
+ if( !pTableObj )
+ return nAction;
+
+ // handle special keys
+ const sal_Int16 nCode = rKEvt.GetKeyCode().GetCode();
+ switch( nCode )
+ {
+ case awt::Key::ESCAPE: // handle escape
+ {
+ if( bTextEdit )
+ {
+ // escape during text edit ends text edit
+ nAction = TblAction::StopTextEdit;
+ }
+ if( mbCellSelectionMode )
+ {
+ // escape with selected cells removes selection
+ nAction = TblAction::RemoveSelection;
+ }
+ break;
+ }
+ case awt::Key::RETURN: // handle return
+ {
+ if( !bMod1 && !bMod2 && !bTextEdit )
+ {
+ // when not already editing, return starts text edit
+ setSelectionStart( SdrTableObj::getFirstCell() );
+ nAction = TblAction::EditCell;
+ }
+ break;
+ }
+ case awt::Key::F2: // f2 toggles text edit
+ {
+ if( bMod1 || bMod2 ) // f2 with modifiers is handled by the view
+ {
+ }
+ else if( bTextEdit )
+ {
+ // f2 during text edit stops text edit
+ nAction = TblAction::StopTextEdit;
+ }
+ else if( mbCellSelectionMode )
+ {
+ // f2 with selected cells removes selection
+ nAction = TblAction::RemoveSelection;
+ }
+ else
+ {
+ // f2 with no selection and no text edit starts text edit
+ setSelectionStart( SdrTableObj::getFirstCell() );
+ nAction = TblAction::EditCell;
+ }
+ break;
+ }
+ case awt::Key::HOME:
+ case awt::Key::NUM7:
+ {
+ if( (bMod1 || bMod2) && (bTextEdit || mbCellSelectionMode) )
+ {
+ if( bMod1 && !bMod2 )
+ {
+ // ctrl + home jumps to first cell
+ nAction = TblAction::GotoFirstCell;
+ }
+ else if( !bMod1 && bMod2 )
+ {
+ // alt + home jumps to first column
+ nAction = TblAction::GotoFirstColumn;
+ }
+ }
+ break;
+ }
+ case awt::Key::END:
+ case awt::Key::NUM1:
+ {
+ if( (bMod1 || bMod2) && (bTextEdit || mbCellSelectionMode) )
+ {
+ if( bMod1 && !bMod2 )
+ {
+ // ctrl + end jumps to last cell
+ nAction = TblAction::GotoLastCell;
+ }
+ else if( !bMod1 && bMod2 )
+ {
+ // alt + home jumps to last column
+ nAction = TblAction::GotoLastColumn;
+ }
+ }
+ break;
+ }
+
+ case awt::Key::TAB:
+ {
+ if( bTextEdit || mbCellSelectionMode )
+ nAction = TblAction::Tab;
+ break;
+ }
+
+ case awt::Key::UP:
+ case awt::Key::NUM8:
+ case awt::Key::DOWN:
+ case awt::Key::NUM2:
+ case awt::Key::LEFT:
+ case awt::Key::NUM4:
+ case awt::Key::RIGHT:
+ case awt::Key::NUM6:
+ {
+
+ if( !bMod1 && bMod2 )
+ {
+ if(bTextEdit || mbCellSelectionMode)
+ {
+ if( (nCode == awt::Key::UP) || (nCode == awt::Key::NUM8) )
+ {
+ nAction = TblAction::GotoLeftCell;
+ break;
+ }
+ else if( (nCode == awt::Key::DOWN) || (nCode == awt::Key::NUM2) )
+ {
+ nAction = TblAction::GotoRightCell;
+ break;
+ }
+ }
+ }
+
+ bool bTextMove = false;
+ OutlinerView* pOLV = mrView.GetTextEditOutlinerView();
+ if( pOLV )
+ {
+ RemoveSelection();
+ // during text edit, check if we navigate out of the cell
+ ESelection aOldSelection = pOLV->GetSelection();
+ pOLV->PostKeyEvent(rKEvt);
+ bTextMove = aOldSelection == pOLV->GetSelection();
+ if( !bTextMove )
+ {
+ nAction = TblAction::NONE;
+ }
+ }
+
+ if( mbCellSelectionMode || bTextMove )
+ {
+ // no text edit, navigate in cells if selection active
+ switch( nCode )
+ {
+ case awt::Key::LEFT:
+ case awt::Key::NUM4:
+ nAction = TblAction::GotoLeftCell;
+ break;
+ case awt::Key::RIGHT:
+ case awt::Key::NUM6:
+ nAction = TblAction::GotoRightCell;
+ break;
+ case awt::Key::DOWN:
+ case awt::Key::NUM2:
+ nAction = TblAction::GotoDownCell;
+ break;
+ case awt::Key::UP:
+ case awt::Key::NUM8:
+ nAction = TblAction::GotoUpCell;
+ break;
+ }
+ }
+ break;
+ }
+ case awt::Key::PAGEUP:
+ if( bMod2 )
+ nAction = TblAction::GotoFirstRow;
+ break;
+
+ case awt::Key::PAGEDOWN:
+ if( bMod2 )
+ nAction = TblAction::GotoLastRow;
+ break;
+ }
+ return nAction;
+}
+
+bool SvxTableController::executeAction(TblAction nAction, bool bSelect, vcl::Window* pWindow)
+{
+ sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
+ if( !pTableObj )
+ return false;
+
+ switch( nAction )
+ {
+ case TblAction::GotoFirstCell:
+ {
+ gotoCell( SdrTableObj::getFirstCell(), bSelect, pWindow, nAction );
+ break;
+ }
+
+ case TblAction::GotoLeftCell:
+ {
+ gotoCell( pTableObj->getLeftCell( getSelectionEnd(), !bSelect ), bSelect, pWindow, nAction );
+ break;
+ }
+
+ case TblAction::GotoRightCell:
+ {
+ gotoCell( pTableObj->getRightCell( getSelectionEnd(), !bSelect ), bSelect, pWindow, nAction);
+ break;
+ }
+
+ case TblAction::GotoLastCell:
+ {
+ gotoCell( pTableObj->getLastCell(), bSelect, pWindow, nAction );
+ break;
+ }
+
+ case TblAction::GotoFirstColumn:
+ {
+ CellPos aPos( SdrTableObj::getFirstCell().mnCol, getSelectionEnd().mnRow );
+ gotoCell( aPos, bSelect, pWindow, nAction );
+ break;
+ }
+
+ case TblAction::GotoLastColumn:
+ {
+ CellPos aPos( pTableObj->getLastCell().mnCol, getSelectionEnd().mnRow );
+ gotoCell( aPos, bSelect, pWindow, nAction );
+ break;
+ }
+
+ case TblAction::GotoFirstRow:
+ {
+ CellPos aPos( getSelectionEnd().mnCol, SdrTableObj::getFirstCell().mnRow );
+ gotoCell( aPos, bSelect, pWindow, nAction );
+ break;
+ }
+
+ case TblAction::GotoUpCell:
+ {
+ gotoCell( pTableObj->getUpCell(getSelectionEnd(), !bSelect), bSelect, pWindow, nAction );
+ break;
+ }
+
+ case TblAction::GotoDownCell:
+ {
+ gotoCell( pTableObj->getDownCell(getSelectionEnd(), !bSelect), bSelect, pWindow, nAction );
+ break;
+ }
+
+ case TblAction::GotoLastRow:
+ {
+ CellPos aPos( getSelectionEnd().mnCol, pTableObj->getLastCell().mnRow );
+ gotoCell( aPos, bSelect, pWindow, nAction );
+ break;
+ }
+
+ case TblAction::EditCell:
+ EditCell( getSelectionStart(), pWindow, nAction );
+ break;
+
+ case TblAction::StopTextEdit:
+ StopTextEdit();
+ break;
+
+ case TblAction::RemoveSelection:
+ RemoveSelection();
+ break;
+
+ case TblAction::Tab:
+ {
+ if( bSelect )
+ gotoCell( pTableObj->getPreviousCell( getSelectionEnd(), true ), false, pWindow, nAction );
+ else
+ {
+ CellPos aSelectionEnd( getSelectionEnd() );
+ CellPos aNextCell( pTableObj->getNextCell( aSelectionEnd, true ) );
+ if( aSelectionEnd == aNextCell )
+ {
+ onInsert( SID_TABLE_INSERT_ROW );
+ aNextCell = pTableObj->getNextCell( aSelectionEnd, true );
+ }
+ gotoCell( aNextCell, false, pWindow, nAction );
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return nAction != TblAction::HandledByView;
+}
+
+
+void SvxTableController::gotoCell(const CellPos& rPos, bool bSelect, vcl::Window* pWindow, TblAction nAction /*= TblAction::NONE */)
+{
+ if( mxTableObj.is() && mxTableObj->IsTextEditActive() )
+ mrView.SdrEndTextEdit(true);
+
+ if( bSelect )
+ {
+ maCursorLastPos = rPos;
+ if( mxTableObj.is() )
+ mxTableObj->setActiveCell( rPos );
+
+ if( !mbCellSelectionMode )
+ {
+ setSelectedCells( maCursorFirstPos, rPos );
+ }
+ else
+ {
+ UpdateSelection( rPos );
+ }
+ }
+ else
+ {
+ RemoveSelection();
+ EditCell( rPos, pWindow, nAction );
+ }
+}
+
+
+const CellPos& SvxTableController::getSelectionStart()
+{
+ checkCell( maCursorFirstPos );
+ return maCursorFirstPos;
+}
+
+
+void SvxTableController::setSelectionStart( const CellPos& rPos )
+{
+ maCursorFirstPos = rPos;
+}
+
+
+const CellPos& SvxTableController::getSelectionEnd()
+{
+ checkCell( maCursorLastPos );
+ return maCursorLastPos;
+}
+
+
+void SvxTableController::MergeRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow )
+{
+ if(!checkTableObject() || !mxTable.is())
+ return;
+
+ try
+ {
+ Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( nFirstCol, nFirstRow,nLastCol, nLastRow ) ), UNO_QUERY_THROW );
+
+ if( xRange->isMergeable() )
+ {
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_TABLE_MERGE) );
+ rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
+ }
+
+ xRange->merge();
+ mbHasJustMerged = true;
+ setSelectedCells( maCursorFirstPos, maCursorFirstPos );
+
+ if( bUndo )
+ rModel.EndUndo();
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.table", "" );
+ }
+}
+
+
+void SvxTableController::checkCell( CellPos& rPos ) const
+{
+ if( !mxTable.is() )
+ return;
+
+ try
+ {
+ if( rPos.mnCol >= mxTable->getColumnCount() )
+ rPos.mnCol = mxTable->getColumnCount()-1;
+
+ if( rPos.mnRow >= mxTable->getRowCount() )
+ rPos.mnRow = mxTable->getRowCount()-1;
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+}
+
+
+void SvxTableController::findMergeOrigin( CellPos& rPos )
+{
+ if( !mxTable.is() )
+ return;
+
+ try
+ {
+ Reference< XMergeableCell > xCell( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ), UNO_QUERY_THROW );
+ if( xCell->isMerged() )
+ {
+ ::findMergeOrigin( mxTable, rPos.mnCol, rPos.mnRow, rPos.mnCol, rPos.mnRow );
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+}
+
+
+void SvxTableController::EditCell(const CellPos& rPos, vcl::Window* pWindow, TblAction nAction /*= TblAction::NONE */)
+{
+ SdrPageView* pPV(mrView.GetSdrPageView());
+
+ if(nullptr == pPV || !checkTableObject())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+
+ if(rTableObj.getSdrPageFromSdrObject() != pPV->GetPage())
+ return;
+
+ bool bEmptyOutliner = false;
+
+ if(!rTableObj.GetOutlinerParaObject() && mrView.GetTextEditOutliner())
+ {
+ ::Outliner* pOutl = mrView.GetTextEditOutliner();
+ sal_Int32 nParaCnt = pOutl->GetParagraphCount();
+ Paragraph* p1stPara = pOutl->GetParagraph( 0 );
+
+ if(nParaCnt==1 && p1stPara)
+ {
+ // with only one paragraph
+ if (pOutl->GetText(p1stPara).isEmpty())
+ {
+ bEmptyOutliner = true;
+ }
+ }
+ }
+
+ CellPos aPos( rPos );
+ findMergeOrigin( aPos );
+
+ if( &rTableObj == mrView.GetTextEditObject() && !bEmptyOutliner && rTableObj.IsTextEditActive( aPos ) )
+ return;
+
+ if( rTableObj.IsTextEditActive() )
+ mrView.SdrEndTextEdit(true);
+
+ rTableObj.setActiveCell( aPos );
+
+ // create new outliner, owner will be the SdrObjEditView
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ std::unique_ptr<SdrOutliner> pOutl(SdrMakeOutliner(OutlinerMode::OutlineObject, rModel));
+
+ if (pOutl && rTableObj.IsVerticalWriting())
+ pOutl->SetVertical( true );
+
+ if (!mrView.SdrBeginTextEdit(&rTableObj, pPV, pWindow, true, pOutl.release()))
+ return;
+
+ maCursorLastPos = maCursorFirstPos = rPos;
+
+ OutlinerView* pOLV = mrView.GetTextEditOutlinerView();
+
+ // Move cursor to end of text
+ ESelection aNewSelection;
+
+ const WritingMode eMode = rTableObj.GetWritingMode();
+ if (((nAction == TblAction::GotoLeftCell) || (nAction == TblAction::GotoRightCell)) && (eMode != WritingMode_TB_RL))
+ {
+ const bool bLast = ((nAction == TblAction::GotoLeftCell) && (eMode == WritingMode_LR_TB)) ||
+ ((nAction == TblAction::GotoRightCell) && (eMode == WritingMode_RL_TB));
+
+ if( bLast )
+ aNewSelection = ESelection(EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND, EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND);
+ }
+ pOLV->SetSelection(aNewSelection);
+}
+
+
+void SvxTableController::StopTextEdit()
+{
+ if(mrView.IsTextEdit())
+ {
+ mrView.SdrEndTextEdit();
+ mrView.SetCurrentObj(SdrObjKind::Table);
+ mrView.SetEditMode(SdrViewEditMode::Edit);
+ }
+}
+
+
+void SvxTableController::getSelectedCells( CellPos& rFirst, CellPos& rLast )
+{
+ if( mbCellSelectionMode )
+ {
+ checkCell( maCursorFirstPos );
+ checkCell( maCursorLastPos );
+
+ rFirst.mnCol = std::min( maCursorFirstPos.mnCol, maCursorLastPos.mnCol );
+ rFirst.mnRow = std::min( maCursorFirstPos.mnRow, maCursorLastPos.mnRow );
+ rLast.mnCol = std::max( maCursorFirstPos.mnCol, maCursorLastPos.mnCol );
+ rLast.mnRow = std::max( maCursorFirstPos.mnRow, maCursorLastPos.mnRow );
+
+ if( !mxTable.is() )
+ return;
+
+ bool bExt = false;
+ do
+ {
+ bExt = false;
+ for( sal_Int32 nRow = rFirst.mnRow; nRow <= rLast.mnRow && !bExt; nRow++ )
+ {
+ for( sal_Int32 nCol = rFirst.mnCol; nCol <= rLast.mnCol && !bExt; nCol++ )
+ {
+ Reference< XMergeableCell > xCell( mxTable->getCellByPosition( nCol, nRow ), UNO_QUERY );
+ if( !xCell.is() )
+ continue;
+
+ if( xCell->isMerged() )
+ {
+ CellPos aPos( nCol, nRow );
+ findMergeOrigin( aPos );
+ if( (aPos.mnCol < rFirst.mnCol) || (aPos.mnRow < rFirst.mnRow) )
+ {
+ rFirst.mnCol = std::min( rFirst.mnCol, aPos.mnCol );
+ rFirst.mnRow = std::min( rFirst.mnRow, aPos.mnRow );
+ bExt = true;
+ }
+ }
+ else
+ {
+ if( ((nCol + xCell->getColumnSpan() - 1) > rLast.mnCol) || (nRow + xCell->getRowSpan() - 1 ) > rLast.mnRow )
+ {
+ rLast.mnCol = std::max( rLast.mnCol, nCol + xCell->getColumnSpan() - 1 );
+ rLast.mnRow = std::max( rLast.mnRow, nRow + xCell->getRowSpan() - 1 );
+ bExt = true;
+ }
+ }
+ }
+ }
+ }
+ while(bExt);
+ }
+ else if(mrView.IsTextEdit())
+ {
+ rFirst = getSelectionStart();
+ findMergeOrigin( rFirst );
+ rLast = rFirst;
+
+ if( mxTable.is() )
+ {
+ Reference< XMergeableCell > xCell( mxTable->getCellByPosition( rLast.mnCol, rLast.mnRow ), UNO_QUERY );
+ if( xCell.is() )
+ {
+ rLast.mnCol += xCell->getColumnSpan() - 1;
+ rLast.mnRow += xCell->getRowSpan() - 1;
+ }
+ }
+ }
+ else
+ {
+ rFirst.mnCol = 0;
+ rFirst.mnRow = 0;
+ if( mxTable.is() )
+ {
+ rLast.mnRow = mxTable->getRowCount()-1;
+ rLast.mnCol = mxTable->getColumnCount()-1;
+ }
+ else
+ {
+ rLast.mnRow = 0;
+ rLast.mnCol = 0;
+ }
+ }
+}
+
+
+void SvxTableController::StartSelection( const CellPos& rPos )
+{
+ StopTextEdit();
+ mbCellSelectionMode = true;
+ maCursorLastPos = maCursorFirstPos = rPos;
+ mrView.MarkListHasChanged();
+}
+
+
+void SvxTableController::setSelectedCells( const CellPos& rStart, const CellPos& rEnd )
+{
+ StopTextEdit();
+ mbCellSelectionMode = true;
+ maCursorFirstPos = rStart;
+ UpdateSelection( rEnd );
+}
+
+
+bool SvxTableController::ChangeFontSize(bool bGrow, const FontList* pFontList)
+{
+ if(!checkTableObject() || !mxTable.is())
+ return false;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+
+ if (mrView.IsTextEdit())
+ return true;
+
+ CellPos aStart, aEnd;
+
+ if(hasSelectedCells())
+ {
+ getSelectedCells(aStart, aEnd);
+ }
+ else
+ {
+ aStart.mnRow = 0;
+ aStart.mnCol = 0;
+ aEnd.mnRow = mxTable->getRowCount() - 1;
+ aEnd.mnCol = mxTable->getColumnCount() - 1;
+ }
+
+ for (sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++)
+ {
+ for (sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++)
+ {
+ CellRef xCell(dynamic_cast< Cell* >(mxTable->getCellByPosition(nCol, nRow).get()));
+ if (xCell.is())
+ {
+ if (rModel.IsUndoEnabled())
+ xCell->AddUndo();
+
+ SfxItemSet aCellSet(xCell->GetItemSet());
+ if (EditView::ChangeFontSize(bGrow, aCellSet, pFontList))
+ {
+ xCell->SetMergedItemSetAndBroadcast(aCellSet, false);
+ }
+ }
+ }
+ }
+
+ UpdateTableShape();
+
+ return true;
+}
+
+
+void SvxTableController::UpdateSelection( const CellPos& rPos )
+{
+ maCursorLastPos = rPos;
+ mrView.MarkListHasChanged();
+}
+
+
+void SvxTableController::clearSelection()
+{
+ RemoveSelection();
+}
+
+
+void SvxTableController::selectAll()
+{
+ if( mxTable.is() )
+ {
+ CellPos aPos2( mxTable->getColumnCount()-1, mxTable->getRowCount()-1 );
+ if( (aPos2.mnCol >= 0) && (aPos2.mnRow >= 0) )
+ {
+ CellPos aPos1;
+ setSelectedCells( aPos1, aPos2 );
+ }
+ }
+}
+
+
+void SvxTableController::RemoveSelection()
+{
+ if( mbCellSelectionMode )
+ {
+ mbCellSelectionMode = false;
+ mrView.MarkListHasChanged();
+ }
+}
+
+
+void SvxTableController::onTableModified()
+{
+ if( mnUpdateEvent == nullptr )
+ mnUpdateEvent = Application::PostUserEvent( LINK( this, SvxTableController, UpdateHdl ) );
+}
+
+
+void SvxTableController::updateSelectionOverlay()
+{
+ // There is no need to update selection overlay after merging cells
+ // since the selection overlay should remain the same
+ if ( mbHasJustMerged )
+ return;
+
+ destroySelectionOverlay();
+ if( !mbCellSelectionMode )
+ return;
+
+ sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
+ if( !pTableObj )
+ return;
+
+ sdr::overlay::OverlayObjectCell::RangeVector aRanges;
+
+ tools::Rectangle aStartRect, aEndRect;
+ CellPos aStart,aEnd;
+ getSelectedCells( aStart, aEnd );
+ pTableObj->getCellBounds( aStart, aStartRect );
+
+ basegfx::B2DRange a2DRange( basegfx::B2DPoint(aStartRect.Left(), aStartRect.Top()) );
+ a2DRange.expand( basegfx::B2DPoint(aStartRect.Right(), aStartRect.Bottom()) );
+
+ findMergeOrigin( aEnd );
+ pTableObj->getCellBounds( aEnd, aEndRect );
+ a2DRange.expand( basegfx::B2DPoint(aEndRect.Left(), aEndRect.Top()) );
+ a2DRange.expand( basegfx::B2DPoint(aEndRect.Right(), aEndRect.Bottom()) );
+ aRanges.push_back( a2DRange );
+
+ ::Color aHighlight( COL_BLUE );
+ OutputDevice* pOutDev = mrView.GetFirstOutputDevice();
+ if( pOutDev )
+ aHighlight = pOutDev->GetSettings().GetStyleSettings().GetHighlightColor();
+
+ const sal_uInt32 nCount = mrView.PaintWindowCount();
+ for( sal_uInt32 nIndex = 0; nIndex < nCount; nIndex++ )
+ {
+ SdrPaintWindow* pPaintWindow = mrView.GetPaintWindow(nIndex);
+ if( pPaintWindow )
+ {
+ const rtl::Reference < sdr::overlay::OverlayManager >& xOverlayManager = pPaintWindow->GetOverlayManager();
+ if( xOverlayManager.is() )
+ {
+ std::unique_ptr<sdr::overlay::OverlayObjectCell> pOverlay(new sdr::overlay::OverlayObjectCell( aHighlight, std::vector(aRanges) ));
+
+ xOverlayManager->add(*pOverlay);
+ mpSelectionOverlay.emplace();
+ mpSelectionOverlay->append(std::move(pOverlay));
+ }
+ }
+ }
+
+ // If tiled rendering, emit callbacks for sdr table selection.
+ if (!(pOutDev && comphelper::LibreOfficeKit::isActive()))
+ return;
+
+ tools::Rectangle aSelection(a2DRange.getMinX(), a2DRange.getMinY(), a2DRange.getMaxX(), a2DRange.getMaxY());
+
+ if (pOutDev->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
+ aSelection = o3tl::toTwips(aSelection, o3tl::Length::mm100);
+
+ if(SfxViewShell* pViewShell = SfxViewShell::Current())
+ {
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, aSelection.toString().getStr());
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aSelection.toString().getStr());
+ }
+}
+
+
+void SvxTableController::destroySelectionOverlay()
+{
+ if( !mpSelectionOverlay )
+ return;
+
+ mpSelectionOverlay.reset();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // Clear the LOK text selection so far provided by this table.
+ if(SfxViewShell* pViewShell = SfxViewShell::Current())
+ {
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, "EMPTY");
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_START, "EMPTY");
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_END, "EMPTY");
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, "EMPTY");
+ }
+ }
+}
+
+
+void SvxTableController::MergeAttrFromSelectedCells(SfxItemSet& rAttr, bool bOnlyHardAttr) const
+{
+ if( !mxTable.is() )
+ return;
+
+ CellPos aStart, aEnd;
+ const_cast<SvxTableController&>(*this).getSelectedCells( aStart, aEnd );
+
+ for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
+ {
+ for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() && !xCell->isMerged() )
+ {
+ const SfxItemSet& rSet = xCell->GetItemSet();
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich(aIter.FirstWhich());
+ while(nWhich)
+ {
+ SfxItemState nState = aIter.GetItemState(false);
+ if(!bOnlyHardAttr)
+ {
+ if(SfxItemState::DONTCARE == nState)
+ rAttr.InvalidateItem(nWhich);
+ else
+ rAttr.MergeValue(rSet.Get(nWhich), true);
+ }
+ else if(SfxItemState::SET == nState)
+ {
+ const SfxPoolItem& rItem = rSet.Get(nWhich);
+ rAttr.MergeValue(rItem, true);
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+ }
+ }
+ }
+}
+
+
+static void ImplSetLinePreserveColor( SvxBoxItem& rNewFrame, const SvxBorderLine* pNew, SvxBoxItemLine nLine )
+{
+ if( pNew )
+ {
+ const SvxBorderLine* pOld = rNewFrame.GetLine(nLine);
+ if( pOld )
+ {
+ SvxBorderLine aNewLine( *pNew );
+ aNewLine.SetColor( pOld->GetColor() );
+ rNewFrame.SetLine( &aNewLine, nLine );
+ return;
+ }
+ }
+ rNewFrame.SetLine( pNew, nLine );
+}
+
+
+static void ImplApplyBoxItem( CellPosFlag nCellPosFlags, const SvxBoxItem* pBoxItem, const SvxBoxInfoItem* pBoxInfoItem, SvxBoxItem& rNewFrame )
+{
+ if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
+ {
+ // current cell is outside the selection
+
+ if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
+ {
+ if (nCellPosFlags & CellPosFlag::Upper)
+ {
+ if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) )
+ rNewFrame.SetLine(nullptr, SvxBoxItemLine::BOTTOM );
+ }
+ else if (nCellPosFlags & CellPosFlag::Lower)
+ {
+ if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) )
+ rNewFrame.SetLine( nullptr, SvxBoxItemLine::TOP );
+ }
+ }
+ else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
+ {
+ if (nCellPosFlags & CellPosFlag::Before)
+ {
+ if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) )
+ rNewFrame.SetLine( nullptr, SvxBoxItemLine::RIGHT );
+ }
+ else if (nCellPosFlags & CellPosFlag::After)
+ {
+ if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) )
+ rNewFrame.SetLine( nullptr, SvxBoxItemLine::LEFT );
+ }
+ }
+ }
+ else
+ {
+ // current cell is inside the selection
+
+ if ((nCellPosFlags & CellPosFlag::Left) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT)
+ : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT))
+ rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Left) ? pBoxItem->GetLeft() : pBoxInfoItem->GetVert(), SvxBoxItemLine::LEFT );
+
+ if( (nCellPosFlags & CellPosFlag::Right) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
+ rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Right) ? pBoxItem->GetRight() : pBoxInfoItem->GetVert(), SvxBoxItemLine::RIGHT );
+
+ if( (nCellPosFlags & CellPosFlag::Top) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
+ rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Top) ? pBoxItem->GetTop() : pBoxInfoItem->GetHori(), SvxBoxItemLine::TOP );
+
+ if( (nCellPosFlags & CellPosFlag::Bottom) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
+ rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Bottom) ? pBoxItem->GetBottom() : pBoxInfoItem->GetHori(), SvxBoxItemLine::BOTTOM );
+
+ // apply distance to borders
+ if( pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::DISTANCE ) )
+ for( SvxBoxItemLine nLine : o3tl::enumrange<SvxBoxItemLine>() )
+ rNewFrame.SetDistance( pBoxItem->GetDistance( nLine ), nLine );
+ }
+}
+
+
+static void ImplSetLineColor( SvxBoxItem& rNewFrame, SvxBoxItemLine nLine, const Color& rColor )
+{
+ const SvxBorderLine* pSourceLine = rNewFrame.GetLine( nLine );
+ if( pSourceLine )
+ {
+ SvxBorderLine aLine( *pSourceLine );
+ aLine.SetColor( rColor );
+ rNewFrame.SetLine( &aLine, nLine );
+ }
+}
+
+
+static void ImplApplyLineColorItem( CellPosFlag nCellPosFlags, const SvxColorItem* pLineColorItem, SvxBoxItem& rNewFrame )
+{
+ const Color aColor( pLineColorItem->GetValue() );
+
+ if (!(nCellPosFlags & (CellPosFlag::Lower|CellPosFlag::Before|CellPosFlag::After)))
+ ImplSetLineColor( rNewFrame, SvxBoxItemLine::BOTTOM, aColor );
+
+ if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Before|CellPosFlag::After)))
+ ImplSetLineColor( rNewFrame, SvxBoxItemLine::TOP, aColor );
+
+ if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower|CellPosFlag::After)))
+ ImplSetLineColor( rNewFrame, SvxBoxItemLine::RIGHT, aColor );
+
+ if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower|CellPosFlag::Before)))
+ ImplSetLineColor( rNewFrame, SvxBoxItemLine::LEFT, aColor );
+}
+
+
+static void ImplApplyBorderLineItem( CellPosFlag nCellPosFlags, const SvxBorderLine* pBorderLineItem, SvxBoxItem& rNewFrame )
+{
+ if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
+ {
+ if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
+ {
+ if (nCellPosFlags & CellPosFlag::Upper)
+ {
+ if( rNewFrame.GetBottom() )
+ ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::BOTTOM );
+ }
+ else if (nCellPosFlags & CellPosFlag::Lower)
+ {
+ if( rNewFrame.GetTop() )
+ ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::TOP );
+ }
+ }
+ else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
+ {
+ if (nCellPosFlags & CellPosFlag::Before)
+ {
+ if( rNewFrame.GetRight() )
+ ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::RIGHT );
+ }
+ else if (nCellPosFlags & CellPosFlag::After)
+ {
+ if( rNewFrame.GetLeft() )
+ ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::LEFT );
+ }
+ }
+ }
+ else
+ {
+ if( rNewFrame.GetBottom() )
+ ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::BOTTOM );
+ if( rNewFrame.GetTop() )
+ ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::TOP );
+ if( rNewFrame.GetRight() )
+ ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::RIGHT );
+ if( rNewFrame.GetLeft() )
+ ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::LEFT );
+ }
+}
+
+
+void SvxTableController::ApplyBorderAttr( const SfxItemSet& rAttr )
+{
+ if( !mxTable.is() )
+ return;
+
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+ if( !(nRowCount && nColCount) )
+ return;
+
+ const SvxBoxItem* pBoxItem = nullptr;
+ if(SfxItemState::SET == rAttr.GetItemState(SDRATTR_TABLE_BORDER, false) )
+ pBoxItem = &rAttr.Get( SDRATTR_TABLE_BORDER );
+
+ const SvxBoxInfoItem* pBoxInfoItem = nullptr;
+ if(SfxItemState::SET == rAttr.GetItemState(SDRATTR_TABLE_BORDER_INNER, false) )
+ pBoxInfoItem = &rAttr.Get( SDRATTR_TABLE_BORDER_INNER );
+
+ const SvxColorItem* pLineColorItem = nullptr;
+ if(SfxItemState::SET == rAttr.GetItemState(SID_FRAME_LINECOLOR, false) )
+ pLineColorItem = &rAttr.Get( SID_FRAME_LINECOLOR );
+
+ const SvxBorderLine* pBorderLineItem = nullptr;
+ if(SfxItemState::SET == rAttr.GetItemState(SID_FRAME_LINESTYLE, false) )
+ pBorderLineItem = rAttr.Get( SID_FRAME_LINESTYLE ).GetLine();
+
+ if( pBoxInfoItem && !pBoxItem )
+ {
+ const static SvxBoxItem gaEmptyBoxItem( SDRATTR_TABLE_BORDER );
+ pBoxItem = &gaEmptyBoxItem;
+ }
+ else if( pBoxItem && !pBoxInfoItem )
+ {
+ const static SvxBoxInfoItem gaEmptyBoxInfoItem( SDRATTR_TABLE_BORDER_INNER );
+ pBoxInfoItem = &gaEmptyBoxInfoItem;
+ }
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+
+ const sal_Int32 nLastRow = std::min( aEnd.mnRow + 2, nRowCount );
+ const sal_Int32 nLastCol = std::min( aEnd.mnCol + 2, nColCount );
+
+ for( sal_Int32 nRow = std::max( aStart.mnRow - 1, sal_Int32(0) ); nRow < nLastRow; nRow++ )
+ {
+ CellPosFlag nRowFlags = CellPosFlag::NONE;
+ nRowFlags |= (nRow == aStart.mnRow) ? CellPosFlag::Top : CellPosFlag::NONE;
+ nRowFlags |= (nRow == aEnd.mnRow) ? CellPosFlag::Bottom : CellPosFlag::NONE;
+ nRowFlags |= (nRow < aStart.mnRow) ? CellPosFlag::Upper : CellPosFlag::NONE;
+ nRowFlags |= (nRow > aEnd.mnRow) ? CellPosFlag::Lower : CellPosFlag::NONE;
+
+ for( sal_Int32 nCol = std::max( aStart.mnCol - 1, sal_Int32(0) ); nCol < nLastCol; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( !xCell.is() )
+ continue;
+
+ const SfxItemSet& rSet = xCell->GetItemSet();
+ const SvxBoxItem* pOldOuter = &rSet.Get( SDRATTR_TABLE_BORDER );
+
+ SvxBoxItem aNewFrame( *pOldOuter );
+
+ CellPosFlag nCellPosFlags = nRowFlags;
+ nCellPosFlags |= (nCol == aStart.mnCol) ? CellPosFlag::Left : CellPosFlag::NONE;
+ nCellPosFlags |= (nCol == aEnd.mnCol) ? CellPosFlag::Right : CellPosFlag::NONE;
+ nCellPosFlags |= (nCol < aStart.mnCol) ? CellPosFlag::Before : CellPosFlag::NONE;
+ nCellPosFlags |= (nCol > aEnd.mnCol) ? CellPosFlag::After : CellPosFlag::NONE;
+
+ if( pBoxItem && pBoxInfoItem )
+ ImplApplyBoxItem( nCellPosFlags, pBoxItem, pBoxInfoItem, aNewFrame );
+
+ if( pLineColorItem )
+ ImplApplyLineColorItem( nCellPosFlags, pLineColorItem, aNewFrame );
+
+ if( pBorderLineItem )
+ ImplApplyBorderLineItem( nCellPosFlags, pBorderLineItem, aNewFrame );
+
+ if (aNewFrame != *pOldOuter)
+ {
+ SfxItemSet aAttr(*rSet.GetPool(), rSet.GetRanges());
+ aAttr.Put(aNewFrame);
+ xCell->SetMergedItemSetAndBroadcast( aAttr, false );
+ }
+ }
+ }
+}
+
+
+void SvxTableController::UpdateTableShape()
+{
+ SdrObject* pTableObj = mxTableObj.get();
+ if( pTableObj )
+ {
+ pTableObj->ActionChanged();
+ pTableObj->BroadcastObjectChange();
+ }
+ updateSelectionOverlay();
+}
+
+
+void SvxTableController::SetAttrToSelectedCells(const SfxItemSet& rAttr, bool bReplaceAll)
+{
+ if(!checkTableObject() || !mxTable.is())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ if( bUndo )
+ rModel.BegUndo( SvxResId(STR_TABLE_NUMFORMAT) );
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+
+ SfxItemSet aAttr(*rAttr.GetPool(), rAttr.GetRanges());
+ aAttr.Put(rAttr);
+
+ const bool bFrame = (rAttr.GetItemState( SDRATTR_TABLE_BORDER ) == SfxItemState::SET) || (rAttr.GetItemState( SDRATTR_TABLE_BORDER_INNER ) == SfxItemState::SET);
+
+ if( bFrame )
+ {
+ aAttr.ClearItem( SDRATTR_TABLE_BORDER );
+ aAttr.ClearItem( SDRATTR_TABLE_BORDER_INNER );
+ }
+
+ for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
+ {
+ for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() )
+ {
+ if( bUndo )
+ xCell->AddUndo();
+ xCell->SetMergedItemSetAndBroadcast(aAttr, bReplaceAll);
+ }
+ }
+ }
+
+ if( bFrame )
+ {
+ ApplyBorderAttr( rAttr );
+ }
+
+ UpdateTableShape();
+
+ if( bUndo )
+ rModel.EndUndo();
+}
+
+void SvxTableController::SetAttrToSelectedShape(const SfxItemSet& rAttr)
+{
+ if (!checkTableObject() || !mxTable.is())
+ return;
+
+ // Filter out non-shadow items from rAttr.
+ SfxItemSetFixed<SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST> aSet(*rAttr.GetPool());
+ aSet.Put(rAttr);
+
+ if (!aSet.Count())
+ {
+ // If there are no items to be applied on the shape, then don't set anything, it would
+ // terminate text edit without a good reason, which affects undo/redo.
+ return;
+ }
+
+ // Set shadow items on the marked shape.
+ mrView.SetAttrToMarked(aSet, /*bReplaceAll=*/false);
+}
+
+bool SvxTableController::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
+{
+ if( mxTableObj.is() && hasSelectedCells() )
+ {
+ MergeAttrFromSelectedCells( rTargetSet, bOnlyHardAttr );
+
+ if( mrView.IsTextEdit() )
+ {
+ OutlinerView* pTextEditOutlinerView = mrView.GetTextEditOutlinerView();
+ if(pTextEditOutlinerView)
+ {
+ // FALSE= consider InvalidItems not as the default, but as "holes"
+ rTargetSet.Put(pTextEditOutlinerView->GetAttribs(), false);
+ }
+ }
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+bool SvxTableController::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
+{
+ if( mbCellSelectionMode || mrView.IsTextEdit() )
+ {
+ SetAttrToSelectedCells( rSet, bReplaceAll );
+ return true;
+ }
+ return false;
+}
+
+SdrObject* SvxTableController::GetMarkedSdrObjClone(SdrModel& rTargetModel)
+{
+ SdrTableObj* pRetval(nullptr);
+ sdr::table::SdrTableObj* pCurrentSdrTableObj(GetTableObj());
+
+ if(nullptr == pCurrentSdrTableObj)
+ {
+ return pRetval;
+ }
+
+ if(!mxTableObj.is())
+ {
+ return pRetval;
+ }
+
+ // get selection and create full selection
+ CellPos aStart, aEnd;
+ const CellPos aFullStart, aFullEnd(mxTable->getColumnCount()-1, mxTable->getRowCount()-1);
+
+ getSelectedCells(aStart, aEnd);
+
+ // compare to see if we have a partial selection
+ if(aStart != aFullStart || aEnd != aFullEnd)
+ {
+ // create full clone
+ pRetval = pCurrentSdrTableObj->CloneSdrObject(rTargetModel);
+
+ // limit SdrObject's TableModel to partial selection
+ pRetval->CropTableModelToSelection(aStart, aEnd);
+ }
+
+ return pRetval;
+}
+
+bool SvxTableController::PasteObjModel( const SdrModel& rModel )
+{
+ if( mxTableObj.is() && (rModel.GetPageCount() >= 1) )
+ {
+ const SdrPage* pPastePage = rModel.GetPage(0);
+ if( pPastePage && pPastePage->GetObjCount() == 1 )
+ {
+ SdrTableObj* pPasteTableObj = dynamic_cast< SdrTableObj* >( pPastePage->GetObj(0) );
+ if( pPasteTableObj )
+ {
+ return PasteObject( pPasteTableObj );
+ }
+ }
+ }
+
+ return false;
+}
+
+
+bool SvxTableController::PasteObject( SdrTableObj const * pPasteTableObj )
+{
+ if( !pPasteTableObj )
+ return false;
+
+ Reference< XTable > xPasteTable( pPasteTableObj->getTable() );
+ if( !xPasteTable.is() )
+ return false;
+
+ if( !mxTable.is() )
+ return false;
+
+ sal_Int32 nPasteColumns = xPasteTable->getColumnCount();
+ sal_Int32 nPasteRows = xPasteTable->getRowCount();
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+
+ if( mrView.IsTextEdit() )
+ mrView.SdrEndTextEdit(true);
+
+ sal_Int32 nColumns = mxTable->getColumnCount();
+ sal_Int32 nRows = mxTable->getRowCount();
+
+ const sal_Int32 nMissing = nPasteRows - ( nRows - aStart.mnRow );
+ if( nMissing > 0 )
+ {
+ Reference< XTableRows > xRows( mxTable->getRows() );
+ xRows->insertByIndex( nRows, nMissing );
+ nRows = mxTable->getRowCount();
+ }
+
+ nPasteRows = std::min( nPasteRows, nRows - aStart.mnRow );
+ nPasteColumns = std::min( nPasteColumns, nColumns - aStart.mnCol );
+
+ // copy cell contents
+ for( sal_Int32 nRow = 0; nRow < nPasteRows; ++nRow )
+ {
+ for( sal_Int32 nCol = 0, nTargetCol = aStart.mnCol; nCol < nPasteColumns; ++nCol )
+ {
+ CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nTargetCol, aStart.mnRow + nRow ).get() ) );
+ if( xTargetCell.is() && !xTargetCell->isMerged() )
+ {
+ CellRef xSourceCell(dynamic_cast<Cell*>(xPasteTable->getCellByPosition(nCol, nRow).get()));
+ if (xSourceCell.is())
+ {
+ xTargetCell->AddUndo();
+ // Prevent cell span exceeding the pasting range.
+ if (nColumns < nTargetCol + xSourceCell->getColumnSpan())
+ xTargetCell->replaceContentAndFormatting(xSourceCell);
+ else
+ xTargetCell->cloneFrom(xSourceCell);
+
+ nCol += xSourceCell->getColumnSpan() - 1;
+ nTargetCol += xTargetCell->getColumnSpan();
+ }
+ }
+ }
+ }
+
+ UpdateTableShape();
+
+ return true;
+}
+
+bool SvxTableController::ApplyFormatPaintBrush( SfxItemSet& rFormatSet, bool bNoCharacterFormats, bool bNoParagraphFormats )
+{
+ if(!mbCellSelectionMode)
+ {
+ return false;
+ }
+
+ if(!checkTableObject())
+ return false;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ if( bUndo )
+ rModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+ const bool bFrame = (rFormatSet.GetItemState( SDRATTR_TABLE_BORDER ) == SfxItemState::SET) || (rFormatSet.GetItemState( SDRATTR_TABLE_BORDER_INNER ) == SfxItemState::SET);
+
+ for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
+ {
+ for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() )
+ {
+ if (bUndo)
+ xCell->AddUndo();
+ SdrText* pText = xCell.get();
+ SdrObjEditView::ApplyFormatPaintBrushToText( rFormatSet, rTableObj, pText, bNoCharacterFormats, bNoParagraphFormats );
+ }
+ }
+ }
+
+ if( bFrame )
+ {
+ ApplyBorderAttr( rFormatSet );
+ }
+
+ UpdateTableShape();
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ return true;
+}
+
+
+IMPL_LINK_NOARG(SvxTableController, UpdateHdl, void*, void)
+{
+ mnUpdateEvent = nullptr;
+
+ if( mbCellSelectionMode )
+ {
+ CellPos aStart( maCursorFirstPos );
+ CellPos aEnd( maCursorLastPos );
+ checkCell(aStart);
+ checkCell(aEnd);
+ if( aStart != maCursorFirstPos || aEnd != maCursorLastPos )
+ {
+ setSelectedCells( aStart, aEnd );
+ }
+ }
+
+ updateSelectionOverlay();
+ mbHasJustMerged = false;
+}
+
+namespace
+{
+
+struct LinesState
+{
+ LinesState(SvxBoxItem& rBoxItem_, SvxBoxInfoItem& rBoxInfoItem_)
+ : rBoxItem(rBoxItem_)
+ , rBoxInfoItem(rBoxInfoItem_)
+ , bDistanceIndeterminate(false)
+ {
+ aBorderSet.fill(false);
+ aInnerLineSet.fill(false);
+ aBorderIndeterminate.fill(false);
+ aInnerLineIndeterminate.fill(false);
+ aDistanceSet.fill(false);
+ aDistance.fill(0);
+ }
+
+ SvxBoxItem& rBoxItem;
+ SvxBoxInfoItem& rBoxInfoItem;
+ o3tl::enumarray<SvxBoxItemLine, bool> aBorderSet;
+ o3tl::enumarray<SvxBoxInfoItemLine, bool> aInnerLineSet;
+ o3tl::enumarray<SvxBoxItemLine, bool> aBorderIndeterminate;
+ o3tl::enumarray<SvxBoxInfoItemLine, bool> aInnerLineIndeterminate;
+ o3tl::enumarray<SvxBoxItemLine, bool> aDistanceSet;
+ o3tl::enumarray<SvxBoxItemLine, sal_uInt16> aDistance;
+ bool bDistanceIndeterminate;
+};
+
+class BoxItemWrapper
+{
+public:
+ BoxItemWrapper(SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem, SvxBoxItemLine nBorderLine, SvxBoxInfoItemLine nInnerLine, bool bBorder);
+
+ const SvxBorderLine* getLine() const;
+ void setLine(const SvxBorderLine* pLine);
+
+private:
+ SvxBoxItem& m_rBoxItem;
+ SvxBoxInfoItem& m_rBoxInfoItem;
+ const SvxBoxItemLine m_nBorderLine;
+ const SvxBoxInfoItemLine m_nInnerLine;
+ const bool m_bBorder;
+};
+
+BoxItemWrapper::BoxItemWrapper(
+ SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem,
+ const SvxBoxItemLine nBorderLine, const SvxBoxInfoItemLine nInnerLine, const bool bBorder)
+ : m_rBoxItem(rBoxItem)
+ , m_rBoxInfoItem(rBoxInfoItem)
+ , m_nBorderLine(nBorderLine)
+ , m_nInnerLine(nInnerLine)
+ , m_bBorder(bBorder)
+{
+}
+
+const SvxBorderLine* BoxItemWrapper::getLine() const
+{
+ if (m_bBorder)
+ return m_rBoxItem.GetLine(m_nBorderLine);
+ else
+ return (m_nInnerLine == SvxBoxInfoItemLine::HORI) ? m_rBoxInfoItem.GetHori() : m_rBoxInfoItem.GetVert();
+}
+
+void BoxItemWrapper::setLine(const SvxBorderLine* pLine)
+{
+ if (m_bBorder)
+ m_rBoxItem.SetLine(pLine, m_nBorderLine);
+ else
+ m_rBoxInfoItem.SetLine(pLine, m_nInnerLine);
+}
+
+void lcl_MergeBorderLine(
+ LinesState& rLinesState, const SvxBorderLine* const pLine, const SvxBoxItemLine nLine,
+ SvxBoxInfoItemValidFlags nValidFlag, const bool bBorder = true)
+{
+ const SvxBoxInfoItemLine nInnerLine(bBorder ? SvxBoxInfoItemLine::HORI : ((nValidFlag & SvxBoxInfoItemValidFlags::HORI) ? SvxBoxInfoItemLine::HORI : SvxBoxInfoItemLine::VERT));
+ BoxItemWrapper aBoxItem(rLinesState.rBoxItem, rLinesState.rBoxInfoItem, nLine, nInnerLine, bBorder);
+ bool& rbSet(bBorder ? rLinesState.aBorderSet[nLine] : rLinesState.aInnerLineSet[nInnerLine]);
+
+ if (rbSet)
+ {
+ bool& rbIndeterminate(bBorder ? rLinesState.aBorderIndeterminate[nLine] : rLinesState.aInnerLineIndeterminate[nInnerLine]);
+ if (!rbIndeterminate)
+ {
+ const SvxBorderLine* const pMergedLine(aBoxItem.getLine());
+ if ((pLine && !pMergedLine) || (!pLine && pMergedLine) || (pLine && (*pLine != *pMergedLine)))
+ {
+ aBoxItem.setLine(nullptr);
+ rbIndeterminate = true;
+ }
+ }
+ }
+ else
+ {
+ aBoxItem.setLine(pLine);
+ rbSet = true;
+ }
+}
+
+void lcl_MergeBorderOrInnerLine(
+ LinesState& rLinesState, const SvxBorderLine* const pLine, const SvxBoxItemLine nLine,
+ SvxBoxInfoItemValidFlags nValidFlag, const bool bBorder)
+{
+ if (bBorder)
+ lcl_MergeBorderLine(rLinesState, pLine, nLine, nValidFlag);
+ else
+ {
+ const bool bVertical = (nLine == SvxBoxItemLine::LEFT) || (nLine == SvxBoxItemLine::RIGHT);
+ lcl_MergeBorderLine(rLinesState, pLine, nLine, bVertical ? SvxBoxInfoItemValidFlags::VERT : SvxBoxInfoItemValidFlags::HORI, false);
+ }
+}
+
+void lcl_MergeDistance(
+ LinesState& rLinesState, const SvxBoxItemLine nIndex, const sal_uInt16 nDistance)
+{
+ if (rLinesState.aDistanceSet[nIndex])
+ {
+ if (!rLinesState.bDistanceIndeterminate)
+ rLinesState.bDistanceIndeterminate = nDistance != rLinesState.aDistance[nIndex];
+ }
+ else
+ {
+ rLinesState.aDistance[nIndex] = nDistance;
+ rLinesState.aDistanceSet[nIndex] = true;
+ }
+}
+
+void lcl_MergeCommonBorderAttr(LinesState& rLinesState, const SvxBoxItem& rCellBoxItem, const CellPosFlag nCellPosFlags)
+{
+ if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
+ {
+ // current cell is outside the selection
+
+ if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
+ {
+ if (nCellPosFlags & CellPosFlag::Upper)
+ lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetBottom(), SvxBoxItemLine::TOP, SvxBoxInfoItemValidFlags::TOP);
+ else if (nCellPosFlags & CellPosFlag::Lower)
+ lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetTop(), SvxBoxItemLine::BOTTOM, SvxBoxInfoItemValidFlags::BOTTOM);
+ }
+ else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
+ {
+ if (nCellPosFlags & CellPosFlag::Before)
+ lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetRight(), SvxBoxItemLine::LEFT, SvxBoxInfoItemValidFlags::LEFT);
+ else if (nCellPosFlags & CellPosFlag::After)
+ lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetLeft(), SvxBoxItemLine::RIGHT, SvxBoxInfoItemValidFlags::RIGHT);
+ }
+
+ // NOTE: inner distances for cells outside the selected range
+ // are not relevant -> we ignore them.
+ }
+ else
+ {
+ // current cell is inside the selection
+
+ lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetTop(), SvxBoxItemLine::TOP, SvxBoxInfoItemValidFlags::TOP, static_cast<bool>(nCellPosFlags & CellPosFlag::Top));
+ lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetBottom(), SvxBoxItemLine::BOTTOM, SvxBoxInfoItemValidFlags::BOTTOM, static_cast<bool>(nCellPosFlags & CellPosFlag::Bottom));
+ lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetLeft(), SvxBoxItemLine::LEFT, SvxBoxInfoItemValidFlags::LEFT, static_cast<bool>(nCellPosFlags & CellPosFlag::Left));
+ lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetRight(), SvxBoxItemLine::RIGHT, SvxBoxInfoItemValidFlags::RIGHT, static_cast<bool>(nCellPosFlags & CellPosFlag::Right));
+
+ lcl_MergeDistance(rLinesState, SvxBoxItemLine::TOP, rCellBoxItem.GetDistance(SvxBoxItemLine::TOP));
+ lcl_MergeDistance(rLinesState, SvxBoxItemLine::BOTTOM, rCellBoxItem.GetDistance(SvxBoxItemLine::BOTTOM));
+ lcl_MergeDistance(rLinesState, SvxBoxItemLine::LEFT, rCellBoxItem.GetDistance(SvxBoxItemLine::LEFT));
+ lcl_MergeDistance(rLinesState, SvxBoxItemLine::RIGHT, rCellBoxItem.GetDistance(SvxBoxItemLine::RIGHT));
+ }
+}
+
+}
+
+void SvxTableController::FillCommonBorderAttrFromSelectedCells( SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem ) const
+{
+ if( !mxTable.is() )
+ return;
+
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+ if( !(nRowCount && nColCount) )
+ return;
+
+ CellPos aStart, aEnd;
+ const_cast< SvxTableController* >( this )->getSelectedCells( aStart, aEnd );
+
+ // We are adding one more row/column around the block of selected cells.
+ // We will be checking the adjoining border of these too.
+ const sal_Int32 nLastRow = std::min( aEnd.mnRow + 2, nRowCount );
+ const sal_Int32 nLastCol = std::min( aEnd.mnCol + 2, nColCount );
+
+ rBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::ALL, false );
+ LinesState aLinesState( rBoxItem, rBoxInfoItem );
+
+ /* Here we go through all the selected cells (enhanced by
+ * the adjoining row/column on each side) and determine the
+ * lines for presentation. The algorithm is simple:
+ * 1. if a border or inner line is set (or unset) in all
+ * cells to the same value, it will be used.
+ * 2. if a border or inner line is set only in some cells,
+ * it will be set to indeterminate state (SetValid() on
+ * rBoxInfoItem).
+ */
+ for( sal_Int32 nRow = std::max( aStart.mnRow - 1, sal_Int32(0) ); nRow < nLastRow; nRow++ )
+ {
+ CellPosFlag nRowFlags = CellPosFlag::NONE;
+ nRowFlags |= (nRow == aStart.mnRow) ? CellPosFlag::Top : CellPosFlag::NONE;
+ nRowFlags |= (nRow == aEnd.mnRow) ? CellPosFlag::Bottom : CellPosFlag::NONE;
+ nRowFlags |= (nRow < aStart.mnRow) ? CellPosFlag::Upper : CellPosFlag::NONE;
+ nRowFlags |= (nRow > aEnd.mnRow) ? CellPosFlag::Lower : CellPosFlag::NONE;
+
+ for( sal_Int32 nCol = std::max( aStart.mnCol - 1, sal_Int32(0) ); nCol < nLastCol; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( !xCell.is() )
+ continue;
+
+ CellPosFlag nCellPosFlags = nRowFlags;
+ nCellPosFlags |= (nCol == aStart.mnCol) ? CellPosFlag::Left : CellPosFlag::NONE;
+ nCellPosFlags |= (nCol == aEnd.mnCol) ? CellPosFlag::Right : CellPosFlag::NONE;
+ nCellPosFlags |= (nCol < aStart.mnCol) ? CellPosFlag::Before : CellPosFlag::NONE;
+ nCellPosFlags |= (nCol > aEnd.mnCol) ? CellPosFlag::After : CellPosFlag::NONE;
+
+ const SfxItemSet& rSet = xCell->GetItemSet();
+ SvxBoxItem aCellBoxItem(mergeDrawinglayerTextDistancesAndSvxBoxItem(rSet));
+ lcl_MergeCommonBorderAttr( aLinesState, aCellBoxItem, nCellPosFlags );
+ }
+ }
+
+ if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::TOP])
+ aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::TOP);
+ if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::BOTTOM])
+ aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::BOTTOM);
+ if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::LEFT])
+ aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::LEFT);
+ if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::RIGHT])
+ aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::RIGHT);
+ if (!aLinesState.aInnerLineIndeterminate[SvxBoxInfoItemLine::HORI])
+ aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::HORI);
+ if (!aLinesState.aInnerLineIndeterminate[SvxBoxInfoItemLine::VERT])
+ aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::VERT);
+
+ if (!aLinesState.bDistanceIndeterminate)
+ {
+ if (aLinesState.aDistanceSet[SvxBoxItemLine::TOP])
+ aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::TOP], SvxBoxItemLine::TOP);
+ if (aLinesState.aDistanceSet[SvxBoxItemLine::BOTTOM])
+ aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::BOTTOM], SvxBoxItemLine::BOTTOM);
+ if (aLinesState.aDistanceSet[SvxBoxItemLine::LEFT])
+ aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::LEFT], SvxBoxItemLine::LEFT);
+ if (aLinesState.aDistanceSet[SvxBoxItemLine::RIGHT])
+ aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::RIGHT], SvxBoxItemLine::RIGHT);
+ aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::DISTANCE);
+ }
+}
+
+bool SvxTableController::selectRow( sal_Int32 row )
+{
+ if( !mxTable.is() )
+ return false;
+ CellPos aStart( 0, row ), aEnd( mxTable->getColumnCount() - 1, row );
+ StartSelection( aEnd );
+ gotoCell( aStart, true, nullptr );
+ return true;
+}
+
+bool SvxTableController::selectColumn( sal_Int32 column )
+{
+ if( !mxTable.is() )
+ return false;
+ CellPos aStart( column, 0 ), aEnd( column, mxTable->getRowCount() - 1 );
+ StartSelection( aEnd );
+ gotoCell( aStart, true, nullptr );
+ return true;
+}
+
+bool SvxTableController::deselectRow( sal_Int32 row )
+{
+ if( !mxTable.is() )
+ return false;
+ CellPos aStart( 0, row ), aEnd( mxTable->getColumnCount() - 1, row );
+ StartSelection( aEnd );
+ gotoCell( aStart, false, nullptr );
+ return true;
+}
+
+bool SvxTableController::deselectColumn( sal_Int32 column )
+{
+ if( !mxTable.is() )
+ return false;
+ CellPos aStart( column, 0 ), aEnd( column, mxTable->getRowCount() - 1 );
+ StartSelection( aEnd );
+ gotoCell( aStart, false, nullptr );
+ return true;
+}
+
+bool SvxTableController::isRowSelected( sal_Int32 nRow )
+{
+ if( hasSelectedCells() )
+ {
+ CellPos aFirstPos, aLastPos;
+ getSelectedCells( aFirstPos, aLastPos );
+ if( (aFirstPos.mnCol == 0) && (nRow >= aFirstPos.mnRow && nRow <= aLastPos.mnRow) && (mxTable->getColumnCount() - 1 == aLastPos.mnCol) )
+ return true;
+ }
+ return false;
+}
+
+bool SvxTableController::isColumnSelected( sal_Int32 nColumn )
+{
+ if( hasSelectedCells() )
+ {
+ CellPos aFirstPos, aLastPos;
+ getSelectedCells( aFirstPos, aLastPos );
+ if( (aFirstPos.mnRow == 0) && (nColumn >= aFirstPos.mnCol && nColumn <= aLastPos.mnCol) && (mxTable->getRowCount() - 1 == aLastPos.mnRow) )
+ return true;
+ }
+ return false;
+}
+
+bool SvxTableController::isRowHeader()
+{
+ if(!checkTableObject())
+ return false;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ TableStyleSettings aSettings(rTableObj.getTableStyleSettings());
+
+ return aSettings.mbUseFirstRow;
+}
+
+bool SvxTableController::isColumnHeader()
+{
+ if(!checkTableObject())
+ return false;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ TableStyleSettings aSettings(rTableObj.getTableStyleSettings());
+
+ return aSettings.mbUseFirstColumn;
+}
+
+bool SvxTableController::setCursorLogicPosition(const Point& rPosition, bool bPoint)
+{
+ if (mxTableObj->GetObjIdentifier() != SdrObjKind::Table)
+ return false;
+
+ SdrTableObj* pTableObj = mxTableObj.get();
+ CellPos aCellPos;
+ if (pTableObj->CheckTableHit(rPosition, aCellPos.mnCol, aCellPos.mnRow) != TableHitKind::NONE)
+ {
+ // Position is a table cell.
+ if (mbCellSelectionMode)
+ {
+ // We have a table selection already: adjust the point or the mark.
+ if (bPoint)
+ setSelectedCells(maCursorFirstPos, aCellPos);
+ else
+ setSelectedCells(aCellPos, maCursorLastPos);
+ return true;
+ }
+ else if (aCellPos != maMouseDownPos)
+ {
+ // No selection, but rPosition is at another cell: start table selection.
+ StartSelection(maMouseDownPos);
+ // Update graphic selection, should be hidden now.
+ mrView.AdjustMarkHdl();
+ }
+ }
+
+ return false;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tabledesign.cxx b/svx/source/table/tabledesign.cxx
new file mode 100644
index 000000000..03b16250f
--- /dev/null
+++ b/svx/source/table/tabledesign.cxx
@@ -0,0 +1,705 @@
+/* -*- 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 <com/sun/star/style/XStyle.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <com/sun/star/util/XModifyListener.hpp>
+
+#include <vcl/svapp.hxx>
+
+#include <comphelper/compbase.hxx>
+#include <comphelper/interfacecontainer4.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/sequence.hxx>
+
+#include <svx/sdr/table/tabledesign.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+
+#include <celltypes.hxx>
+
+#include <vector>
+#include <map>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::style;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::container;
+
+namespace sdr::table {
+
+typedef std::map< OUString, sal_Int32 > CellStyleNameMap;
+
+typedef ::comphelper::WeakComponentImplHelper< XStyle, XNameReplace, XServiceInfo, XIndexAccess, XModifyBroadcaster, XModifyListener > TableDesignStyleBase;
+
+namespace {
+
+class TableDesignStyle : public TableDesignStyleBase
+{
+public:
+ TableDesignStyle();
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XStyle
+ virtual sal_Bool SAL_CALL isUserDefined() override;
+ virtual sal_Bool SAL_CALL isInUse() override;
+ virtual OUString SAL_CALL getParentStyle() override;
+ virtual void SAL_CALL setParentStyle( const OUString& aParentStyle ) override;
+
+ // XNamed
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName( const OUString& aName ) override;
+
+ // XNameAccess
+ virtual Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual Sequence< OUString > SAL_CALL getElementNames() override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override ;
+ virtual Any SAL_CALL getByIndex( sal_Int32 Index ) override;
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString& aName, const Any& aElement ) override;
+
+ // XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener( const Reference< XModifyListener >& aListener ) override;
+ virtual void SAL_CALL removeModifyListener( const Reference< XModifyListener >& aListener ) override;
+
+ // XModifyListener
+ virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override;
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ void notifyModifyListener();
+
+ // this function is called upon disposing the component
+ virtual void disposing(std::unique_lock<std::mutex>&) override;
+
+ static const CellStyleNameMap& getCellStyleNameMap();
+
+ OUString msName;
+ Reference< XStyle > maCellStyles[style_count];
+ comphelper::OInterfaceContainerHelper4<XModifyListener> maModifyListeners;
+};
+
+}
+
+typedef std::vector< Reference< XStyle > > TableDesignStyleVector;
+
+namespace {
+
+class TableDesignFamily : public ::cppu::WeakImplHelper< XNameContainer, XNamed, XIndexAccess, XSingleServiceFactory, XServiceInfo, XComponent, XPropertySet >
+{
+public:
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XNamed
+ virtual OUString SAL_CALL getName( ) override;
+ virtual void SAL_CALL setName( const OUString& aName ) override;
+
+ // XNameAccess
+ virtual Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual Sequence< OUString > SAL_CALL getElementNames() override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XElementAccess
+ virtual Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override ;
+ virtual Any SAL_CALL getByIndex( sal_Int32 Index ) override;
+
+ // XNameContainer
+ virtual void SAL_CALL insertByName( const OUString& aName, const Any& aElement ) override;
+ virtual void SAL_CALL removeByName( const OUString& Name ) override;
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString& aName, const Any& aElement ) override;
+
+ // XSingleServiceFactory
+ virtual Reference< XInterface > SAL_CALL createInstance( ) override;
+ virtual Reference< XInterface > SAL_CALL createInstanceWithArguments( const Sequence< Any >& aArguments ) override;
+
+ // XComponent
+ virtual void SAL_CALL dispose( ) override;
+ virtual void SAL_CALL addEventListener( const Reference< XEventListener >& xListener ) override;
+ virtual void SAL_CALL removeEventListener( const Reference< XEventListener >& aListener ) override;
+
+ // XPropertySet
+ virtual Reference<XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const Any& aValue ) override;
+ virtual Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const Reference<XPropertyChangeListener>& xListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const Reference<XPropertyChangeListener>& aListener ) override;
+ virtual void SAL_CALL addVetoableChangeListener(const OUString& PropertyName, const Reference<XVetoableChangeListener>& aListener ) override;
+ virtual void SAL_CALL removeVetoableChangeListener(const OUString& PropertyName,const Reference<XVetoableChangeListener>&aListener ) override;
+
+ TableDesignStyleVector maDesigns;
+};
+
+}
+
+TableDesignStyle::TableDesignStyle()
+{
+}
+
+const CellStyleNameMap& TableDesignStyle::getCellStyleNameMap()
+{
+ static CellStyleNameMap const aMap
+ {
+ { OUString( "first-row" ) , first_row_style },
+ { OUString( "last-row" ) , last_row_style },
+ { OUString( "first-column" ) , first_column_style },
+ { OUString( "last-column" ) , last_column_style },
+ { OUString( "body" ) , body_style },
+ { OUString( "even-rows" ) , even_rows_style },
+ { OUString( "odd-rows" ) , odd_rows_style },
+ { OUString( "even-columns" ) , even_columns_style },
+ { OUString( "odd-columns" ) , odd_columns_style },
+ { OUString( "background" ) , background_style },
+ };
+
+ return aMap;
+}
+
+// XServiceInfo
+OUString SAL_CALL TableDesignStyle::getImplementationName()
+{
+ return "TableDesignStyle";
+}
+
+sal_Bool SAL_CALL TableDesignStyle::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+Sequence< OUString > SAL_CALL TableDesignStyle::getSupportedServiceNames()
+{
+ return { "com.sun.star.style.Style" };
+}
+
+// XStyle
+sal_Bool SAL_CALL TableDesignStyle::isUserDefined()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL TableDesignStyle::isInUse()
+{
+ std::unique_lock aGuard( m_aMutex );
+ if (maModifyListeners.getLength(aGuard))
+ {
+ comphelper::OInterfaceIteratorHelper4 it(aGuard, maModifyListeners);
+ while ( it.hasMoreElements() )
+ {
+ TableDesignUser* pUser = dynamic_cast< TableDesignUser* >( it.next().get() );
+ if( pUser && pUser->isInUse() )
+ return true;
+ }
+ }
+ return false;
+}
+
+
+OUString SAL_CALL TableDesignStyle::getParentStyle()
+{
+ return OUString();
+}
+
+
+void SAL_CALL TableDesignStyle::setParentStyle( const OUString& )
+{
+}
+
+
+// XNamed
+
+
+OUString SAL_CALL TableDesignStyle::getName()
+{
+ return msName;
+}
+
+
+void SAL_CALL TableDesignStyle::setName( const OUString& rName )
+{
+ msName = rName;
+}
+
+
+// XNameAccess
+
+
+Any SAL_CALL TableDesignStyle::getByName( const OUString& rName )
+{
+ const CellStyleNameMap& rMap = getCellStyleNameMap();
+
+ CellStyleNameMap::const_iterator iter = rMap.find( rName );
+ if( iter == rMap.end() )
+ throw NoSuchElementException();
+
+ return Any( maCellStyles[(*iter).second] );
+}
+
+
+Sequence< OUString > SAL_CALL TableDesignStyle::getElementNames()
+{
+ return comphelper::mapKeysToSequence( getCellStyleNameMap() );
+}
+
+
+sal_Bool SAL_CALL TableDesignStyle::hasByName( const OUString& rName )
+{
+ const CellStyleNameMap& rMap = getCellStyleNameMap();
+
+ CellStyleNameMap::const_iterator iter = rMap.find( rName );
+ return iter != rMap.end();
+}
+
+
+// XElementAccess
+
+
+Type SAL_CALL TableDesignStyle::getElementType()
+{
+ return cppu::UnoType<XStyle>::get();
+}
+
+
+sal_Bool SAL_CALL TableDesignStyle::hasElements()
+{
+ return true;
+}
+
+
+// XIndexAccess
+
+
+sal_Int32 SAL_CALL TableDesignStyle::getCount()
+{
+ return style_count;
+}
+
+
+Any SAL_CALL TableDesignStyle::getByIndex( sal_Int32 Index )
+{
+ if( (Index < 0) || (Index >= style_count) )
+ throw IndexOutOfBoundsException();
+
+ std::unique_lock aGuard( m_aMutex );
+ return Any( maCellStyles[Index] );
+}
+
+
+// XNameReplace
+
+
+void SAL_CALL TableDesignStyle::replaceByName( const OUString& rName, const Any& aElement )
+{
+ const CellStyleNameMap& rMap = getCellStyleNameMap();
+ CellStyleNameMap::const_iterator iter = rMap.find( rName );
+ if( iter == rMap.end() )
+ throw NoSuchElementException();
+
+
+ Reference< XStyle > xNewStyle;
+ if( !(aElement >>= xNewStyle) )
+ throw IllegalArgumentException();
+
+ const sal_Int32 nIndex = (*iter).second;
+
+ std::unique_lock aGuard( m_aMutex );
+
+ Reference< XStyle > xOldStyle( maCellStyles[nIndex] );
+
+ if( xNewStyle == xOldStyle )
+ return;
+
+ Reference< XModifyListener > xListener( this );
+
+ // end listening to old style, if possible
+ Reference< XModifyBroadcaster > xOldBroadcaster( xOldStyle, UNO_QUERY );
+ if( xOldBroadcaster.is() )
+ xOldBroadcaster->removeModifyListener( xListener );
+
+ // start listening to new style, if possible
+ Reference< XModifyBroadcaster > xNewBroadcaster( xNewStyle, UNO_QUERY );
+ if( xNewBroadcaster.is() )
+ xNewBroadcaster->addModifyListener( xListener );
+
+ maCellStyles[nIndex] = xNewStyle;
+}
+
+
+// XComponent
+
+
+void TableDesignStyle::disposing(std::unique_lock<std::mutex>&)
+{
+ for(Reference<XStyle> & rCellStyle : maCellStyles)
+ rCellStyle.clear();
+}
+
+
+// XModifyBroadcaster
+
+
+void SAL_CALL TableDesignStyle::addModifyListener( const Reference< XModifyListener >& xListener )
+{
+ std::unique_lock aGuard( m_aMutex );
+ if (m_bDisposed)
+ {
+ aGuard.unlock();
+ EventObject aEvt( static_cast< OWeakObject * >( this ) );
+ xListener->disposing( aEvt );
+ }
+ else
+ {
+ maModifyListeners.addInterface( aGuard, xListener );
+ }
+}
+
+
+void SAL_CALL TableDesignStyle::removeModifyListener( const Reference< XModifyListener >& xListener )
+{
+ std::unique_lock aGuard( m_aMutex );
+ maModifyListeners.removeInterface( aGuard, xListener );
+}
+
+
+void TableDesignStyle::notifyModifyListener()
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if( maModifyListeners.getLength(aGuard) )
+ {
+ EventObject aEvt( static_cast< OWeakObject * >( this ) );
+ maModifyListeners.forEach(aGuard,
+ [&] (Reference<XModifyListener> const& xListener)
+ { return xListener->modified(aEvt); });
+ }
+}
+
+
+// XModifyListener
+
+
+// if we get a modify hint from a style, notify all registered XModifyListener
+void SAL_CALL TableDesignStyle::modified( const css::lang::EventObject& )
+{
+ notifyModifyListener();
+}
+
+
+void SAL_CALL TableDesignStyle::disposing( const css::lang::EventObject& )
+{
+}
+
+
+// TableStyle
+
+
+// XServiceInfo
+OUString SAL_CALL TableDesignFamily::getImplementationName()
+{
+ return "TableDesignFamily";
+}
+
+sal_Bool SAL_CALL TableDesignFamily::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+Sequence< OUString > SAL_CALL TableDesignFamily::getSupportedServiceNames()
+{
+ return { "com.sun.star.style.StyleFamily" };
+}
+
+// XNamed
+OUString SAL_CALL TableDesignFamily::getName()
+{
+ return "table";
+}
+
+void SAL_CALL TableDesignFamily::setName( const OUString& )
+{
+}
+
+// XNameAccess
+Any SAL_CALL TableDesignFamily::getByName( const OUString& rName )
+{
+ SolarMutexGuard aGuard;
+
+ auto iter = std::find_if(maDesigns.begin(), maDesigns.end(),
+ [&rName](const Reference<XStyle>& rpStyle) { return rpStyle->getName() == rName; });
+ if (iter != maDesigns.end())
+ return Any( (*iter) );
+
+ throw NoSuchElementException();
+}
+
+
+Sequence< OUString > SAL_CALL TableDesignFamily::getElementNames()
+{
+ SolarMutexGuard aGuard;
+
+ Sequence< OUString > aRet( maDesigns.size() );
+ OUString* pNames = aRet.getArray();
+
+ for( const auto& rpStyle : maDesigns )
+ *pNames++ = rpStyle->getName();
+
+ return aRet;
+}
+
+
+sal_Bool SAL_CALL TableDesignFamily::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+
+ return std::any_of(maDesigns.begin(), maDesigns.end(),
+ [&aName](const Reference<XStyle>& rpStyle) { return rpStyle->getName() == aName; });
+}
+
+
+// XElementAccess
+
+
+Type SAL_CALL TableDesignFamily::getElementType()
+{
+ return cppu::UnoType<XStyle>::get();
+}
+
+
+sal_Bool SAL_CALL TableDesignFamily::hasElements()
+{
+ SolarMutexGuard aGuard;
+
+ return !maDesigns.empty();
+}
+
+
+// XIndexAccess
+
+
+sal_Int32 SAL_CALL TableDesignFamily::getCount()
+{
+ SolarMutexGuard aGuard;
+
+ return sal::static_int_cast< sal_Int32 >( maDesigns.size() );
+}
+
+
+Any SAL_CALL TableDesignFamily::getByIndex( sal_Int32 Index )
+{
+ SolarMutexGuard aGuard;
+
+ if( (Index >= 0) && (Index < sal::static_int_cast< sal_Int32 >( maDesigns.size() ) ) )
+ return Any( maDesigns[Index] );
+
+ throw IndexOutOfBoundsException();
+}
+
+
+// XNameContainer
+
+
+void SAL_CALL TableDesignFamily::insertByName( const OUString& rName, const Any& rElement )
+{
+ SolarMutexGuard aGuard;
+
+ Reference< XStyle > xStyle( rElement, UNO_QUERY );
+ if( !xStyle.is() )
+ throw IllegalArgumentException();
+
+ xStyle->setName( rName );
+ if (std::any_of(maDesigns.begin(), maDesigns.end(),
+ [&rName](const Reference<XStyle>& rpStyle) { return rpStyle->getName() == rName; }))
+ throw ElementExistException();
+
+ maDesigns.push_back( xStyle );
+}
+
+
+void SAL_CALL TableDesignFamily::removeByName( const OUString& rName )
+{
+ SolarMutexGuard aGuard;
+
+ auto iter = std::find_if(maDesigns.begin(), maDesigns.end(),
+ [&rName](const Reference<XStyle>& rpStyle) { return rpStyle->getName() == rName; });
+ if (iter != maDesigns.end())
+ {
+ maDesigns.erase( iter );
+ return;
+ }
+
+ throw NoSuchElementException();
+}
+
+
+// XNameReplace
+
+
+void SAL_CALL TableDesignFamily::replaceByName( const OUString& rName, const Any& aElement )
+{
+ SolarMutexGuard aGuard;
+
+ Reference< XStyle > xStyle( aElement, UNO_QUERY );
+ if( !xStyle.is() )
+ throw IllegalArgumentException();
+
+ auto iter = std::find_if(maDesigns.begin(), maDesigns.end(),
+ [&rName](const Reference<XStyle>& rpStyle) { return rpStyle->getName() == rName; });
+ if (iter != maDesigns.end())
+ {
+ (*iter) = xStyle;
+ xStyle->setName( rName );
+ return;
+ }
+
+ throw NoSuchElementException();
+}
+
+
+// XSingleServiceFactory
+
+
+Reference< XInterface > SAL_CALL TableDesignFamily::createInstance()
+{
+ return Reference< XInterface >( static_cast< XStyle* >( new TableDesignStyle ) );
+}
+
+
+Reference< XInterface > SAL_CALL TableDesignFamily::createInstanceWithArguments( const Sequence< Any >& )
+{
+ return createInstance();
+}
+
+
+// XComponent
+
+
+void SAL_CALL TableDesignFamily::dispose( )
+{
+ TableDesignStyleVector aDesigns;
+ aDesigns.swap( maDesigns );
+
+ for( const auto& rStyle : aDesigns )
+ {
+ Reference< XComponent > xComp( rStyle, UNO_QUERY );
+ if( xComp.is() )
+ xComp->dispose();
+ }
+}
+
+
+void SAL_CALL TableDesignFamily::addEventListener( const Reference< XEventListener >& )
+{
+}
+
+
+void SAL_CALL TableDesignFamily::removeEventListener( const Reference< XEventListener >& )
+{
+}
+
+
+// XPropertySet
+
+
+Reference<XPropertySetInfo> TableDesignFamily::getPropertySetInfo()
+{
+ OSL_FAIL( "###unexpected!" );
+ return Reference<XPropertySetInfo>();
+}
+
+
+void TableDesignFamily::setPropertyValue( const OUString& , const Any& )
+{
+ OSL_FAIL( "###unexpected!" );
+}
+
+
+Any TableDesignFamily::getPropertyValue( const OUString& PropertyName )
+{
+ if ( PropertyName != "DisplayName" )
+ {
+ throw UnknownPropertyException( "unknown property: " + PropertyName, static_cast<OWeakObject *>(this) );
+ }
+
+ OUString sDisplayName( SvxResId( RID_SVXSTR_STYLEFAMILY_TABLEDESIGN ) );
+ return Any( sDisplayName );
+}
+
+
+void TableDesignFamily::addPropertyChangeListener( const OUString& , const Reference<XPropertyChangeListener>& )
+{
+ OSL_FAIL( "###unexpected!" );
+}
+
+
+void TableDesignFamily::removePropertyChangeListener( const OUString& , const Reference<XPropertyChangeListener>& )
+{
+ OSL_FAIL( "###unexpected!" );
+}
+
+
+void TableDesignFamily::addVetoableChangeListener( const OUString& , const Reference<XVetoableChangeListener>& )
+{
+ OSL_FAIL( "###unexpected!" );
+}
+
+
+void TableDesignFamily::removeVetoableChangeListener( const OUString& , const Reference<XVetoableChangeListener>& )
+{
+ OSL_FAIL( "###unexpected!" );
+}
+
+
+Reference< XNameAccess > CreateTableDesignFamily()
+{
+ return new TableDesignFamily;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablehandles.cxx b/svx/source/table/tablehandles.cxx
new file mode 100644
index 000000000..a776c579e
--- /dev/null
+++ b/svx/source/table/tablehandles.cxx
@@ -0,0 +1,313 @@
+/* -*- 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 "tablehandles.hxx"
+
+#include <vcl/outdev.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <svx/sdr/overlay/overlayobject.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/svdmrkv.hxx>
+#include <svx/svdpagv.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx>
+#include <sdr/overlay/overlayrectangle.hxx>
+#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <osl/diagnose.h>
+
+namespace sdr::table {
+
+namespace {
+
+class OverlayTableEdge : public sdr::overlay::OverlayObject
+{
+protected:
+ basegfx::B2DPolyPolygon maPolyPolygon;
+ bool mbVisible;
+
+ // geometry creation for OverlayObject
+ virtual drawinglayer::primitive2d::Primitive2DContainer createOverlayObjectPrimitive2DSequence() override;
+
+public:
+ OverlayTableEdge( const basegfx::B2DPolyPolygon& rPolyPolygon, bool bVisible );
+};
+
+}
+
+TableEdgeHdl::TableEdgeHdl( const Point& rPnt, bool bHorizontal, sal_Int32 nMin, sal_Int32 nMax, sal_Int32 nEdges )
+: SdrHdl( rPnt, SdrHdlKind::User )
+, mbHorizontal( bHorizontal )
+, mnMin( nMin )
+, mnMax( nMax )
+, maEdges(nEdges)
+{
+}
+
+void TableEdgeHdl::SetEdge( sal_Int32 nEdge, sal_Int32 nStart, sal_Int32 nEnd, TableEdgeState eState )
+{
+ if( (nEdge >= 0) && (nEdge <= sal::static_int_cast<sal_Int32>(maEdges.size())) )
+ {
+ maEdges[nEdge].mnStart = nStart;
+ maEdges[nEdge].mnEnd = nEnd;
+ maEdges[nEdge].meState = eState;
+ }
+ else
+ {
+ OSL_FAIL( "sdr::table::TableEdgeHdl::SetEdge(), invalid edge!" );
+ }
+}
+
+PointerStyle TableEdgeHdl::GetPointer() const
+{
+ if( mbHorizontal )
+ return PointerStyle::VSplit;
+ else
+ return PointerStyle::HSplit;
+}
+
+sal_Int32 TableEdgeHdl::GetValidDragOffset( const SdrDragStat& rDrag ) const
+{
+ return std::clamp( static_cast<sal_Int32>(mbHorizontal ? rDrag.GetDY() : rDrag.GetDX()), mnMin, mnMax );
+}
+
+basegfx::B2DPolyPolygon TableEdgeHdl::getSpecialDragPoly(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aVisible;
+ basegfx::B2DPolyPolygon aInvisible;
+
+ // create and return visible and non-visible parts for drag
+ getPolyPolygon(aVisible, aInvisible, &rDrag);
+ aVisible.append(aInvisible);
+
+ return aVisible;
+}
+
+void TableEdgeHdl::getPolyPolygon(basegfx::B2DPolyPolygon& rVisible, basegfx::B2DPolyPolygon& rInvisible, const SdrDragStat* pDrag) const
+{
+ // changed method to create visible and invisible partial polygons in one run in
+ // separate PolyPolygons; both kinds are used
+ basegfx::B2DPoint aOffset(aPos.X(), aPos.Y());
+ rVisible.clear();
+ rInvisible.clear();
+
+ if( pDrag )
+ {
+ basegfx::Axis2D eDragAxis = mbHorizontal ? basegfx::Axis2D::Y : basegfx::Axis2D::X;
+ aOffset.set(eDragAxis, aOffset.get(eDragAxis) + GetValidDragOffset( *pDrag ));
+ }
+
+ basegfx::B2DPoint aStart(aOffset), aEnd(aOffset);
+ basegfx::Axis2D eAxis = mbHorizontal ? basegfx::Axis2D::X : basegfx::Axis2D::Y;
+
+ for( const TableEdge& aEdge : maEdges )
+ {
+ aStart.set(eAxis, aOffset.get(eAxis) + aEdge.mnStart);
+ aEnd.set(eAxis, aOffset.get(eAxis) + aEdge.mnEnd);
+
+ basegfx::B2DPolygon aPolygon;
+ aPolygon.append( aStart );
+ aPolygon.append( aEnd );
+
+ if(aEdge.meState == Visible)
+ {
+ rVisible.append(aPolygon);
+ }
+ else
+ {
+ rInvisible.append(aPolygon);
+ }
+ }
+}
+
+void TableEdgeHdl::CreateB2dIAObject()
+{
+ GetRidOfIAObject();
+
+ if(!pHdlList || !pHdlList->GetView() || pHdlList->GetView()->areMarkHandlesHidden())
+ return;
+
+ SdrMarkView* pView = pHdlList->GetView();
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ basegfx::B2DPolyPolygon aVisible;
+ basegfx::B2DPolyPolygon aInvisible;
+
+ // get visible and invisible parts
+ getPolyPolygon(aVisible, aInvisible, nullptr);
+
+ if(!(aVisible.count() || aInvisible.count()))
+ return;
+
+ for(sal_uInt32 nWindow = 0; nWindow < pPageView->PageWindowCount(); nWindow++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(nWindow);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ if(aVisible.count())
+ {
+ // create overlay object for visible parts
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject(new OverlayTableEdge(aVisible, true));
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+
+ if(aInvisible.count())
+ {
+ // also create overlay object for invisible parts to allow
+ // a standard HitTest using the primitives from that overlay object
+ // (see OverlayTableEdge implementation)
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject(new OverlayTableEdge(aInvisible, false));
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+ }
+}
+
+
+OverlayTableEdge::OverlayTableEdge( const basegfx::B2DPolyPolygon& rPolyPolygon, bool bVisible )
+: OverlayObject(COL_GRAY)
+, maPolyPolygon( rPolyPolygon )
+, mbVisible(bVisible)
+{
+}
+
+drawinglayer::primitive2d::Primitive2DContainer OverlayTableEdge::createOverlayObjectPrimitive2DSequence()
+{
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+
+ if(maPolyPolygon.count())
+ {
+ // Discussed with CL. Currently i will leave the transparence out since this
+ // a little bit expensive. We may check the look with drag polygons later
+ const drawinglayer::primitive2d::Primitive2DReference aReference(
+ new drawinglayer::primitive2d::PolyPolygonHairlinePrimitive2D(
+ maPolyPolygon,
+ getBaseColor().getBColor()));
+
+ if(mbVisible)
+ {
+ // visible, just return as sequence
+ aRetval = drawinglayer::primitive2d::Primitive2DContainer { aReference };
+ }
+ else
+ {
+ // embed in HiddenGeometryPrimitive2D to support HitTest of this invisible
+ // overlay object
+ drawinglayer::primitive2d::Primitive2DContainer aSequence { aReference };
+ const drawinglayer::primitive2d::Primitive2DReference aNewReference(
+ new drawinglayer::primitive2d::HiddenGeometryPrimitive2D(std::move(aSequence)));
+ aRetval = drawinglayer::primitive2d::Primitive2DContainer { aNewReference };
+ }
+ }
+
+ return aRetval;
+}
+
+
+TableBorderHdl::TableBorderHdl(
+ const tools::Rectangle& rRect,
+ bool bAnimate)
+: SdrHdl(rRect.TopLeft(), SdrHdlKind::Move),
+ maRectangle(rRect),
+ mbAnimate(bAnimate)
+{
+}
+
+PointerStyle TableBorderHdl::GetPointer() const
+{
+ return PointerStyle::Move;
+}
+
+// create marker for this kind
+void TableBorderHdl::CreateB2dIAObject()
+{
+ GetRidOfIAObject();
+
+ if (!pHdlList || !pHdlList->GetView() || pHdlList->GetView()->areMarkHandlesHidden())
+ return;
+
+ SdrMarkView* pView = pHdlList->GetView();
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if (!pPageView)
+ return;
+
+ for(sal_uInt32 nWindow = 0; nWindow < pPageView->PageWindowCount(); nWindow++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(nWindow);
+
+ if (rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference<sdr::overlay::OverlayManager>& xManager = rPageWindow.GetOverlayManager();
+
+ if (xManager.is())
+ {
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(maRectangle);
+ const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
+ const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
+ // make animation dependent from text edit active, because for tables
+ // this handle is also used when text edit *is* active for it. This
+ // interferes too much concerning repaint stuff (at least as long as
+ // text edit is not yet on the overlay)
+
+ OutputDevice& rOutDev = rPageWindow.GetPaintWindow().GetOutputDevice();
+ float fScaleFactor = rOutDev.GetDPIScaleFactor();
+ double fWidth = fScaleFactor * 6.0;
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject(
+ new sdr::overlay::OverlayRectangle(aRange.getMinimum(), aRange.getMaximum(),
+ aHilightColor, fTransparence,
+ fWidth, 0.0, 0.0, mbAnimate));
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+
+} // end of namespace sdr::table
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablehandles.hxx b/svx/source/table/tablehandles.hxx
new file mode 100644
index 000000000..095edb0b5
--- /dev/null
+++ b/svx/source/table/tablehandles.hxx
@@ -0,0 +1,89 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_TABLE_TABLEHANDLES_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_TABLEHANDLES_HXX
+
+#include <svx/svdhdl.hxx>
+
+
+namespace sdr::table {
+
+enum TableEdgeState { Empty, Invisible, Visible };
+
+struct TableEdge
+{
+ sal_Int32 mnStart;
+ sal_Int32 mnEnd;
+ TableEdgeState meState;
+
+ TableEdge() : mnStart(0), mnEnd(0), meState(Empty) {}
+};
+
+class TableEdgeHdl : public SdrHdl
+{
+public:
+ TableEdgeHdl( const Point& rPnt, bool bHorizontal, sal_Int32 nMin, sal_Int32 nMax, sal_Int32 nEdges );
+
+ sal_Int32 GetValidDragOffset( const SdrDragStat& rDrag ) const;
+
+ virtual PointerStyle GetPointer() const override;
+
+ void SetEdge( sal_Int32 nEdge, sal_Int32 nStart, sal_Int32 nEnd, TableEdgeState nState );
+
+ bool IsHorizontalEdge() const { return mbHorizontal; }
+
+ basegfx::B2DPolyPolygon getSpecialDragPoly(const SdrDragStat& rDrag) const;
+ void getPolyPolygon(basegfx::B2DPolyPolygon& rVisible, basegfx::B2DPolyPolygon& rInvisible, const SdrDragStat* pDrag) const;
+
+protected:
+ // create marker for this kind
+ virtual void CreateB2dIAObject() override;
+
+private:
+ bool mbHorizontal;
+ sal_Int32 mnMin, mnMax;
+ std::vector< TableEdge > maEdges;
+};
+
+class TableBorderHdl : public SdrHdl
+{
+public:
+ TableBorderHdl(
+ const tools::Rectangle& rRect,
+ bool bAnimate);
+
+ virtual PointerStyle GetPointer() const override;
+
+protected:
+ // create marker for this kind
+ virtual void CreateB2dIAObject() override;
+
+private:
+ tools::Rectangle maRectangle;
+
+ bool mbAnimate : 1;
+};
+
+} // end of namespace sdr::table
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablelayouter.cxx b/svx/source/table/tablelayouter.cxx
new file mode 100644
index 000000000..d846a719c
--- /dev/null
+++ b/svx/source/table/tablelayouter.cxx
@@ -0,0 +1,1311 @@
+/* -*- 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 <com/sun/star/table/XMergeableCell.hpp>
+
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/gen.hxx>
+#include <libxml/xmlwriter.h>
+
+#include <cell.hxx>
+#include <o3tl/safeint.hxx>
+#include <tablemodel.hxx>
+#include "tablelayouter.hxx"
+#include <svx/svdotable.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::text;
+
+
+namespace sdr::table {
+
+
+static SvxBorderLine gEmptyBorder;
+
+constexpr OUStringLiteral gsSize( u"Size" );
+
+TableLayouter::TableLayouter( const TableModelRef& xTableModel )
+: mxTable( xTableModel )
+{
+}
+
+
+TableLayouter::~TableLayouter()
+{
+ ClearBorderLayout();
+}
+
+
+basegfx::B2ITuple TableLayouter::getCellSize( const CellRef& xCell, const CellPos& rPos ) const
+{
+ sal_Int32 width = 0;
+ sal_Int32 height = 0;
+
+ try
+ {
+ if( xCell.is() && !xCell->isMerged() )
+ {
+ CellPos aPos( rPos );
+
+ sal_Int32 nRowCount = getRowCount();
+ sal_Int32 nRowSpan = std::max( xCell->getRowSpan(), sal_Int32(1) );
+ while( nRowSpan && (aPos.mnRow < nRowCount) )
+ {
+ if( static_cast<sal_Int32>(maRows.size()) <= aPos.mnRow )
+ break;
+
+ height = o3tl::saturating_add(height, maRows[aPos.mnRow++].mnSize);
+ nRowSpan--;
+ }
+
+ sal_Int32 nColCount = getColumnCount();
+ sal_Int32 nColSpan = std::max( xCell->getColumnSpan(), sal_Int32(1) );
+ while( nColSpan && (aPos.mnCol < nColCount ) )
+ {
+ if( static_cast<sal_Int32>(maColumns.size()) <= aPos.mnCol )
+ break;
+
+ width = o3tl::saturating_add(width, maColumns[aPos.mnCol++].mnSize);
+ nColSpan--;
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+
+ return basegfx::B2ITuple( width, height );
+}
+
+
+bool TableLayouter::getCellArea( const CellRef& xCell, const CellPos& rPos, basegfx::B2IRectangle& rArea ) const
+{
+ try
+ {
+ if( xCell.is() && !xCell->isMerged() && isValid(rPos) )
+ {
+ const basegfx::B2ITuple aCellSize( getCellSize( xCell, rPos ) );
+ const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB);
+
+ if( (rPos.mnCol < static_cast<sal_Int32>(maColumns.size())) && (rPos.mnRow < static_cast<sal_Int32>(maRows.size()) ) )
+ {
+ const sal_Int32 y = maRows[rPos.mnRow].mnPos;
+
+ sal_Int32 endy;
+ if (o3tl::checked_add(y, aCellSize.getY(), endy))
+ return false;
+
+ if(bRTL)
+ {
+ ///For RTL Table Calculate the Right End of cell instead of Left
+ const sal_Int32 x = maColumns[rPos.mnCol].mnPos + maColumns[rPos.mnCol].mnSize;
+ sal_Int32 startx;
+ if (o3tl::checked_sub(x, aCellSize.getX(), startx))
+ return false;
+ rArea = basegfx::B2IRectangle(startx, y, x, endy);
+ }
+ else
+ {
+ const sal_Int32 x = maColumns[rPos.mnCol].mnPos;
+ sal_Int32 endx;
+ if (o3tl::checked_add(x, aCellSize.getX(), endx))
+ return false;
+ rArea = basegfx::B2IRectangle(x, y, endx, endy);
+ }
+ return true;
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+ return false;
+}
+
+
+sal_Int32 TableLayouter::getRowHeight( sal_Int32 nRow ) const
+{
+ if( isValidRow(nRow) )
+ return maRows[nRow].mnSize;
+ else
+ return 0;
+}
+
+
+sal_Int32 TableLayouter::getColumnWidth( sal_Int32 nColumn ) const
+{
+ if( isValidColumn(nColumn) )
+ return maColumns[nColumn].mnSize;
+ else
+ return 0;
+}
+
+sal_Int32 TableLayouter::calcPreferredColumnWidth( sal_Int32 nColumn, Size aSize ) const
+{
+ sal_Int32 nRet = 0;
+ for ( sal_uInt32 nRow = 0; nRow < static_cast<sal_uInt32>(maRows.size()); ++nRow )
+ {
+ // Account for the space desired by spanned columns.
+ // Only the last spanned cell will try to ensure sufficient space,
+ // by looping through the previous columns, subtracting their portion.
+ sal_Int32 nWish = 0;
+ sal_Int32 nSpannedColumn = nColumn;
+ bool bFindSpan = true;
+ while ( bFindSpan && isValidColumn(nSpannedColumn) )
+ {
+ // recursive function call gets earlier portion of spanned column.
+ if ( nSpannedColumn < nColumn )
+ nWish -= calcPreferredColumnWidth( nSpannedColumn, aSize );
+
+ CellRef xCell( getCell( CellPos( nSpannedColumn, nRow ) ) );
+ if ( xCell.is() && !xCell->isMerged()
+ && (xCell->getColumnSpan() == 1 || nSpannedColumn < nColumn) )
+ {
+ nWish += xCell->calcPreferredWidth(aSize);
+ bFindSpan = false;
+ }
+ else if ( xCell.is() && xCell->isMerged()
+ && nColumn == nSpannedColumn
+ && isValidColumn(nColumn + 1) )
+ {
+ xCell = getCell( CellPos( nColumn + 1, nRow ) );
+ bFindSpan = xCell.is() && !xCell->isMerged();
+ }
+ nSpannedColumn--;
+ }
+ nRet = std::max( nRet, nWish );
+ }
+ return nRet;
+}
+
+
+bool TableLayouter::isEdgeVisible( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal ) const
+{
+ const BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders;
+
+ if( (nEdgeX >= 0) && (nEdgeX < sal::static_int_cast<sal_Int32>(rMap.size())) &&
+ (nEdgeY >= 0) && (nEdgeY < sal::static_int_cast<sal_Int32>(rMap[nEdgeX].size())) )
+ {
+ return rMap[nEdgeX][nEdgeY] != nullptr;
+ }
+ else
+ {
+ OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
+ }
+
+ return false;
+}
+
+
+/** returns the requested borderline in rpBorderLine or a null pointer if there is no border at this edge */
+SvxBorderLine* TableLayouter::getBorderLine( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal )const
+{
+ SvxBorderLine* pLine = nullptr;
+
+ const BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders;
+
+ if( (nEdgeX >= 0) && (nEdgeX < sal::static_int_cast<sal_Int32>(rMap.size())) &&
+ (nEdgeY >= 0) && (nEdgeY < sal::static_int_cast<sal_Int32>(rMap[nEdgeX].size())) )
+ {
+ pLine = rMap[nEdgeX][nEdgeY];
+ if( pLine == &gEmptyBorder )
+ pLine = nullptr;
+ }
+ else
+ {
+ OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
+ }
+
+ return pLine;
+}
+
+std::vector<EdgeInfo> TableLayouter::getHorizontalEdges()
+{
+ std::vector<EdgeInfo> aReturn;
+ sal_Int32 nRowSize = sal_Int32(maRows.size());
+ for (sal_Int32 i = 0; i <= nRowSize; i++)
+ {
+ sal_Int32 nEdgeMin = 0;
+ sal_Int32 nEdgeMax = 0;
+ sal_Int32 nEdge = getHorizontalEdge(i, &nEdgeMin, &nEdgeMax);
+ nEdgeMin -= nEdge;
+ nEdgeMax -= nEdge;
+ aReturn.emplace_back(i, nEdge, nEdgeMin, nEdgeMax);
+ }
+ return aReturn;
+}
+
+std::vector<EdgeInfo> TableLayouter::getVerticalEdges()
+{
+ std::vector<EdgeInfo> aReturn;
+ sal_Int32 nColumnSize = sal_Int32(maColumns.size());
+ for (sal_Int32 i = 0; i <= nColumnSize; i++)
+ {
+ sal_Int32 nEdgeMin = 0;
+ sal_Int32 nEdgeMax = 0;
+ sal_Int32 nEdge = getVerticalEdge(i, &nEdgeMin, &nEdgeMax);
+ nEdgeMin -= nEdge;
+ nEdgeMax -= nEdge;
+ aReturn.emplace_back(i, nEdge, nEdgeMin, nEdgeMax);
+ }
+ return aReturn;
+}
+
+sal_Int32 TableLayouter::getHorizontalEdge( int nEdgeY, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ )
+{
+ sal_Int32 nRet = 0;
+ const sal_Int32 nRowCount = getRowCount();
+ if( (nEdgeY >= 0) && (nEdgeY <= nRowCount ) )
+ nRet = maRows[std::min(static_cast<sal_Int32>(nEdgeY),nRowCount-1)].mnPos;
+
+ if( nEdgeY == nRowCount )
+ nRet += maRows[nEdgeY - 1].mnSize;
+
+ if( pnMin )
+ {
+ if( (nEdgeY > 0) && (nEdgeY <= nRowCount ) )
+ {
+ *pnMin = maRows[nEdgeY-1].mnPos + 600; // todo
+ }
+ else
+ {
+ *pnMin = nRet;
+ }
+ }
+
+ if( pnMax )
+ {
+ *pnMax = 0x0fffffff;
+ }
+ return nRet;
+}
+
+
+sal_Int32 TableLayouter::getVerticalEdge( int nEdgeX, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ )
+{
+ sal_Int32 nRet = 0;
+
+ const sal_Int32 nColCount = getColumnCount();
+ if( (nEdgeX >= 0) && (nEdgeX <= nColCount ) )
+ nRet = maColumns[std::min(static_cast<sal_Int32>(nEdgeX),nColCount-1)].mnPos;
+
+ const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB);
+ if( bRTL )
+ {
+ if( (nEdgeX >= 0) && (nEdgeX < nColCount) )
+ nRet += maColumns[nEdgeX].mnSize;
+ }
+ else
+ {
+ if( nEdgeX == nColCount )
+ nRet += maColumns[nEdgeX - 1].mnSize;
+ }
+
+ if( pnMin )
+ {
+ *pnMin = nRet;
+ if( bRTL )
+ {
+ if( nEdgeX < nColCount )
+ *pnMin = nRet - maColumns[nEdgeX].mnSize + getMinimumColumnWidth(nEdgeX);
+ }
+ else
+ {
+ if( (nEdgeX > 0) && (nEdgeX <= nColCount ) )
+ *pnMin = maColumns[nEdgeX-1].mnPos + getMinimumColumnWidth( nEdgeX-1 );
+ }
+ }
+
+ if( pnMax )
+ {
+ *pnMax = 0x0fffffff; // todo
+ if( bRTL )
+ {
+ if( nEdgeX > 0 )
+ *pnMax = nRet + maColumns[nEdgeX-1].mnSize - getMinimumColumnWidth( nEdgeX-1 );
+ }
+ else
+ {
+ if( (nEdgeX >= 0) && (nEdgeX < nColCount ) )
+ *pnMax = maColumns[nEdgeX].mnPos + maColumns[nEdgeX].mnSize - getMinimumColumnWidth( nEdgeX );
+ }
+ }
+
+ return nRet;
+}
+
+
+static bool checkMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32 nCellX, sal_Int32 nCellY, bool& bRunning )
+{
+ Reference< XMergeableCell > xCell( xTable->getCellByPosition( nCellX, nCellY ), UNO_QUERY );
+ if( xCell.is() && !xCell->isMerged() )
+ {
+ const sal_Int32 nRight = xCell->getColumnSpan() + nCellX;
+ const sal_Int32 nBottom = xCell->getRowSpan() + nCellY;
+ if( (nMergedX < nRight) && (nMergedY < nBottom) )
+ return true;
+
+ bRunning = false;
+ }
+ return false;
+}
+
+/** returns true if the cell(nMergedX,nMergedY) is merged with other cells.
+ the returned cell( rOriginX, rOriginY ) is the origin( top left cell ) of the merge.
+*/
+bool findMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32& rOriginX, sal_Int32& rOriginY )
+{
+ rOriginX = nMergedX;
+ rOriginY = nMergedY;
+
+ if( xTable.is() ) try
+ {
+ // check if this cell already the origin or not merged at all
+ Reference< XMergeableCell > xCell( xTable->getCellByPosition( nMergedX, nMergedY ), UNO_QUERY_THROW );
+ if( !xCell->isMerged() )
+ return true;
+
+ bool bCheckVert = true;
+ bool bCheckHorz = true;
+
+ sal_Int32 nMinCol = 0;
+ sal_Int32 nMinRow = 0;
+
+ sal_Int32 nStep = 1, i;
+
+ sal_Int32 nRow, nCol;
+ do
+ {
+ if( bCheckVert )
+ {
+ nRow = nMergedY - nStep;
+ if( nRow >= nMinRow )
+ {
+ nCol = nMergedX;
+ for( i = 0; (i <= nStep) && (nCol >= nMinCol); i++, nCol-- )
+ {
+ if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckVert ) )
+ {
+ rOriginX = nCol; rOriginY = nRow;
+ return true;
+ }
+
+ if( !bCheckVert )
+ {
+ if( nCol == nMergedX )
+ {
+ nMinRow = nRow+1;
+ }
+ else
+ {
+ bCheckVert = true;
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ bCheckVert = false;
+ }
+ }
+
+ if( bCheckHorz )
+ {
+ nCol = nMergedX - nStep;
+ if( nCol >= nMinCol )
+ {
+ nRow = nMergedY;
+ for( i = 0; (i < nStep) && (nRow >= nMinRow); i++, nRow-- )
+ {
+ if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckHorz ) )
+ {
+ rOriginX = nCol; rOriginY = nRow;
+ return true;
+ }
+
+ if( !bCheckHorz )
+ {
+ if( nRow == nMergedY )
+ {
+ nMinCol = nCol+1;
+ }
+ else
+ {
+ bCheckHorz = true;
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ bCheckHorz = false;
+ }
+ }
+ nStep++;
+ }
+ while( bCheckVert || bCheckHorz );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+ return false;
+}
+
+
+sal_Int32 TableLayouter::getMinimumColumnWidth( sal_Int32 nColumn )
+{
+ if( isValidColumn( nColumn ) )
+ {
+ return maColumns[nColumn].mnMinSize;
+ }
+ else
+ {
+ OSL_FAIL( "TableLayouter::getMinimumColumnWidth(), column out of range!" );
+ return 0;
+ }
+}
+
+
+sal_Int32 TableLayouter::distribute( LayoutVector& rLayouts, sal_Int32 nDistribute )
+{
+ // break loops after 100 runs to avoid freezing office due to developer error
+ sal_Int32 nSafe = 100;
+
+ const std::size_t nCount = rLayouts.size();
+ std::size_t nIndex;
+
+ bool bConstrainsBroken = false;
+
+ do
+ {
+ bConstrainsBroken = false;
+
+ // first enforce minimum size constrains on all entities
+ for( nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ Layout& rLayout = rLayouts[nIndex];
+ if( rLayout.mnSize < rLayout.mnMinSize )
+ {
+ sal_Int32 nDiff(0);
+ bConstrainsBroken |= o3tl::checked_sub(rLayout.mnMinSize, rLayout.mnSize, nDiff);
+ bConstrainsBroken |= o3tl::checked_sub(nDistribute, nDiff, nDistribute);
+ rLayout.mnSize = rLayout.mnMinSize;
+ }
+ }
+
+ // calculate current width
+ // if nDistribute is < 0 (shrinking), entities that are already
+ // at minimum width are not counted
+ sal_Int32 nCurrentWidth = 0;
+ for( nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ Layout& rLayout = rLayouts[nIndex];
+ if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) )
+ nCurrentWidth = o3tl::saturating_add(nCurrentWidth, rLayout.mnSize);
+ }
+
+ // now distribute over entities
+ if( (nCurrentWidth != 0) && (nDistribute != 0) )
+ {
+ sal_Int32 nDistributed = nDistribute;
+ for( nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ Layout& rLayout = rLayouts[nIndex];
+ if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) )
+ {
+ sal_Int32 n(nDistributed); // for last entity use up rest
+ if (nIndex != (nCount-1))
+ {
+ bConstrainsBroken |= o3tl::checked_multiply(nDistribute, rLayout.mnSize, n);
+ n /= nCurrentWidth;
+ }
+
+ bConstrainsBroken |= o3tl::checked_add(rLayout.mnSize, n, rLayout.mnSize);
+ nDistributed -= n;
+
+ if( rLayout.mnSize < rLayout.mnMinSize )
+ bConstrainsBroken = true;
+ }
+ }
+ }
+ } while( bConstrainsBroken && --nSafe );
+
+ sal_Int32 nSize = 0;
+ for( nIndex = 0; nIndex < nCount; ++nIndex )
+ nSize = o3tl::saturating_add(nSize, rLayouts[nIndex].mnSize);
+
+ return nSize;
+}
+
+
+typedef std::vector< CellRef > MergeableCellVector;
+typedef std::vector< MergeableCellVector > MergeVector;
+
+
+void TableLayouter::LayoutTableWidth( tools::Rectangle& rArea, bool bFit )
+{
+ const sal_Int32 nColCount = getColumnCount();
+ const sal_Int32 nRowCount = getRowCount();
+ if( nColCount == 0 )
+ return;
+
+ MergeVector aMergedCells( nColCount );
+ std::vector<sal_Int32> aOptimalColumns;
+
+ static const OUStringLiteral sOptimalSize(u"OptimalSize");
+
+ if( sal::static_int_cast< sal_Int32 >( maColumns.size() ) != nColCount )
+ maColumns.resize( nColCount );
+
+ Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_SET_THROW );
+
+ // first calculate current width and initial minimum width per column,
+ // merged cells will be counted later
+ sal_Int32 nCurrentWidth = 0;
+ sal_Int32 nCol = 0, nRow = 0;
+ for( nCol = 0; nCol < nColCount; nCol++ )
+ {
+ sal_Int32 nMinWidth = 0;
+
+ bool bIsEmpty = true; // check if all cells in this column are merged
+
+ for( nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ CellRef xCell( getCell( CellPos( nCol, nRow ) ) );
+ if( xCell.is() && !xCell->isMerged() )
+ {
+ bIsEmpty = false;
+
+ sal_Int32 nColSpan = xCell->getColumnSpan();
+ if( nColSpan > 1 )
+ {
+ // merged cells will be evaluated later
+ aMergedCells[nCol+nColSpan-1].push_back( xCell );
+ }
+ else
+ {
+ nMinWidth = std::max( nMinWidth, xCell->getMinimumWidth() );
+ }
+ }
+ }
+
+ maColumns[nCol].mnMinSize = nMinWidth;
+
+ if( bIsEmpty )
+ {
+ maColumns[nCol].mnSize = 0;
+ }
+ else
+ {
+ sal_Int32 nColWidth = 0;
+ Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
+ bool bOptimal = false;
+ xColSet->getPropertyValue( sOptimalSize ) >>= bOptimal;
+ if( bOptimal )
+ {
+ aOptimalColumns.push_back(nCol);
+ }
+ else
+ {
+ xColSet->getPropertyValue( gsSize ) >>= nColWidth;
+ }
+
+ maColumns[nCol].mnSize = std::max( nColWidth, nMinWidth);
+
+ nCurrentWidth = o3tl::saturating_add(nCurrentWidth, maColumns[nCol].mnSize);
+ }
+ }
+
+ // if we have optimal sized rows, distribute what is given (left)
+ if( !bFit && !aOptimalColumns.empty() && (nCurrentWidth < rArea.getWidth()) )
+ {
+ sal_Int32 nLeft = rArea.getWidth() - nCurrentWidth;
+ sal_Int32 nDistribute = nLeft / aOptimalColumns.size();
+
+ auto iter( aOptimalColumns.begin() );
+ while( iter != aOptimalColumns.end() )
+ {
+ sal_Int32 nOptCol = *iter++;
+ if( iter == aOptimalColumns.end() )
+ nDistribute = nLeft;
+
+ maColumns[nOptCol].mnSize += nDistribute;
+ nLeft -= nDistribute;
+ }
+
+ DBG_ASSERT( nLeft == 0, "svx::TableLayouter::LayoutTableWidtht(), layouting failed!" );
+ }
+
+ // now check if merged cells fit
+ for( nCol = 1; nCol < nColCount; ++nCol )
+ {
+ bool bChanges = false;
+
+ const sal_Int32 nOldSize = maColumns[nCol].mnSize;
+
+ for( const CellRef& xCell : aMergedCells[nCol] )
+ {
+ sal_Int32 nMinWidth = xCell->getMinimumWidth();
+
+ for( sal_Int32 nMCol = nCol - xCell->getColumnSpan() + 1; (nMCol > 0) && (nMCol < nCol); ++nMCol )
+ nMinWidth -= maColumns[nMCol].mnSize;
+
+ if( nMinWidth > maColumns[nCol].mnMinSize )
+ maColumns[nCol].mnMinSize = nMinWidth;
+
+ if( nMinWidth > maColumns[nCol].mnSize )
+ {
+ maColumns[nCol].mnSize = nMinWidth;
+ bChanges = true;
+ }
+ }
+
+ if( bChanges )
+ {
+ nCurrentWidth = o3tl::saturating_add(nCurrentWidth, maColumns[nCol].mnSize - nOldSize);
+ }
+ }
+
+ // now scale if wanted and needed
+ if( bFit && (nCurrentWidth != rArea.getWidth()) )
+ distribute( maColumns, rArea.getWidth() - nCurrentWidth );
+
+ // last step, update left edges
+ sal_Int32 nNewWidth = 0;
+
+ const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB);
+ RangeIterator<sal_Int32> coliter( 0, nColCount, !bRTL );
+ while( coliter.next(nCol ) )
+ {
+ maColumns[nCol].mnPos = nNewWidth;
+ nNewWidth = o3tl::saturating_add(nNewWidth, maColumns[nCol].mnSize);
+ if( bFit )
+ {
+ Reference< XPropertySet > xColSet( xCols->getByIndex(nCol), UNO_QUERY_THROW );
+ xColSet->setPropertyValue( gsSize, Any( maColumns[nCol].mnSize ) );
+ }
+ }
+
+ rArea.SetSize( Size( nNewWidth, rArea.GetHeight() ) );
+ updateCells( rArea );
+}
+
+
+void TableLayouter::LayoutTableHeight( tools::Rectangle& rArea, bool bFit )
+{
+ const sal_Int32 nColCount = getColumnCount();
+ const sal_Int32 nRowCount = getRowCount();
+
+ if( nRowCount == 0 )
+ return;
+
+ Reference< XTableRows > xRows( mxTable->getRows() );
+
+ MergeVector aMergedCells( nRowCount );
+ std::vector<sal_Int32> aOptimalRows;
+
+ static const OUStringLiteral sOptimalSize(u"OptimalSize");
+
+ // first calculate current height and initial minimum size per column,
+ // merged cells will be counted later
+ sal_Int32 nCurrentHeight = 0;
+ sal_Int32 nCol, nRow;
+ for( nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ sal_Int32 nMinHeight = 0;
+
+ bool bIsEmpty = true; // check if all cells in this row are merged
+
+ for( nCol = 0; nCol < nColCount; ++nCol )
+ {
+ CellRef xCell( getCell( CellPos( nCol, nRow ) ) );
+ if( xCell.is() && !xCell->isMerged() )
+ {
+ bIsEmpty = false;
+
+ sal_Int32 nRowSpan = xCell->getRowSpan();
+ if( nRowSpan > 1 )
+ {
+ // merged cells will be evaluated later
+ aMergedCells[nRow+nRowSpan-1].push_back( xCell );
+ }
+ else
+ {
+ nMinHeight = std::max( nMinHeight, xCell->getMinimumHeight() );
+ }
+ }
+ }
+
+ maRows[nRow].mnMinSize = nMinHeight;
+
+ if( bIsEmpty )
+ {
+ maRows[nRow].mnSize = 0;
+ }
+ else
+ {
+ sal_Int32 nRowHeight = 0;
+ Reference<XPropertySet> xRowSet(xRows->getByIndex(nRow), UNO_QUERY_THROW);
+
+ bool bOptimal = false;
+ xRowSet->getPropertyValue( sOptimalSize ) >>= bOptimal;
+ if( bOptimal )
+ {
+ aOptimalRows.push_back( nRow );
+ }
+ else
+ {
+ xRowSet->getPropertyValue( gsSize ) >>= nRowHeight;
+ }
+
+ maRows[nRow].mnSize = nRowHeight;
+
+ if( maRows[nRow].mnSize < nMinHeight )
+ maRows[nRow].mnSize = nMinHeight;
+
+ nCurrentHeight = o3tl::saturating_add(nCurrentHeight, maRows[nRow].mnSize);
+ }
+ }
+
+ // if we have optimal sized rows, distribute what is given (left)
+ if( !bFit && !aOptimalRows.empty() && (nCurrentHeight < rArea.getHeight()) )
+ {
+ sal_Int32 nLeft = rArea.getHeight() - nCurrentHeight;
+ sal_Int32 nDistribute = nLeft / aOptimalRows.size();
+
+ auto iter( aOptimalRows.begin() );
+ while( iter != aOptimalRows.end() )
+ {
+ sal_Int32 nOptRow = *iter++;
+ if( iter == aOptimalRows.end() )
+ nDistribute = nLeft;
+
+ maRows[nOptRow].mnSize += nDistribute;
+ nLeft -= nDistribute;
+
+ }
+
+ DBG_ASSERT( nLeft == 0, "svx::TableLayouter::LayoutTableHeight(), layouting failed!" );
+ }
+
+ // now check if merged cells fit
+ for( nRow = 1; nRow < nRowCount; ++nRow )
+ {
+ bool bChanges = false;
+ sal_Int32 nOldSize = maRows[nRow].mnSize;
+
+ for( const CellRef& xCell : aMergedCells[nRow] )
+ {
+ sal_Int32 nMinHeight = xCell->getMinimumHeight();
+
+ for( sal_Int32 nMRow = nRow - xCell->getRowSpan() + 1; (nMRow > 0) && (nMRow < nRow); ++nMRow )
+ nMinHeight -= maRows[nMRow].mnSize;
+
+ if( nMinHeight > maRows[nRow].mnMinSize )
+ maRows[nRow].mnMinSize = nMinHeight;
+
+ if( nMinHeight > maRows[nRow].mnSize )
+ {
+ maRows[nRow].mnSize = nMinHeight;
+ bChanges = true;
+ }
+ }
+ if( bChanges )
+ nCurrentHeight = o3tl::saturating_add(nCurrentHeight, maRows[nRow].mnSize - nOldSize);
+ }
+
+ // now scale if wanted and needed
+ if( bFit && nCurrentHeight != rArea.getHeight() )
+ distribute(maRows, o3tl::saturating_sub<sal_Int32>(rArea.getHeight(), nCurrentHeight));
+
+ // last step, update left edges
+ sal_Int32 nNewHeight = 0;
+ for( nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ maRows[nRow].mnPos = nNewHeight;
+ nNewHeight = o3tl::saturating_add(nNewHeight, maRows[nRow].mnSize);
+
+ if( bFit )
+ {
+ Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW );
+ xRowSet->setPropertyValue( gsSize, Any( maRows[nRow].mnSize ) );
+ }
+ }
+
+ rArea.SetSize( Size( rArea.GetWidth(), nNewHeight ) );
+ updateCells( rArea );
+}
+
+
+/** try to fit the table into the given rectangle.
+ If the rectangle is too small, it will be grown to fit the table. */
+void TableLayouter::LayoutTable( tools::Rectangle& rRectangle, bool bFitWidth, bool bFitHeight )
+{
+ if( !mxTable.is() )
+ return;
+
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+
+ if( (nRowCount != getRowCount()) || (nColCount != getColumnCount()) )
+ {
+ if( static_cast< sal_Int32 >( maRows.size() ) != nRowCount )
+ maRows.resize( nRowCount );
+
+ for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
+ maRows[nRow].clear();
+
+ if( static_cast< sal_Int32 >( maColumns.size() ) != nColCount )
+ maColumns.resize( nColCount );
+
+ for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
+ maColumns[nCol].clear();
+ }
+
+ LayoutTableWidth( rRectangle, bFitWidth );
+ LayoutTableHeight( rRectangle, bFitHeight );
+ UpdateBorderLayout();
+}
+
+
+void TableLayouter::updateCells( tools::Rectangle const & rRectangle )
+{
+ const sal_Int32 nColCount = getColumnCount();
+ const sal_Int32 nRowCount = getRowCount();
+
+ CellPos aPos;
+ for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ )
+ {
+ for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ )
+ {
+ CellRef xCell( getCell( aPos ) );
+ if( xCell.is() )
+ {
+ basegfx::B2IRectangle aCellArea;
+ if( getCellArea( xCell, aPos, aCellArea ) )
+ {
+ tools::Rectangle aCellRect;
+ aCellRect.SetLeft( aCellArea.getMinX() );
+ aCellRect.SetRight( aCellArea.getMaxX() );
+ aCellRect.SetTop( aCellArea.getMinY() );
+ aCellRect.SetBottom( aCellArea.getMaxY() );
+ aCellRect.Move( rRectangle.Left(), rRectangle.Top() );
+ xCell->setCellRect( aCellRect );
+ }
+ }
+ }
+ }
+}
+
+
+CellRef TableLayouter::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", "");
+ }
+ return xCell;
+}
+
+
+bool TableLayouter::HasPriority( const SvxBorderLine* pThis, const SvxBorderLine* pOther )
+{
+ if (!pThis || ((pThis == &gEmptyBorder) && (pOther != nullptr)))
+ return false;
+ if (!pOther || (pOther == &gEmptyBorder))
+ return true;
+
+ sal_uInt16 nThisSize = pThis->GetScaledWidth();
+ sal_uInt16 nOtherSize = pOther->GetScaledWidth();
+
+ if (nThisSize > nOtherSize)
+ return true;
+
+ else if (nThisSize < nOtherSize)
+ {
+ return false;
+ }
+ else
+ {
+ if ( pOther->GetInWidth() && !pThis->GetInWidth() )
+ {
+ return true;
+ }
+ else if ( pThis->GetInWidth() && !pOther->GetInWidth() )
+ {
+ return false;
+ }
+ else
+ {
+ return true; //! ???
+ }
+ }
+}
+
+void TableLayouter::SetBorder( sal_Int32 nCol, sal_Int32 nRow, bool bHorizontal, const SvxBorderLine* pLine )
+{
+ if (!pLine)
+ pLine = &gEmptyBorder;
+
+ BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders;
+
+ if( (nCol >= 0) && (nCol < sal::static_int_cast<sal_Int32>(rMap.size())) &&
+ (nRow >= 0) && (nRow < sal::static_int_cast<sal_Int32>(rMap[nCol].size())) )
+ {
+ SvxBorderLine *pOld = rMap[nCol][nRow];
+
+ if (HasPriority(pLine, pOld))
+ {
+ if (pOld && pOld != &gEmptyBorder)
+ delete pOld;
+
+ SvxBorderLine* pNew = (pLine != &gEmptyBorder) ? new SvxBorderLine(*pLine) : &gEmptyBorder;
+
+ rMap[nCol][nRow] = pNew;
+ }
+ }
+ else
+ {
+ OSL_FAIL( "sdr::table::TableLayouter::SetBorder(), invalid border!" );
+ }
+}
+
+void TableLayouter::ClearBorderLayout()
+{
+ ClearBorderLayout(maHorizontalBorders);
+ ClearBorderLayout(maVerticalBorders);
+}
+
+void TableLayouter::ClearBorderLayout(BorderLineMap& rMap)
+{
+ const sal_Int32 nColCount = rMap.size();
+
+ for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
+ {
+ const sal_Int32 nRowCount = rMap[nCol].size();
+ for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
+ {
+ SvxBorderLine* pLine = rMap[nCol][nRow];
+ if( pLine )
+ {
+ if( pLine != &gEmptyBorder )
+ delete pLine;
+
+ rMap[nCol][nRow] = nullptr;
+ }
+ }
+ }
+}
+
+void TableLayouter::ResizeBorderLayout()
+{
+ ClearBorderLayout();
+ ResizeBorderLayout(maHorizontalBorders);
+ ResizeBorderLayout(maVerticalBorders);
+}
+
+
+void TableLayouter::ResizeBorderLayout( BorderLineMap& rMap )
+{
+ const sal_Int32 nColCount = getColumnCount() + 1;
+ const sal_Int32 nRowCount = getRowCount() + 1;
+
+ if( sal::static_int_cast<sal_Int32>(rMap.size()) != nColCount )
+ rMap.resize( nColCount );
+
+ for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
+ {
+ if( sal::static_int_cast<sal_Int32>(rMap[nCol].size()) != nRowCount )
+ rMap[nCol].resize( nRowCount );
+ }
+}
+
+
+void TableLayouter::UpdateBorderLayout()
+{
+ // make sure old border layout is cleared and border maps have correct size
+ ResizeBorderLayout();
+
+ const sal_Int32 nColCount = getColumnCount();
+ const sal_Int32 nRowCount = getRowCount();
+
+ CellPos aPos;
+ for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ )
+ {
+ for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ )
+ {
+ CellRef xCell( getCell( aPos ) );
+ if( !xCell.is() )
+ continue;
+
+ const SvxBoxItem* pThisAttr = xCell->GetItemSet().GetItem<SvxBoxItem>( SDRATTR_TABLE_BORDER );
+ OSL_ENSURE(pThisAttr,"sdr::table::TableLayouter::UpdateBorderLayout(), no border attribute?");
+
+ if( !pThisAttr )
+ continue;
+
+ const sal_Int32 nLastRow = xCell->getRowSpan() + aPos.mnRow;
+ const sal_Int32 nLastCol = xCell->getColumnSpan() + aPos.mnCol;
+
+ for( sal_Int32 nRow = aPos.mnRow; nRow < nLastRow; nRow++ )
+ {
+ SetBorder( aPos.mnCol, nRow, false, pThisAttr->GetLeft() );
+ SetBorder( nLastCol, nRow, false, pThisAttr->GetRight() );
+ }
+
+ for( sal_Int32 nCol = aPos.mnCol; nCol < nLastCol; nCol++ )
+ {
+ SetBorder( nCol, aPos.mnRow, true, pThisAttr->GetTop() );
+ SetBorder( nCol, nLastRow, true, pThisAttr->GetBottom() );
+ }
+ }
+ }
+}
+
+
+void TableLayouter::DistributeColumns( ::tools::Rectangle& rArea,
+ sal_Int32 nFirstCol,
+ sal_Int32 nLastCol,
+ const bool bOptimize,
+ const bool bMinimize )
+{
+ if( !mxTable.is() )
+ return;
+
+ try
+ {
+ const sal_Int32 nColCount = getColumnCount();
+ Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_SET_THROW );
+ const Size aSize(0xffffff, 0xffffff);
+
+ //special case - optimize a single column
+ if ( (bOptimize || bMinimize) && nFirstCol == nLastCol )
+ {
+ const sal_Int32 nWish = calcPreferredColumnWidth(nFirstCol, aSize);
+ if ( nWish < getColumnWidth(nFirstCol) )
+ {
+ Reference< XPropertySet > xColSet( xCols->getByIndex(nFirstCol), UNO_QUERY_THROW );
+ xColSet->setPropertyValue( gsSize, Any( nWish ) );
+
+ //FitWidth automatically distributes the new excess space
+ LayoutTable( rArea, /*bFitWidth=*/!bMinimize, /*bFitHeight=*/false );
+ }
+ }
+
+ if( (nFirstCol < 0) || (nFirstCol>= nLastCol) || (nLastCol >= nColCount) )
+ return;
+
+ sal_Int32 nAllWidth = 0;
+ float fAllWish = 0;
+ sal_Int32 nUnused = 0;
+ std::vector<sal_Int32> aWish(nColCount);
+
+ for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
+ nAllWidth += getColumnWidth(nCol);
+
+ const sal_Int32 nEqualWidth = nAllWidth / (nLastCol-nFirstCol+1);
+
+ //pass 1 - collect unneeded space (from an equal width perspective)
+ if ( bMinimize || bOptimize )
+ {
+ for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
+ {
+ const sal_Int32 nIndex = nCol - nFirstCol;
+ aWish[nIndex] = calcPreferredColumnWidth(nCol, aSize);
+ fAllWish += aWish[nIndex];
+ if ( aWish[nIndex] < nEqualWidth )
+ nUnused += nEqualWidth - aWish[nIndex];
+ }
+ }
+ const sal_Int32 nDistributeExcess = nAllWidth - fAllWish;
+
+ sal_Int32 nWidth = nEqualWidth;
+ for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
+ {
+ if ( !bMinimize && nCol == nLastCol )
+ nWidth = nAllWidth; // last column gets rounding/logic errors
+ else if ( (bMinimize || bOptimize) && fAllWish )
+ {
+ //pass 2 - first come, first served when requesting from the
+ // unneeded pool, or proportionally allocate excess.
+ const sal_Int32 nIndex = nCol - nFirstCol;
+ if ( aWish[nIndex] > nEqualWidth + nUnused )
+ {
+ nWidth = nEqualWidth + nUnused;
+ nUnused = 0;
+ }
+ else
+ {
+ nWidth = aWish[nIndex];
+ if ( aWish[nIndex] > nEqualWidth )
+ nUnused -= aWish[nIndex] - nEqualWidth;
+
+ if ( !bMinimize && nDistributeExcess > 0 )
+ nWidth += nWidth / fAllWish * nDistributeExcess;
+ }
+ }
+
+ Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
+ xColSet->setPropertyValue( gsSize, Any( nWidth ) );
+
+ nAllWidth -= nWidth;
+ }
+
+ LayoutTable( rArea, !bMinimize, false );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+}
+
+
+void TableLayouter::DistributeRows( ::tools::Rectangle& rArea,
+ sal_Int32 nFirstRow,
+ sal_Int32 nLastRow,
+ const bool bOptimize,
+ const bool bMinimize )
+{
+ if( !mxTable.is() )
+ return;
+
+ try
+ {
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+ Reference< XTableRows > xRows( mxTable->getRows(), UNO_SET_THROW );
+ sal_Int32 nMinHeight = 0;
+
+ //special case - minimize a single row
+ if ( bMinimize && nFirstRow == nLastRow )
+ {
+ const sal_Int32 nWish = std::max( maRows[nFirstRow].mnMinSize, nMinHeight );
+ if ( nWish < getRowHeight(nFirstRow) )
+ {
+ Reference< XPropertySet > xRowSet( xRows->getByIndex( nFirstRow ), UNO_QUERY_THROW );
+ xRowSet->setPropertyValue( gsSize, Any( nWish ) );
+
+ LayoutTable( rArea, /*bFitWidth=*/false, /*bFitHeight=*/!bMinimize );
+ }
+ }
+
+ if( (nFirstRow < 0) || (nFirstRow>= nLastRow) || (nLastRow >= nRowCount) )
+ return;
+
+ sal_Int32 nAllHeight = 0;
+ sal_Int32 nMaxHeight = 0;
+
+ for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow )
+ {
+ nMinHeight = std::max( maRows[nRow].mnMinSize, nMinHeight );
+ nMaxHeight = std::max( maRows[nRow].mnSize, nMaxHeight );
+ nAllHeight += maRows[nRow].mnSize;
+ }
+
+ const sal_Int32 nRows = nLastRow-nFirstRow+1;
+ sal_Int32 nHeight = nAllHeight / nRows;
+
+ if ( !bMinimize && nHeight < nMaxHeight )
+ {
+ if ( !bOptimize )
+ {
+ sal_Int32 nNeededHeight = nRows * nMaxHeight;
+ rArea.AdjustBottom(nNeededHeight - nAllHeight );
+ nHeight = nMaxHeight;
+ nAllHeight = nRows * nMaxHeight;
+ }
+ else if ( nHeight < nMinHeight )
+ {
+ sal_Int32 nNeededHeight = nRows * nMinHeight;
+ rArea.AdjustBottom(nNeededHeight - nAllHeight );
+ nHeight = nMinHeight;
+ nAllHeight = nRows * nMinHeight;
+ }
+ }
+
+ for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow )
+ {
+ if ( bMinimize )
+ nHeight = maRows[nRow].mnMinSize;
+ else if ( nRow == nLastRow )
+ nHeight = nAllHeight; // last row get round errors
+
+ Reference< XPropertySet > xRowSet( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
+ xRowSet->setPropertyValue( gsSize, Any( nHeight ) );
+
+ nAllHeight -= nHeight;
+ }
+
+ LayoutTable( rArea, false, !bMinimize );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+}
+
+void TableLayouter::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("TableLayouter"));
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("columns"));
+ for (const auto& rColumn : maColumns)
+ rColumn.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("rows"));
+ for (const auto& rRow : maRows)
+ rRow.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void TableLayouter::Layout::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("TableLayouter_Layout"));
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("pos"), BAD_CAST(OString::number(mnPos).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("size"), BAD_CAST(OString::number(mnSize).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("minSize"), BAD_CAST(OString::number(mnMinSize).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablelayouter.hxx b/svx/source/table/tablelayouter.hxx
new file mode 100644
index 000000000..2ff69598e
--- /dev/null
+++ b/svx/source/table/tablelayouter.hxx
@@ -0,0 +1,172 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_TABLE_TABLELAYOUTER_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_TABLELAYOUTER_HXX
+
+#include <sal/types.h>
+#include <basegfx/range/b2irectangle.hxx>
+#include <basegfx/tuple/b2ituple.hxx>
+#include <o3tl/safeint.hxx>
+#include <vector>
+
+#include <svx/svdotable.hxx>
+#include <celltypes.hxx>
+
+namespace tools { class Rectangle; }
+
+
+namespace editeng {
+ class SvxBorderLine;
+}
+
+namespace sdr::table {
+
+/** returns true if the cell(nMergedCol,nMergedRow) is merged with other cells.
+ the returned cell( rOriginCol, rOriginRow ) is the origin( top left cell ) of the merge.
+*/
+bool findMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedCol, sal_Int32 nMergedRow, sal_Int32& rOriginCol, sal_Int32& rOriginRow );
+
+typedef std::vector< editeng::SvxBorderLine* > BorderLineVector;
+typedef std::vector< BorderLineVector > BorderLineMap;
+
+
+// TableModel
+
+struct EdgeInfo final
+{
+ sal_Int32 nIndex;
+ sal_Int32 nPosition;
+ sal_Int32 nMin;
+ sal_Int32 nMax;
+
+ EdgeInfo(sal_Int32 nInIndex, sal_Int32 nInPosition, sal_Int32 nInMin, sal_Int32 nInMax)
+ : nIndex(nInIndex)
+ , nPosition(nInPosition)
+ , nMin(nInMin)
+ , nMax(nInMax)
+ {}
+};
+
+class TableLayouter final
+{
+public:
+ explicit TableLayouter( const TableModelRef& xTableModel );
+ ~TableLayouter();
+
+ /** try to fit the table into the given rectangle.
+ If the rectangle is too small, it will be grown to fit the table.
+
+ if bFitWidth or bFitHeight is set, the layouter tries to scale
+ the rows and/or columns to the given area. The result my be bigger
+ to fulfill constrains.
+
+ if bFitWidth or bFitHeight is set, the model is changed.
+ */
+ void LayoutTable( ::tools::Rectangle& rRectangle, bool bFitWidth, bool bFitHeight );
+
+ void UpdateBorderLayout();
+
+ bool getCellArea( const CellRef& xCell, const CellPos& rPos, basegfx::B2IRectangle& rArea ) const;
+
+ ::sal_Int32 getRowCount() const { return static_cast< ::sal_Int32 >( maRows.size() ); }
+ ::sal_Int32 getColumnCount() const { return static_cast< ::sal_Int32 >( maColumns.size() ); }
+ sal_Int32 getRowHeight( sal_Int32 nRow ) const;
+
+ sal_Int32 getColumnWidth( sal_Int32 nColumn ) const;
+ sal_Int32 calcPreferredColumnWidth( sal_Int32 nColumn, Size aSize ) const;
+
+ sal_Int32 getMinimumColumnWidth( sal_Int32 nColumn );
+
+ /** checks if the given edge is visible.
+ Edges between merged cells are not visible.
+ */
+ bool isEdgeVisible( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal ) const;
+
+ /** returns the requested borderline in rpBorderLine or a null pointer if there is no border at this edge */
+ editeng::SvxBorderLine* getBorderLine( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal )const;
+
+ void updateCells( ::tools::Rectangle const & rRectangle );
+
+ std::vector<EdgeInfo> getHorizontalEdges();
+ sal_Int32 getHorizontalEdge( int nEdgeY, sal_Int32* pnMin, sal_Int32* pnMax );
+
+ std::vector<EdgeInfo> getVerticalEdges();
+ sal_Int32 getVerticalEdge( int nEdgeX , sal_Int32* pnMin, sal_Int32* pnMax);
+
+
+ void DistributeColumns( ::tools::Rectangle& rArea,
+ sal_Int32 nFirstCol,
+ sal_Int32 nLastCol,
+ const bool bOptimize,
+ const bool bMinimize );
+ void DistributeRows( ::tools::Rectangle& rArea,
+ sal_Int32 nFirstRow,
+ sal_Int32 nLastRow,
+ const bool bOptimize,
+ const bool bMinimize );
+ void dumpAsXml(xmlTextWriterPtr pWriter) const;
+
+ void LayoutTableWidth(::tools::Rectangle& rArea, bool bFit);
+ void LayoutTableHeight(::tools::Rectangle& rArea, bool bFit);
+
+private:
+ CellRef getCell( const CellPos& rPos ) const;
+ basegfx::B2ITuple getCellSize( const CellRef& xCell, const CellPos& rPos ) const;
+
+ bool isValidColumn( sal_Int32 nColumn ) const { return (nColumn >= 0) && (o3tl::make_unsigned(nColumn) < maColumns.size()); }
+ bool isValidRow( sal_Int32 nRow ) const { return (nRow >= 0) && (o3tl::make_unsigned(nRow) < maRows.size()); }
+ bool isValid( const CellPos& rPos ) const { return isValidColumn( rPos.mnCol ) && isValidRow( rPos.mnRow ); }
+
+ void ClearBorderLayout();
+ static void ClearBorderLayout(BorderLineMap& rMap);
+ void ResizeBorderLayout();
+ void ResizeBorderLayout( BorderLineMap& rMap );
+
+ void SetBorder( sal_Int32 nCol, sal_Int32 nRow, bool bHorizontal, const editeng::SvxBorderLine* pLine );
+
+ static bool HasPriority( const editeng::SvxBorderLine* pThis, const editeng::SvxBorderLine* pOther );
+
+ struct Layout
+ {
+ sal_Int32 mnPos;
+ sal_Int32 mnSize;
+ sal_Int32 mnMinSize;
+
+ Layout() : mnPos( 0 ), mnSize( 0 ), mnMinSize( 0 ) {}
+ void clear() { mnPos = 0; mnSize = 0; mnMinSize = 0; }
+ void dumpAsXml(xmlTextWriterPtr pWriter) const;
+ };
+ typedef std::vector< Layout > LayoutVector;
+
+ static sal_Int32 distribute( LayoutVector& rLayouts, sal_Int32 nDistribute );
+
+ TableModelRef mxTable;
+ LayoutVector maRows;
+ LayoutVector maColumns;
+
+ BorderLineMap maHorizontalBorders;
+ BorderLineMap maVerticalBorders;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablemodel.cxx b/svx/source/table/tablemodel.cxx
new file mode 100644
index 000000000..9104a0a2d
--- /dev/null
+++ b/svx/source/table/tablemodel.cxx
@@ -0,0 +1,1127 @@
+/* -*- 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 <sal/config.h>
+
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/table/XMergeableCell.hpp>
+
+#include <algorithm>
+
+#include <vcl/svapp.hxx>
+#include <osl/mutex.hxx>
+#include <libxml/xmlwriter.h>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <cell.hxx>
+#include "cellcursor.hxx"
+#include <tablemodel.hxx>
+#include "tablerow.hxx"
+#include "tablerows.hxx"
+#include "tablecolumn.hxx"
+#include "tablecolumns.hxx"
+#include "tableundo.hxx"
+#include <o3tl/safeint.hxx>
+#include <svx/svdotable.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+
+using namespace ::osl;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+
+
+namespace sdr::table {
+
+
+// removes the given range from a vector
+template< class Vec, class Iter > static void remove_range( Vec& rVector, sal_Int32 nIndex, sal_Int32 nCount )
+{
+ const sal_Int32 nSize = static_cast<sal_Int32>(rVector.size());
+ if( nCount && (nIndex >= 0) && (nIndex < nSize) )
+ {
+ if( (nIndex + nCount) >= nSize )
+ {
+ // remove at end
+ rVector.resize( nIndex );
+ }
+ else
+ {
+ rVector.erase(rVector.begin() + nIndex, rVector.begin() + nIndex + nCount);
+ }
+ }
+}
+
+
+/** inserts a range into a vector */
+template< class Vec, class Iter, class Entry > static sal_Int32 insert_range( Vec& rVector, sal_Int32 nIndex, sal_Int32 nCount )
+{
+ if( nCount )
+ {
+ if( nIndex >= static_cast< sal_Int32 >( rVector.size() ) )
+ {
+ // append at end
+ nIndex = static_cast< sal_Int32 >( rVector.size() ); // cap to end
+ rVector.resize( nIndex + nCount );
+ }
+ else
+ {
+ // insert
+ Iter aIter( rVector.begin() );
+ std::advance( aIter, nIndex );
+
+ Entry aEmpty;
+ rVector.insert( aIter, nCount, aEmpty );
+ }
+ }
+ return nIndex;
+}
+
+
+TableModel::TableModel( SdrTableObj* pTableObj )
+: TableModelBase( m_aMutex )
+, mpTableObj( pTableObj )
+, mbModified( false )
+, mbNotifyPending( false )
+, mnNotifyLock( 0 )
+{
+}
+
+TableModel::TableModel( SdrTableObj* pTableObj, const TableModelRef& xSourceTable )
+: TableModelBase( m_aMutex )
+, mpTableObj( pTableObj )
+, mbModified( false )
+, mbNotifyPending( false )
+, mnNotifyLock( 0 )
+{
+ if( !xSourceTable.is() )
+ return;
+
+ const sal_Int32 nColCount = xSourceTable->getColumnCountImpl();
+ const sal_Int32 nRowCount = xSourceTable->getRowCountImpl();
+
+ init( nColCount, nRowCount );
+
+ sal_Int32 nRows = nRowCount;
+ while( nRows-- )
+ (*maRows[nRows]) = *xSourceTable->maRows[nRows];
+
+ sal_Int32 nColumns = nColCount;
+ while( nColumns-- )
+ (*maColumns[nColumns]) = *xSourceTable->maColumns[nColumns];
+
+ // copy cells
+ for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol )
+ {
+ for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ CellRef xTargetCell( getCell( nCol, nRow ) );
+ if( xTargetCell.is() )
+ xTargetCell->cloneFrom( xSourceTable->getCell( nCol, nRow ) );
+ }
+ }
+}
+
+
+TableModel::~TableModel()
+{
+}
+
+
+void TableModel::init( sal_Int32 nColumns, sal_Int32 nRows )
+{
+ if( nRows < 20 )
+ maRows.reserve( 20 );
+
+ if( nColumns < 20 )
+ maColumns.reserve( 20 );
+
+ if( nRows && nColumns )
+ {
+ maColumns.resize( nColumns );
+ maRows.resize( nRows );
+
+ while( nRows-- )
+ maRows[nRows].set( new TableRow( this, nRows, nColumns ) );
+
+ while( nColumns-- )
+ maColumns[nColumns].set( new TableColumn( this, nColumns ) );
+ }
+}
+
+
+// ICellRange
+
+
+sal_Int32 TableModel::getLeft()
+{
+ return 0;
+}
+
+
+sal_Int32 TableModel::getTop()
+{
+ return 0;
+}
+
+
+sal_Int32 TableModel::getRight()
+{
+ return getColumnCount();
+}
+
+
+sal_Int32 TableModel::getBottom()
+{
+ return getRowCount();
+}
+
+
+Reference< XTable > TableModel::getTable()
+{
+ return this;
+}
+
+
+void TableModel::UndoInsertRows( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ TableModelNotifyGuard aGuard( this );
+
+ // remove the rows
+ remove_range<RowVector,RowVector::iterator>( maRows, nIndex, nCount );
+ updateRows();
+ setModified(true);
+}
+
+
+void TableModel::UndoRemoveRows( sal_Int32 nIndex, RowVector& aRows )
+{
+ TableModelNotifyGuard aGuard( this );
+
+ const sal_Int32 nCount = sal::static_int_cast< sal_Int32 >( aRows.size() );
+
+ nIndex = insert_range<RowVector,RowVector::iterator,TableRowRef>( maRows, nIndex, nCount );
+
+ for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
+ maRows[nIndex+nOffset] = aRows[nOffset];
+
+ updateRows();
+ setModified(true);
+}
+
+
+void TableModel::UndoInsertColumns( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ TableModelNotifyGuard aGuard( this );
+
+ // now remove the columns
+ remove_range<ColumnVector,ColumnVector::iterator>( maColumns, nIndex, nCount );
+ sal_Int32 nRows = getRowCountImpl();
+ while( nRows-- )
+ maRows[nRows]->removeColumns( nIndex, nCount );
+
+ updateColumns();
+ setModified(true);
+}
+
+
+void TableModel::UndoRemoveColumns( sal_Int32 nIndex, ColumnVector& aCols, CellVector& aCells )
+{
+ TableModelNotifyGuard aGuard( this );
+
+ const sal_Int32 nCount = sal::static_int_cast< sal_Int32 >( aCols.size() );
+
+ // assert if there are not enough cells saved
+ DBG_ASSERT( (aCols.size() * maRows.size()) == aCells.size(), "sdr::table::TableModel::UndoRemoveColumns(), invalid undo data!" );
+
+ nIndex = insert_range<ColumnVector,ColumnVector::iterator,TableColumnRef>( maColumns, nIndex, nCount );
+ for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
+ maColumns[nIndex+nOffset] = aCols[nOffset];
+
+ CellVector::iterator aIter( aCells.begin() );
+
+ sal_Int32 nRows = getRowCountImpl();
+ for( sal_Int32 nRow = 0; nRow < nRows; ++nRow )
+ {
+ CellVector::iterator aIter2 = aIter + nRow * nCount;
+ OSL_ENSURE(aIter2 < aCells.end(), "invalid iterator!");
+ maRows[nRow]->insertColumns( nIndex, nCount, &aIter2 );
+ }
+
+ updateColumns();
+ setModified(true);
+}
+
+
+// XTable
+
+
+Reference< XCellCursor > SAL_CALL TableModel::createCursor()
+{
+ ::SolarMutexGuard aGuard;
+ return createCursorByRange( Reference< XCellRange >( this ) );
+}
+
+
+Reference< XCellCursor > SAL_CALL TableModel::createCursorByRange( const Reference< XCellRange >& rRange )
+{
+ ::SolarMutexGuard aGuard;
+
+ ICellRange* pRange = dynamic_cast< ICellRange* >( rRange.get() );
+ if( (pRange == nullptr) || (pRange->getTable().get() != this) )
+ throw IllegalArgumentException();
+
+ TableModelRef xModel( this );
+ return new CellCursor( xModel, pRange->getLeft(), pRange->getTop(), pRange->getRight(), pRange->getBottom() );
+}
+
+
+sal_Int32 SAL_CALL TableModel::getRowCount()
+{
+ ::SolarMutexGuard aGuard;
+ return getRowCountImpl();
+}
+
+sal_Int32 SAL_CALL TableModel::getColumnCount()
+{
+ ::SolarMutexGuard aGuard;
+ return getColumnCountImpl();
+}
+
+std::vector<sal_Int32> TableModel::getColumnWidths()
+{
+ std::vector<sal_Int32> aRet;
+ for (const TableColumnRef& xColumn : maColumns)
+ aRet.push_back(xColumn->getWidth());
+ return aRet;
+}
+
+// XComponent
+
+
+void TableModel::dispose()
+{
+ ::SolarMutexGuard aGuard;
+ TableModelBase::dispose();
+}
+
+
+// XModifiable
+
+
+sal_Bool SAL_CALL TableModel::isModified( )
+{
+ ::SolarMutexGuard aGuard;
+ return mbModified;
+}
+
+
+void SAL_CALL TableModel::setModified( sal_Bool bModified )
+{
+ {
+ ::SolarMutexGuard aGuard;
+ mbModified = bModified;
+ }
+ if( bModified )
+ notifyModification();
+}
+
+
+// XModifyBroadcaster
+
+
+void SAL_CALL TableModel::addModifyListener( const Reference< XModifyListener >& xListener )
+{
+ rBHelper.addListener( cppu::UnoType<XModifyListener>::get() , xListener );
+}
+
+
+void SAL_CALL TableModel::removeModifyListener( const Reference< XModifyListener >& xListener )
+{
+ rBHelper.removeListener( cppu::UnoType<XModifyListener>::get() , xListener );
+}
+
+
+// XColumnRowRange
+
+
+Reference< XTableColumns > SAL_CALL TableModel::getColumns()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( !mxTableColumns.is() )
+ mxTableColumns.set( new TableColumns( this ) );
+ return mxTableColumns;
+}
+
+
+Reference< XTableRows > SAL_CALL TableModel::getRows()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( !mxTableRows.is() )
+ mxTableRows.set( new TableRows( this ) );
+ return mxTableRows;
+}
+
+
+// XCellRange
+
+
+Reference< XCell > SAL_CALL TableModel::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow )
+{
+ ::SolarMutexGuard aGuard;
+
+ CellRef xCell( getCell( nColumn, nRow ) );
+ if( xCell.is() )
+ return xCell;
+
+ throw IndexOutOfBoundsException();
+}
+
+
+Reference< XCellRange > SAL_CALL TableModel::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
+{
+ ::SolarMutexGuard aGuard;
+
+ if( (nLeft >= 0) && (nTop >= 0) && (nRight >= nLeft) && (nBottom >= nTop) && (nRight < getColumnCountImpl()) && (nBottom < getRowCountImpl() ) )
+ {
+ TableModelRef xModel( this );
+ return new CellRange( xModel, nLeft, nTop, nRight, nBottom );
+ }
+
+ throw IndexOutOfBoundsException();
+}
+
+
+Reference< XCellRange > SAL_CALL TableModel::getCellRangeByName( const OUString& /*aRange*/ )
+{
+ return Reference< XCellRange >();
+}
+
+
+// XPropertySet
+
+
+Reference< XPropertySetInfo > SAL_CALL TableModel::getPropertySetInfo( )
+{
+ Reference< XPropertySetInfo > xInfo;
+ return xInfo;
+}
+
+
+void SAL_CALL TableModel::setPropertyValue( const OUString& /*aPropertyName*/, const Any& /*aValue*/ )
+{
+}
+
+
+Any SAL_CALL TableModel::getPropertyValue( const OUString& /*PropertyName*/ )
+{
+ return Any();
+}
+
+
+void SAL_CALL TableModel::addPropertyChangeListener( const OUString& /*aPropertyName*/, const Reference< XPropertyChangeListener >& /*xListener*/ )
+{
+}
+
+
+void SAL_CALL TableModel::removePropertyChangeListener( const OUString& /*aPropertyName*/, const Reference< XPropertyChangeListener >& /*xListener*/ )
+{
+}
+
+
+void SAL_CALL TableModel::addVetoableChangeListener( const OUString& /*aPropertyName*/, const Reference< XVetoableChangeListener >& /*xListener*/ )
+{
+}
+
+
+void SAL_CALL TableModel::removeVetoableChangeListener( const OUString& /*aPropertyName*/, const Reference< XVetoableChangeListener >& /*xListener*/ )
+{
+}
+
+
+// XFastPropertySet
+
+
+void SAL_CALL TableModel::setFastPropertyValue( ::sal_Int32 /*nHandle*/, const Any& /*aValue*/ )
+{
+}
+
+
+Any SAL_CALL TableModel::getFastPropertyValue( ::sal_Int32 /*nHandle*/ )
+{
+ Any aAny;
+ return aAny;
+}
+
+
+// internals
+
+
+sal_Int32 TableModel::getRowCountImpl() const
+{
+ return static_cast< sal_Int32 >( maRows.size() );
+}
+
+
+sal_Int32 TableModel::getColumnCountImpl() const
+{
+ return static_cast< sal_Int32 >( maColumns.size() );
+}
+
+
+void TableModel::disposing()
+{
+ if( !maRows.empty() )
+ {
+ for( auto& rpRow : maRows )
+ rpRow->dispose();
+ RowVector().swap(maRows);
+ }
+
+ if( !maColumns.empty() )
+ {
+ for( auto& rpCol : maColumns )
+ rpCol->dispose();
+ ColumnVector().swap(maColumns);
+ }
+
+ if( mxTableColumns.is() )
+ {
+ mxTableColumns->dispose();
+ mxTableColumns.clear();
+ }
+
+ if( mxTableRows.is() )
+ {
+ mxTableRows->dispose();
+ mxTableRows.clear();
+ }
+
+ mpTableObj = nullptr;
+}
+
+
+// XBroadcaster
+
+
+void TableModel::lockBroadcasts()
+{
+ ::SolarMutexGuard aGuard;
+ ++mnNotifyLock;
+}
+
+
+void TableModel::unlockBroadcasts()
+{
+ ::SolarMutexGuard aGuard;
+ --mnNotifyLock;
+ if( mnNotifyLock <= 0 )
+ {
+ mnNotifyLock = 0;
+ if( mbNotifyPending )
+ notifyModification();
+ }
+}
+
+
+void TableModel::notifyModification()
+{
+ ::osl::MutexGuard guard( m_aMutex );
+ if( (mnNotifyLock == 0) && mpTableObj )
+ {
+ mbNotifyPending = false;
+
+ ::cppu::OInterfaceContainerHelper * pModifyListeners = rBHelper.getContainer( cppu::UnoType<XModifyListener>::get() );
+ if( pModifyListeners )
+ {
+ EventObject aSource;
+ aSource.Source = static_cast< ::cppu::OWeakObject* >(this);
+ pModifyListeners->notifyEach( &XModifyListener::modified, aSource);
+ }
+ }
+ else
+ {
+ mbNotifyPending = true;
+ }
+}
+
+
+CellRef TableModel::getCell( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ if( ((nRow >= 0) && (nRow < getRowCountImpl())) && (nCol >= 0) && (nCol < getColumnCountImpl()) )
+ {
+ return maRows[nRow]->maCells[nCol];
+ }
+ else
+ {
+ CellRef xRet;
+ return xRet;
+ }
+}
+
+
+CellRef TableModel::createCell()
+{
+ CellRef xCell;
+ if( mpTableObj )
+ mpTableObj->createCell( xCell );
+ return xCell;
+}
+
+
+void TableModel::insertColumns( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ if( !(nCount && mpTableObj) )
+ return;
+
+ try
+ {
+ SdrModel& rModel(mpTableObj->getSdrModelFromSdrObject());
+ TableModelNotifyGuard aGuard( this );
+ nIndex = insert_range<ColumnVector,ColumnVector::iterator,TableColumnRef>( maColumns, nIndex, nCount );
+
+ sal_Int32 nRows = getRowCountImpl();
+ while( nRows-- )
+ maRows[nRows]->insertColumns( nIndex, nCount, nullptr );
+
+ ColumnVector aNewColumns(nCount);
+ for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
+ {
+ TableColumnRef xNewCol( new TableColumn( this, nIndex+nOffset ) );
+ maColumns[nIndex+nOffset] = xNewCol;
+ aNewColumns[nOffset] = xNewCol;
+ }
+
+ const bool bUndo(mpTableObj->IsInserted() && rModel.IsUndoEnabled());
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_TABLE_INSCOL) );
+ rModel.AddUndo( rModel.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj) );
+
+ TableModelRef xThis( this );
+
+ nRows = getRowCountImpl();
+ CellVector aNewCells( nCount * nRows );
+ CellVector::iterator aCellIter( aNewCells.begin() );
+
+ nRows = getRowCountImpl();
+ for( sal_Int32 nRow = 0; nRow < nRows; ++nRow )
+ {
+ for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
+ (*aCellIter++) = getCell( nIndex + nOffset, nRow );
+ }
+
+ rModel.AddUndo( std::make_unique<InsertColUndo>( xThis, nIndex, aNewColumns, aNewCells ) );
+ }
+
+ const sal_Int32 nRowCount = getRowCountImpl();
+ // check if cells merge over new columns
+ for( sal_Int32 nCol = 0; nCol < nIndex; ++nCol )
+ {
+ for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ CellRef xCell( getCell( nCol, nRow ) );
+ sal_Int32 nColSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getColumnSpan() : 1;
+ if( (nColSpan != 1) && ((nColSpan + nCol ) > nIndex) )
+ {
+ // cell merges over newly created columns, so add the new columns to the merged cell
+ const sal_Int32 nRowSpan = xCell->getRowSpan();
+ nColSpan += nCount;
+ merge( nCol, nRow, nColSpan, nRowSpan );
+ }
+ }
+ }
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ rModel.SetChanged();
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+ updateColumns();
+ setModified(true);
+}
+
+
+void TableModel::removeColumns( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ sal_Int32 nColCount = getColumnCountImpl();
+
+ if( !(mpTableObj && nCount && (nIndex >= 0) && (nIndex < nColCount)) )
+ return;
+
+ try
+ {
+ TableModelNotifyGuard aGuard( this );
+
+ // clip removed columns to columns actually available
+ if( (nIndex + nCount) > nColCount )
+ nCount = nColCount - nIndex;
+
+ sal_Int32 nRows = getRowCountImpl();
+ SdrModel& rModel(mpTableObj->getSdrModelFromSdrObject());
+ const bool bUndo(mpTableObj->IsInserted() && rModel.IsUndoEnabled());
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_UNDO_COL_DELETE) );
+ rModel.AddUndo( rModel.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj) );
+ }
+
+ // only rows before and inside the removed rows are considered
+ nColCount = nIndex + nCount + 1;
+
+ const sal_Int32 nRowCount = getRowCountImpl();
+
+ // first check merged cells before and inside the removed rows
+ for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol )
+ {
+ for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ CellRef xCell( getCell( nCol, nRow ) );
+ sal_Int32 nColSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getColumnSpan() : 1;
+ if( nColSpan <= 1 )
+ continue;
+
+ if( nCol >= nIndex )
+ {
+ // current cell is inside the removed columns
+ if( (nCol + nColSpan) > ( nIndex + nCount ) )
+ {
+ // current cells merges with columns after the removed columns
+ const sal_Int32 nRemove = nCount - nCol + nIndex;
+
+ CellRef xTargetCell( getCell( nIndex + nCount, nRow ) );
+ if( xTargetCell.is() )
+ {
+ if( bUndo )
+ xTargetCell->AddUndo();
+ xTargetCell->merge( nColSpan - nRemove, xCell->getRowSpan() );
+ xTargetCell->replaceContentAndFormatting( xCell );
+ }
+ }
+ }
+ else if( nColSpan > (nIndex - nCol) )
+ {
+ // current cells spans inside the removed columns, so adjust
+ const sal_Int32 nRemove = ::std::min( nCount, nCol + nColSpan - nIndex );
+ if( bUndo )
+ xCell->AddUndo();
+ xCell->merge( nColSpan - nRemove, xCell->getRowSpan() );
+ }
+ }
+ }
+
+ // We must not add RemoveColUndo before we make cell spans correct, otherwise we
+ // get invalid cell span after undo.
+ if( bUndo )
+ {
+ TableModelRef xThis( this );
+ ColumnVector aRemovedCols( nCount );
+ sal_Int32 nOffset;
+ for( nOffset = 0; nOffset < nCount; ++nOffset )
+ {
+ aRemovedCols[nOffset] = maColumns[nIndex+nOffset];
+ }
+
+ CellVector aRemovedCells( nCount * nRows );
+ CellVector::iterator aCellIter( aRemovedCells.begin() );
+ for( sal_Int32 nRow = 0; nRow < nRows; ++nRow )
+ {
+ for( nOffset = 0; nOffset < nCount; ++nOffset )
+ (*aCellIter++) = getCell( nIndex + nOffset, nRow );
+ }
+
+ rModel.AddUndo( std::make_unique<RemoveColUndo>( xThis, nIndex, aRemovedCols, aRemovedCells ) );
+ }
+
+ // now remove the columns
+ remove_range<ColumnVector,ColumnVector::iterator>( maColumns, nIndex, nCount );
+ while( nRows-- )
+ maRows[nRows]->removeColumns( nIndex, nCount );
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ rModel.SetChanged();
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+
+ updateColumns();
+ setModified(true);
+}
+
+
+void TableModel::insertRows( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ if( !(nCount && mpTableObj) )
+ return;
+
+ SdrModel& rModel(mpTableObj->getSdrModelFromSdrObject());
+ const bool bUndo(mpTableObj->IsInserted() && rModel.IsUndoEnabled());
+
+ try
+ {
+ TableModelNotifyGuard aGuard( this );
+
+ nIndex = insert_range<RowVector,RowVector::iterator,TableRowRef>( maRows, nIndex, nCount );
+
+ RowVector aNewRows(nCount);
+ const sal_Int32 nColCount = getColumnCountImpl();
+ for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
+ {
+ TableRowRef xNewRow( new TableRow( this, nIndex+nOffset, nColCount ) );
+ maRows[nIndex+nOffset] = xNewRow;
+ aNewRows[nOffset] = xNewRow;
+ }
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_TABLE_INSROW) );
+ rModel.AddUndo( rModel.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj) );
+ TableModelRef xThis( this );
+ rModel.AddUndo( std::make_unique<InsertRowUndo>( xThis, nIndex, aNewRows ) );
+ }
+
+ // check if cells merge over new columns
+ for( sal_Int32 nRow = 0; nRow < nIndex; ++nRow )
+ {
+ for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol )
+ {
+ CellRef xCell( getCell( nCol, nRow ) );
+ sal_Int32 nRowSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getRowSpan() : 1;
+ if( (nRowSpan > 1) && ((nRowSpan + nRow) > nIndex) )
+ {
+ // cell merges over newly created columns, so add the new columns to the merged cell
+ const sal_Int32 nColSpan = xCell->getColumnSpan();
+ nRowSpan += nCount;
+ merge( nCol, nRow, nColSpan, nRowSpan );
+ }
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+ if( bUndo )
+ rModel.EndUndo();
+
+ rModel.SetChanged();
+
+ updateRows();
+ setModified(true);
+}
+
+
+void TableModel::removeRows( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ sal_Int32 nRowCount = getRowCountImpl();
+
+ if( !(mpTableObj && nCount && (nIndex >= 0) && (nIndex < nRowCount)) )
+ return;
+
+ SdrModel& rModel(mpTableObj->getSdrModelFromSdrObject());
+ const bool bUndo(mpTableObj->IsInserted() && rModel.IsUndoEnabled());
+
+ try
+ {
+ TableModelNotifyGuard aGuard( this );
+
+ // clip removed rows to rows actually available
+ if( (nIndex + nCount) > nRowCount )
+ nCount = nRowCount - nIndex;
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_UNDO_ROW_DELETE) );
+ rModel.AddUndo( rModel.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj) );
+ }
+
+ // only rows before and inside the removed rows are considered
+ nRowCount = nIndex + nCount + 1;
+
+ const sal_Int32 nColCount = getColumnCountImpl();
+
+ // first check merged cells before and inside the removed rows
+ for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol )
+ {
+ CellRef xCell( getCell( nCol, nRow ) );
+ sal_Int32 nRowSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getRowSpan() : 1;
+ if( nRowSpan <= 1 )
+ continue;
+
+ if( nRow >= nIndex )
+ {
+ // current cell is inside the removed rows
+ if( (nRow + nRowSpan) > (nIndex + nCount) )
+ {
+ // current cells merges with rows after the removed rows
+ const sal_Int32 nRemove = nCount - nRow + nIndex;
+
+ CellRef xTargetCell( getCell( nCol, nIndex + nCount ) );
+ if( xTargetCell.is() )
+ {
+ if( bUndo )
+ xTargetCell->AddUndo();
+ xTargetCell->merge( xCell->getColumnSpan(), nRowSpan - nRemove );
+ xTargetCell->replaceContentAndFormatting( xCell );
+ }
+ }
+ }
+ else if( nRowSpan > (nIndex - nRow) )
+ {
+ // current cells spans inside the removed rows, so adjust
+ const sal_Int32 nRemove = ::std::min( nCount, nRow + nRowSpan - nIndex );
+ if( bUndo )
+ xCell->AddUndo();
+ xCell->merge( xCell->getColumnSpan(), nRowSpan - nRemove );
+ }
+ }
+ }
+
+ if( bUndo )
+ {
+ TableModelRef xThis( this );
+
+ RowVector aRemovedRows( nCount );
+ for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
+ aRemovedRows[nOffset] = maRows[nIndex+nOffset];
+
+ // We must not RemoveRowUndo before we make cell spans correct, otherwise we
+ // get invalid cell span after undo.
+ rModel.AddUndo( std::make_unique<RemoveRowUndo>( xThis, nIndex, aRemovedRows ) );
+ }
+ // now remove the rows
+ remove_range<RowVector,RowVector::iterator>( maRows, nIndex, nCount );
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ rModel.SetChanged();
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+
+ updateRows();
+ setModified(true);
+}
+
+
+TableRowRef const & TableModel::getRow( sal_Int32 nRow ) const
+{
+ if( (nRow >= 0) && (nRow < getRowCountImpl()) )
+ return maRows[nRow];
+
+ throw IndexOutOfBoundsException();
+}
+
+
+TableColumnRef const & TableModel::getColumn( sal_Int32 nColumn ) const
+{
+ if( (nColumn >= 0) && (nColumn < getColumnCountImpl()) )
+ return maColumns[nColumn];
+
+ throw IndexOutOfBoundsException();
+}
+
+
+/** deletes rows and columns that are completely merged. Must be called between BegUndo/EndUndo! */
+void TableModel::optimize()
+{
+ TableModelNotifyGuard aGuard( this );
+
+ bool bWasModified = false;
+
+ if( !maRows.empty() && !maColumns.empty() )
+ {
+ sal_Int32 nCol = getColumnCountImpl() - 1;
+ sal_Int32 nRows = getRowCountImpl();
+ while( nCol > 0 )
+ {
+ bool bEmpty = true;
+ for( sal_Int32 nRow = 0; (nRow < nRows) && bEmpty; nRow++ )
+ {
+ Reference< XMergeableCell > xCell( getCellByPosition( nCol, nRow ), UNO_QUERY );
+ if( xCell.is() && !xCell->isMerged() )
+ bEmpty = false;
+ }
+
+ if( bEmpty )
+ {
+ try
+ {
+ static const OUStringLiteral sWidth(u"Width");
+ sal_Int32 nWidth1 = 0, nWidth2 = 0;
+ Reference< XPropertySet > xSet1( static_cast< XCellRange* >( maColumns[nCol].get() ), UNO_QUERY_THROW );
+ Reference< XPropertySet > xSet2( static_cast< XCellRange* >( maColumns[nCol-1].get() ), UNO_QUERY_THROW );
+ xSet1->getPropertyValue( sWidth ) >>= nWidth1;
+ xSet2->getPropertyValue( sWidth ) >>= nWidth2;
+ nWidth1 = o3tl::saturating_add(nWidth1, nWidth2);
+ xSet2->setPropertyValue( sWidth, Any( nWidth1 ) );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+
+ removeColumns( nCol, 1 );
+ bWasModified = true;
+ }
+
+ nCol--;
+ }
+
+ sal_Int32 nRow = getRowCountImpl() - 1;
+ sal_Int32 nCols = getColumnCountImpl();
+ while( nRow > 0 )
+ {
+ bool bEmpty = true;
+ for( nCol = 0; (nCol < nCols) && bEmpty; nCol++ )
+ {
+ Reference< XMergeableCell > xCell( getCellByPosition( nCol, nRow ), UNO_QUERY );
+ if( xCell.is() && !xCell->isMerged() )
+ bEmpty = false;
+ }
+
+ if( bEmpty )
+ {
+ try
+ {
+ static const OUStringLiteral sHeight(u"Height");
+ sal_Int32 nHeight1 = 0, nHeight2 = 0;
+ Reference< XPropertySet > xSet1( static_cast< XCellRange* >( maRows[nRow].get() ), UNO_QUERY_THROW );
+ Reference< XPropertySet > xSet2( static_cast< XCellRange* >( maRows[nRow-1].get() ), UNO_QUERY_THROW );
+ xSet1->getPropertyValue( sHeight ) >>= nHeight1;
+ xSet2->getPropertyValue( sHeight ) >>= nHeight2;
+ nHeight1 = o3tl::saturating_add(nHeight1, nHeight2);
+ xSet2->setPropertyValue( sHeight, Any( nHeight1 ) );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+
+ removeRows( nRow, 1 );
+ bWasModified = true;
+ }
+
+ nRow--;
+ }
+ }
+ if( bWasModified )
+ setModified(true);
+}
+
+
+void TableModel::merge( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nColSpan, sal_Int32 nRowSpan )
+{
+ if(nullptr == mpTableObj)
+ return;
+
+ SdrModel& rModel(mpTableObj->getSdrModelFromSdrObject());
+ const bool bUndo(mpTableObj->IsInserted() && rModel.IsUndoEnabled());
+ const sal_Int32 nLastRow = nRow + nRowSpan;
+ const sal_Int32 nLastCol = nCol + nColSpan;
+
+ if( (nLastRow > getRowCount()) || (nLastCol > getColumnCount() ) )
+ {
+ OSL_FAIL("TableModel::merge(), merge beyond the table!");
+ }
+
+ // merge first cell
+ CellRef xOriginCell( dynamic_cast< Cell* >( getCellByPosition( nCol, nRow ).get() ) );
+ if(!xOriginCell.is())
+ return;
+
+ if( bUndo )
+ xOriginCell->AddUndo();
+ xOriginCell->merge( nColSpan, nRowSpan );
+
+ sal_Int32 nTempCol = nCol + 1;
+
+ // merge remaining cells
+ for( ; nRow < nLastRow; nRow++ )
+ {
+ for( ; nTempCol < nLastCol; nTempCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( getCellByPosition( nTempCol, nRow ).get() ) );
+ if( xCell.is() && !xCell->isMerged() )
+ {
+ if( bUndo )
+ xCell->AddUndo();
+ xCell->setMerged();
+ xOriginCell->mergeContent( xCell );
+ }
+ }
+ nTempCol = nCol;
+ }
+}
+
+void TableModel::updateRows()
+{
+ sal_Int32 nRow = 0;
+ for( auto& rpRow : maRows )
+ {
+ rpRow->mnRow = nRow++;
+ }
+}
+
+void TableModel::updateColumns()
+{
+ sal_Int32 nColumn = 0;
+ for( auto& rpCol : maColumns )
+ {
+ rpCol->mnColumn = nColumn++;
+ }
+}
+
+void TableModel::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("TableModel"));
+ for (sal_Int32 nRow = 0; nRow < getRowCountImpl(); ++nRow)
+ for (sal_Int32 nCol = 0; nCol < getColumnCountImpl(); ++nCol)
+ {
+ maRows[nRow]->maCells[nCol]->dumpAsXml(pWriter, nRow, nCol);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablerow.cxx b/svx/source/table/tablerow.cxx
new file mode 100644
index 000000000..71b51a653
--- /dev/null
+++ b/svx/source/table/tablerow.cxx
@@ -0,0 +1,351 @@
+/* -*- 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 <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+#include <cell.hxx>
+#include "tablerow.hxx"
+#include "tableundo.hxx"
+#include <svx/svdmodel.hxx>
+#include <svx/svdotable.hxx>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::beans;
+
+
+namespace sdr::table {
+
+const sal_Int32 Property_Height = 0;
+const sal_Int32 Property_OptimalHeight = 1;
+const sal_Int32 Property_IsVisible = 2;
+const sal_Int32 Property_IsStartOfNewPage = 3;
+
+TableRow::TableRow( const TableModelRef& xTableModel, sal_Int32 nRow, sal_Int32 nColumns )
+: TableRowBase( getStaticPropertySetInfo() )
+, mxTableModel( xTableModel )
+, mnRow( nRow )
+, mnHeight( 0 )
+, mbOptimalHeight( true )
+, mbIsVisible( true )
+, mbIsStartOfNewPage( false )
+{
+ if( nColumns < 20 )
+ maCells.reserve( 20 );
+
+ if( nColumns )
+ {
+ maCells.resize( nColumns );
+ while( nColumns-- )
+ maCells[ nColumns ] = mxTableModel->createCell();
+ }
+}
+
+
+TableRow::~TableRow()
+{
+}
+
+
+void TableRow::dispose()
+{
+ mxTableModel.clear();
+ if( !maCells.empty() )
+ {
+ for( auto& rpCell : maCells )
+ rpCell->dispose();
+ CellVector().swap(maCells);
+ }
+}
+
+
+void TableRow::throwIfDisposed() const
+{
+ if( !mxTableModel.is() )
+ throw DisposedException();
+}
+
+
+TableRow& TableRow::operator=( const TableRow& r )
+{
+ mnHeight = r.mnHeight;
+ mbOptimalHeight = r.mbOptimalHeight;
+ mbIsVisible = r.mbIsVisible;
+ mbIsStartOfNewPage = r.mbIsStartOfNewPage;
+ maName = r.maName;
+ mnRow = r.mnRow;
+
+ return *this;
+}
+
+
+void TableRow::insertColumns( sal_Int32 nIndex, sal_Int32 nCount, CellVector::iterator const * pIter /* = 0 */ )
+{
+ throwIfDisposed();
+ if( !nCount )
+ return;
+
+ if( nIndex >= static_cast< sal_Int32 >( maCells.size() ) )
+ nIndex = static_cast< sal_Int32 >( maCells.size() );
+ if ( pIter )
+ maCells.insert( maCells.begin() + nIndex, *pIter, (*pIter) + nCount );
+ else
+ {
+ maCells.reserve( maCells.size() + nCount );
+ for ( sal_Int32 i = 0; i < nCount; i++ )
+ maCells.insert( maCells.begin() + nIndex + i, mxTableModel->createCell() );
+ }
+}
+
+
+void TableRow::removeColumns( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ throwIfDisposed();
+ if( (nCount < 0) || ( nIndex < 0))
+ return;
+
+ if( (nIndex + nCount) < static_cast< sal_Int32 >( maCells.size() ) )
+ {
+ CellVector::iterator aBegin( maCells.begin() );
+ std::advance(aBegin, nIndex);
+
+ if( nCount > 1 )
+ {
+ CellVector::iterator aEnd( aBegin );
+ while( nCount-- && (aEnd != maCells.end()) )
+ ++aEnd;
+ maCells.erase( aBegin, aEnd );
+ }
+ else
+ {
+ maCells.erase( aBegin );
+ }
+ }
+ else
+ {
+ maCells.resize( nIndex );
+ }
+}
+
+const TableModelRef& TableRow::getModel() const
+{
+ return mxTableModel;
+}
+
+// XCellRange
+
+
+Reference< XCell > SAL_CALL TableRow::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow )
+{
+ throwIfDisposed();
+ if( nRow != 0 )
+ throw IndexOutOfBoundsException();
+
+ return mxTableModel->getCellByPosition( nColumn, mnRow );
+}
+
+
+Reference< XCellRange > SAL_CALL TableRow::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
+{
+ throwIfDisposed();
+ if( (nLeft >= 0 ) && (nTop == 0) && (nRight >= nLeft) && (nBottom == 0) )
+ {
+ return mxTableModel->getCellRangeByPosition( nLeft, mnRow, nRight, mnRow );
+ }
+ throw IndexOutOfBoundsException();
+}
+
+
+Reference< XCellRange > SAL_CALL TableRow::getCellRangeByName( const OUString& /*aRange*/ )
+{
+ throwIfDisposed();
+ return Reference< XCellRange >();
+}
+
+
+// XNamed
+
+
+OUString SAL_CALL TableRow::getName()
+{
+ return maName;
+}
+
+
+void SAL_CALL TableRow::setName( const OUString& aName )
+{
+ maName = aName;
+}
+
+
+// XFastPropertySet
+
+
+void SAL_CALL TableRow::setFastPropertyValue( sal_Int32 nHandle, const Any& aValue )
+{
+ if(!mxTableModel.is() || nullptr == mxTableModel->getSdrTableObj())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableModel->getSdrTableObj());
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ bool bOk(false);
+ bool bChange(false);
+ std::unique_ptr<TableRowUndo> pUndo;
+ const bool bUndo(rTableObj.IsInserted() && rModel.IsUndoEnabled());
+
+ if( bUndo )
+ {
+ TableRowRef xThis( this );
+ pUndo.reset(new TableRowUndo( xThis ));
+ }
+
+ switch( nHandle )
+ {
+ case Property_Height:
+ {
+ sal_Int32 nHeight = mnHeight;
+ bOk = aValue >>= nHeight;
+ if( bOk && (mnHeight != nHeight) )
+ {
+ mnHeight = nHeight;
+ mbOptimalHeight = mnHeight == 0;
+ bChange = true;
+ }
+ break;
+ }
+
+ case Property_OptimalHeight:
+ {
+ bool bOptimalHeight = mbOptimalHeight;
+ bOk = aValue >>= bOptimalHeight;
+ if( bOk && (mbOptimalHeight != bOptimalHeight) )
+ {
+ mbOptimalHeight = bOptimalHeight;
+ if( bOptimalHeight )
+ mnHeight = 0;
+ bChange = true;
+ }
+ break;
+ }
+ case Property_IsVisible:
+ {
+ bool bIsVisible = mbIsVisible;
+ bOk = aValue >>= bIsVisible;
+ if( bOk && (mbIsVisible != bIsVisible) )
+ {
+ mbIsVisible = bIsVisible;
+ bChange = true;
+ }
+ break;
+ }
+
+ case Property_IsStartOfNewPage:
+ {
+ bool bIsStartOfNewPage = mbIsStartOfNewPage;
+ bOk = aValue >>= bIsStartOfNewPage;
+ if( bOk && (mbIsStartOfNewPage != bIsStartOfNewPage) )
+ {
+ mbIsStartOfNewPage = bIsStartOfNewPage;
+ bChange = true;
+ }
+ break;
+ }
+ default:
+ throw UnknownPropertyException( OUString::number(nHandle), static_cast<cppu::OWeakObject*>(this));
+ }
+
+ if( !bOk )
+ {
+ throw IllegalArgumentException();
+ }
+
+ if( bChange )
+ {
+ if( pUndo )
+ {
+ rModel.AddUndo( std::move(pUndo) );
+ }
+ mxTableModel->setModified(true);
+ }
+}
+
+
+Any SAL_CALL TableRow::getFastPropertyValue( sal_Int32 nHandle )
+{
+ switch( nHandle )
+ {
+ case Property_Height: return Any( mnHeight );
+ case Property_OptimalHeight: return Any( mbOptimalHeight );
+ case Property_IsVisible: return Any( mbIsVisible );
+ case Property_IsStartOfNewPage: return Any( mbIsStartOfNewPage );
+ default: throw UnknownPropertyException( OUString::number(nHandle), static_cast<cppu::OWeakObject*>(this));
+ }
+}
+
+
+rtl::Reference< FastPropertySetInfo > TableRow::getStaticPropertySetInfo()
+{
+ static rtl::Reference<FastPropertySetInfo> xInfo = []() {
+ PropertyVector aProperties(6);
+
+ aProperties[0].Name = "Height";
+ aProperties[0].Handle = Property_Height;
+ aProperties[0].Type = ::cppu::UnoType<sal_Int32>::get();
+ aProperties[0].Attributes = 0;
+
+ aProperties[1].Name = "OptimalHeight";
+ aProperties[1].Handle = Property_OptimalHeight;
+ aProperties[1].Type = cppu::UnoType<bool>::get();
+ aProperties[1].Attributes = 0;
+
+ aProperties[2].Name = "IsVisible";
+ aProperties[2].Handle = Property_IsVisible;
+ aProperties[2].Type = cppu::UnoType<bool>::get();
+ aProperties[2].Attributes = 0;
+
+ aProperties[3].Name = "IsStartOfNewPage";
+ aProperties[3].Handle = Property_IsStartOfNewPage;
+ aProperties[3].Type = cppu::UnoType<bool>::get();
+ aProperties[3].Attributes = 0;
+
+ aProperties[4].Name = "Size";
+ aProperties[4].Handle = Property_Height;
+ aProperties[4].Type = ::cppu::UnoType<sal_Int32>::get();
+ aProperties[4].Attributes = 0;
+
+ aProperties[5].Name = "OptimalSize";
+ aProperties[5].Handle = Property_OptimalHeight;
+ aProperties[5].Type = cppu::UnoType<bool>::get();
+ aProperties[5].Attributes = 0;
+
+ return rtl::Reference<FastPropertySetInfo>(new FastPropertySetInfo(aProperties));
+ }();
+
+ return xInfo;
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablerow.hxx b/svx/source/table/tablerow.hxx
new file mode 100644
index 000000000..9548e317f
--- /dev/null
+++ b/svx/source/table/tablerow.hxx
@@ -0,0 +1,84 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_TABLE_TABLEROW_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_TABLEROW_HXX
+
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include "propertyset.hxx"
+#include <celltypes.hxx>
+
+
+namespace sdr::table {
+
+typedef ::cppu::ImplInheritanceHelper< FastPropertySet, css::table::XCellRange, css::container::XNamed > TableRowBase;
+
+class TableRow : public TableRowBase
+{
+ friend class TableModel;
+ friend class TableRowUndo;
+public:
+ TableRow( const TableModelRef& xTableModel, sal_Int32 nRow, sal_Int32 nColumns );
+ virtual ~TableRow() override;
+
+ void dispose();
+ /// @throws css::uno::RuntimeException
+ void throwIfDisposed() const;
+
+ TableRow& operator=( const TableRow& );
+
+ void insertColumns( sal_Int32 nIndex, sal_Int32 nCount, CellVector::iterator const * pIter );
+ void removeColumns( sal_Int32 nIndex, sal_Int32 nCount );
+ /// Reference to the table model containing this row.
+ const TableModelRef& getModel() const;
+
+ // XCellRange
+ virtual css::uno::Reference< css::table::XCell > SAL_CALL getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow ) override;
+ virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) override;
+ virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByName( const OUString& aRange ) override;
+
+ // XNamed
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName( const OUString& aName ) override;
+
+ // XFastPropertySet
+ virtual void SAL_CALL setFastPropertyValue( ::sal_Int32 nHandle, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getFastPropertyValue( ::sal_Int32 nHandle ) override;
+
+private:
+ static rtl::Reference< FastPropertySetInfo > getStaticPropertySetInfo();
+
+ TableModelRef mxTableModel;
+ CellVector maCells;
+ sal_Int32 mnRow;
+ sal_Int32 mnHeight;
+ bool mbOptimalHeight;
+ bool mbIsVisible;
+ bool mbIsStartOfNewPage;
+ OUString maName;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablerows.cxx b/svx/source/table/tablerows.cxx
new file mode 100644
index 000000000..8698f72f4
--- /dev/null
+++ b/svx/source/table/tablerows.cxx
@@ -0,0 +1,114 @@
+/* -*- 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 <com/sun/star/lang/DisposedException.hpp>
+
+#include <tablemodel.hxx>
+#include "tablerow.hxx"
+#include "tablerows.hxx"
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::table;
+
+
+namespace sdr::table {
+
+TableRows::TableRows( const TableModelRef& xTableModel )
+: mxTableModel( xTableModel )
+{
+}
+
+
+TableRows::~TableRows()
+{
+ dispose();
+}
+
+
+void TableRows::dispose()
+{
+ mxTableModel.clear();
+}
+
+
+void TableRows::throwIfDisposed() const
+{
+ if( !mxTableModel.is() )
+ throw DisposedException();
+}
+
+
+// XTableRows
+
+
+void SAL_CALL TableRows::insertByIndex( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ throwIfDisposed();
+ mxTableModel->insertRows( nIndex, nCount );
+}
+
+
+void SAL_CALL TableRows::removeByIndex( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ throwIfDisposed();
+ mxTableModel->removeRows( nIndex, nCount );
+}
+
+
+// XIndexAccess
+
+
+sal_Int32 SAL_CALL TableRows::getCount()
+{
+ throwIfDisposed();
+ return mxTableModel->getRowCount();
+}
+
+
+Any SAL_CALL TableRows::getByIndex( sal_Int32 Index )
+{
+ throwIfDisposed();
+ return Any( Reference< XCellRange >( static_cast< XCellRange* >( mxTableModel->getRow( Index ).get() ) ) );
+}
+
+
+// XElementAccess
+
+
+Type SAL_CALL TableRows::getElementType()
+{
+ throwIfDisposed();
+ return cppu::UnoType<XCellRange>::get();
+}
+
+
+sal_Bool SAL_CALL TableRows::hasElements()
+{
+ throwIfDisposed();
+ return mxTableModel->getRowCount() != 0;
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablerows.hxx b/svx/source/table/tablerows.hxx
new file mode 100644
index 000000000..3b25d3d9e
--- /dev/null
+++ b/svx/source/table/tablerows.hxx
@@ -0,0 +1,59 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_TABLE_TABLEROWS_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_TABLEROWS_HXX
+
+#include <com/sun/star/table/XTableRows.hpp>
+#include <cppuhelper/implbase1.hxx>
+
+#include <celltypes.hxx>
+
+namespace sdr::table
+{
+class TableRows : public ::cppu::WeakAggImplHelper1<css::table::XTableRows>
+{
+public:
+ explicit TableRows(const TableModelRef& xTableModel);
+ virtual ~TableRows() override;
+
+ void dispose();
+ /// @throws css::uno::RuntimeException
+ void throwIfDisposed() const;
+
+ // XTableRows
+ virtual void SAL_CALL insertByIndex(sal_Int32 nIndex, sal_Int32 nCount) override;
+ virtual void SAL_CALL removeByIndex(sal_Int32 nIndex, sal_Int32 nCount) override;
+
+ // XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override;
+ virtual css::uno::Any SAL_CALL getByIndex(sal_Int32 Index) override;
+
+ // Methods
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+private:
+ TableModelRef mxTableModel;
+};
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablertfexporter.cxx b/svx/source/table/tablertfexporter.cxx
new file mode 100644
index 000000000..c298d5d1a
--- /dev/null
+++ b/svx/source/table/tablertfexporter.cxx
@@ -0,0 +1,235 @@
+/* -*- 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 <vector>
+
+#include <com/sun/star/table/XTable.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <tools/diagnose_ex.h>
+#include <tools/stream.hxx>
+#include <svtools/rtfkeywd.hxx>
+#include <svtools/rtfout.hxx>
+
+#include <editeng/eeitem.hxx>
+#include <svx/sdtaitm.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+
+#include <cell.hxx>
+#include <svx/svdotable.hxx>
+#include <svx/svdoutl.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/outlobj.hxx>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+
+namespace sdr::table {
+
+class SdrTableRtfExporter
+{
+public:
+ SdrTableRtfExporter( SvStream& rStrmP, SdrTableObj& rObj );
+ void Write();
+ void WriteRow( const Reference< XPropertySet >& xRowSet, sal_Int32 nRow, const std::vector< sal_Int32 >& aColumnStart );
+ void WriteCell( sal_Int32 nCol, sal_Int32 nRow );
+
+private:
+ SvStream& mrStrm;
+ SdrTableObj& mrObj;
+ Reference< XTable > mxTable;
+};
+
+void ExportAsRTF( SvStream& rStrm, SdrTableObj& rObj )
+{
+ SdrTableRtfExporter aEx( rStrm, rObj );
+ aEx.Write();
+}
+
+constexpr OUStringLiteral gsSize( u"Size" );
+
+SdrTableRtfExporter::SdrTableRtfExporter( SvStream& rStrm, SdrTableObj& rObj )
+: mrStrm( rStrm )
+, mrObj( rObj )
+, mxTable( rObj.getTable() )
+{
+}
+
+void SdrTableRtfExporter::Write()
+{
+ mrStrm.WriteChar( '{' ).WriteCharPtr( OOO_STRING_SVTOOLS_RTF_RTF );
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_ANSI ).WriteCharPtr( SAL_NEWLINE_STRING );
+
+ Reference< XTableColumns > xColumns( mxTable->getColumns() );
+ const sal_Int32 nColCount = xColumns->getCount();
+
+ std::vector< sal_Int32 > aColumnStart;
+ aColumnStart.reserve( nColCount );
+
+ // determine right offset of cells
+ sal_Int32 nPos = 0;
+ for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ ) try
+ {
+ Reference< XPropertySet > xSet( xColumns->getByIndex(nCol), UNO_QUERY_THROW );
+ sal_Int32 nWidth = 0;
+ xSet->getPropertyValue( gsSize ) >>= nWidth;
+ nPos += o3tl::toTwips(nWidth, o3tl::Length::mm100);
+ aColumnStart.push_back( nPos );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+
+ // export rows
+ Reference< XTableRows > xRows( mxTable->getRows() );
+ const sal_Int32 nRowCount = xRows->getCount();
+
+ for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ ) try
+ {
+ Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW );
+ WriteRow( xRowSet, nRow, aColumnStart );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+
+ mrStrm.WriteChar( '}' ).WriteCharPtr( SAL_NEWLINE_STRING );
+}
+
+void SdrTableRtfExporter::WriteRow( const Reference< XPropertySet >& xRowSet, sal_Int32 nRow, const std::vector< sal_Int32 >& aColumnStart )
+{
+ sal_Int32 nRowHeight = 0;
+ xRowSet->getPropertyValue( gsSize ) >>= nRowHeight;
+
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_TROWD ).WriteCharPtr( OOO_STRING_SVTOOLS_RTF_TRGAPH ).WriteCharPtr( "30" ).WriteCharPtr( OOO_STRING_SVTOOLS_RTF_TRLEFT ).WriteCharPtr( "-30" );
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_TRRH ).WriteOString( OString::number(nRowHeight) );
+
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+ for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+
+ if( !xCell.is() )
+ continue;
+
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_CELLX ).WriteOString( OString::number(aColumnStart[nCol]) );
+ if ( (nCol & 0x0F) == 0x0F )
+ mrStrm.WriteCharPtr( SAL_NEWLINE_STRING ); // prevent long lines
+ }
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_PARD ).WriteCharPtr( OOO_STRING_SVTOOLS_RTF_PLAIN ).WriteCharPtr( OOO_STRING_SVTOOLS_RTF_INTBL ).WriteCharPtr( SAL_NEWLINE_STRING );
+
+ sal_uInt64 nStrmPos = mrStrm.Tell();
+ for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
+ {
+ WriteCell( nCol, nRow );
+ if ( mrStrm.Tell() - nStrmPos > 255 )
+ {
+ mrStrm.WriteCharPtr( SAL_NEWLINE_STRING );
+ nStrmPos = mrStrm.Tell();
+ }
+ }
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_ROW ).WriteCharPtr( SAL_NEWLINE_STRING );
+}
+
+
+void SdrTableRtfExporter::WriteCell( sal_Int32 nCol, sal_Int32 nRow )
+{
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+
+ if( !xCell.is() || xCell->isMerged() )
+ {
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_CELL );
+ return ;
+ }
+
+ OUString aContent;
+
+ std::optional<OutlinerParaObject> pParaObj = xCell->CreateEditOutlinerParaObject();
+
+ if( !pParaObj && xCell->GetOutlinerParaObject() )
+ pParaObj = *xCell->GetOutlinerParaObject();
+
+ if(pParaObj)
+ {
+ // handle outliner attributes
+ SdrOutliner& rOutliner = mrObj.ImpGetDrawOutliner();
+ rOutliner.SetText(*pParaObj);
+
+ aContent = rOutliner.GetEditEngine().GetText();
+
+ rOutliner.Clear();
+ }
+
+ bool bResetAttr = false;
+
+ SdrTextHorzAdjust eHAdj = xCell->GetTextHorizontalAdjust();
+
+ const SfxItemSet& rCellSet = xCell->GetItemSet();
+
+ const SvxWeightItem& rWeightItem = rCellSet.Get( EE_CHAR_WEIGHT );
+ const SvxPostureItem& rPostureItem = rCellSet.Get( EE_CHAR_ITALIC );
+ const SvxUnderlineItem& rUnderlineItem = rCellSet.Get( EE_CHAR_UNDERLINE );
+
+ const char* pChar;
+
+ switch( eHAdj )
+ {
+ case SDRTEXTHORZADJUST_CENTER: pChar = OOO_STRING_SVTOOLS_RTF_QC; break;
+ case SDRTEXTHORZADJUST_BLOCK: pChar = OOO_STRING_SVTOOLS_RTF_QJ; break;
+ case SDRTEXTHORZADJUST_RIGHT: pChar = OOO_STRING_SVTOOLS_RTF_QR; break;
+ case SDRTEXTHORZADJUST_LEFT:
+ default: pChar = OOO_STRING_SVTOOLS_RTF_QL; break;
+ }
+ mrStrm.WriteCharPtr( pChar );
+
+ if ( rWeightItem.GetWeight() >= WEIGHT_BOLD )
+ { // bold
+ bResetAttr = true;
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_B );
+ }
+ if ( rPostureItem.GetPosture() != ITALIC_NONE )
+ { // italic
+ bResetAttr = true;
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_I );
+ }
+ if ( rUnderlineItem.GetLineStyle() != LINESTYLE_NONE )
+ { // underline
+ bResetAttr = true;
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_UL );
+ }
+
+ mrStrm.WriteChar( ' ' );
+ RTFOutFuncs::Out_String( mrStrm, aContent );
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_CELL );
+
+ if ( bResetAttr )
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_PLAIN );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablertfimporter.cxx b/svx/source/table/tablertfimporter.cxx
new file mode 100644
index 000000000..8f6e1c649
--- /dev/null
+++ b/svx/source/table/tablertfimporter.cxx
@@ -0,0 +1,503 @@
+/* -*- 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 <vector>
+
+#include <com/sun/star/table/XTable.hpp>
+#include <com/sun/star/table/XMergeableCellRange.hpp>
+
+#include <tools/stream.hxx>
+#include <tools/UnitConversion.hxx>
+#include <svtools/rtftoken.h>
+
+#include <svx/svdetc.hxx>
+#include <editeng/outlobj.hxx>
+
+#include <cell.hxx>
+#include <svx/svdotable.hxx>
+#include <svx/svdoutl.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editdata.hxx>
+#include <svx/svdmodel.hxx>
+#include <editeng/editids.hrc>
+#include <editeng/svxrtf.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+
+namespace sdr::table {
+
+namespace {
+
+struct RTFCellDefault
+{
+ SfxItemSet maItemSet;
+ sal_Int32 mnRowSpan;
+ sal_Int32 mnColSpan; // MergeCell if >1, merged cells if 0
+ sal_Int32 mnCellX;
+
+ explicit RTFCellDefault( SfxItemPool* pPool ) : maItemSet( *pPool ), mnRowSpan(1), mnColSpan(1), mnCellX(0) {}
+};
+
+}
+
+typedef std::vector< std::shared_ptr< RTFCellDefault > > RTFCellDefaultVector;
+
+namespace {
+
+struct RTFCellInfo
+{
+ SfxItemSet maItemSet;
+ sal_Int32 mnStartPara;
+ sal_Int32 mnParaCount;
+ sal_Int32 mnCellX;
+ sal_Int32 mnRowSpan;
+ std::shared_ptr< RTFCellInfo > mxVMergeCell;
+
+ explicit RTFCellInfo( SfxItemPool& rPool ) : maItemSet( rPool ), mnStartPara(0), mnParaCount(0), mnCellX(0), mnRowSpan(1) {}
+};
+
+}
+
+typedef std::shared_ptr< RTFCellInfo > RTFCellInfoPtr;
+typedef std::vector< RTFCellInfoPtr > RTFColumnVector;
+
+typedef std::shared_ptr< RTFColumnVector > RTFColumnVectorPtr;
+
+class SdrTableRTFParser
+{
+public:
+ explicit SdrTableRTFParser( SdrTableObj& rTableObj );
+
+ void Read( SvStream& rStream );
+
+ void ProcToken( RtfImportInfo* pInfo );
+
+ void NextRow();
+ void NextColumn();
+ void NewCellRow();
+
+ void InsertCell( RtfImportInfo const * pInfo );
+ void InsertColumnEdge( sal_Int32 nEdge );
+
+ void FillTable();
+
+ DECL_LINK( RTFImportHdl, RtfImportInfo&, void );
+
+private:
+ SdrTableObj& mrTableObj;
+ std::unique_ptr<SdrOutliner> mpOutliner;
+ SfxItemPool& mrItemPool;
+
+ RTFCellDefaultVector maDefaultList;
+ RTFCellDefaultVector::iterator maDefaultIterator;
+
+ int mnLastToken;
+ bool mbNewDef;
+
+ sal_Int32 mnStartPara;
+
+ sal_Int32 mnRowCnt;
+ sal_Int32 mnLastEdge;
+ sal_Int32 mnVMergeIdx;
+
+ std::vector< sal_Int32 > maColumnEdges;
+ std::vector< sal_Int32 >::iterator maLastEdge;
+ std::vector< RTFColumnVectorPtr > maRows;
+
+ std::unique_ptr<RTFCellDefault> mpInsDefault;
+ RTFCellDefault* mpActDefault;
+ RTFCellDefault* mpDefMerge;
+
+ Reference< XTable > mxTable;
+
+ RTFColumnVectorPtr mxLastRow;
+ // Copy assignment is forbidden and not implemented.
+ SdrTableRTFParser (const SdrTableRTFParser &) = delete;
+ SdrTableRTFParser & operator= (const SdrTableRTFParser &) = delete;
+};
+
+SdrTableRTFParser::SdrTableRTFParser( SdrTableObj& rTableObj )
+: mrTableObj( rTableObj )
+, mpOutliner( SdrMakeOutliner( OutlinerMode::TextObject, rTableObj.getSdrModelFromSdrObject() ) )
+, mrItemPool( rTableObj.getSdrModelFromSdrObject().GetItemPool() )
+, mnLastToken( 0 )
+, mbNewDef( false )
+, mnStartPara( 0 )
+, mnRowCnt( 0 )
+, mnLastEdge( 0 )
+, mnVMergeIdx ( 0 )
+, mpActDefault( nullptr )
+, mpDefMerge( nullptr )
+, mxTable( rTableObj.getTable() )
+{
+ mpOutliner->SetUpdateLayout(true);
+ mpOutliner->SetStyleSheet( 0, mrTableObj.GetStyleSheet() );
+ mpInsDefault.reset( new RTFCellDefault( &mrItemPool ) );
+}
+
+void SdrTableRTFParser::Read( SvStream& rStream )
+{
+ EditEngine& rEdit = const_cast< EditEngine& >( mpOutliner->GetEditEngine() );
+
+ Link<RtfImportInfo&,void> aOldLink( rEdit.GetRtfImportHdl() );
+ rEdit.SetRtfImportHdl( LINK( this, SdrTableRTFParser, RTFImportHdl ) );
+ mpOutliner->Read( rStream, OUString(), EETextFormat::Rtf );
+ rEdit.SetRtfImportHdl( aOldLink );
+
+ FillTable();
+}
+
+IMPL_LINK( SdrTableRTFParser, RTFImportHdl, RtfImportInfo&, rInfo, void )
+{
+ switch ( rInfo.eState )
+ {
+ case RtfImportState::NextToken:
+ ProcToken( &rInfo );
+ break;
+ case RtfImportState::UnknownAttr:
+ ProcToken( &rInfo );
+ break;
+ case RtfImportState::Start:
+ {
+ SvxRTFParser* pParser = static_cast<SvxRTFParser*>(rInfo.pParser);
+ pParser->SetAttrPool( &mrItemPool );
+ pParser->SetPardMap(SID_ATTR_BORDER_OUTER, SDRATTR_TABLE_BORDER);
+ }
+ break;
+ case RtfImportState::End:
+ if ( rInfo.aSelection.nEndPos )
+ {
+ mpActDefault = nullptr;
+ rInfo.nToken = RTF_PAR;
+ rInfo.aSelection.nEndPara++;
+ ProcToken( &rInfo );
+ }
+ break;
+ case RtfImportState::SetAttr:
+ case RtfImportState::InsertText:
+ case RtfImportState::InsertPara:
+ break;
+ default:
+ SAL_WARN( "svx.table","unknown ImportInfo.eState");
+ }
+}
+
+void SdrTableRTFParser::NextRow()
+{
+ mxLastRow = maRows.back();
+ mnVMergeIdx = 0;
+ ++mnRowCnt;
+}
+
+void SdrTableRTFParser::InsertCell( RtfImportInfo const * pInfo )
+{
+
+ RTFCellInfoPtr xCellInfo = std::make_shared<RTFCellInfo>(mrItemPool);
+
+ xCellInfo->mnStartPara = mnStartPara;
+ xCellInfo->mnParaCount = pInfo->aSelection.nEndPara - 1 - mnStartPara;
+ xCellInfo->mnCellX = mpActDefault->mnCellX;
+ xCellInfo->mnRowSpan = mpActDefault->mnRowSpan;
+
+
+ if ( mxLastRow != nullptr )
+ {
+ sal_Int32 nSize = mxLastRow->size();
+ while( mnVMergeIdx < nSize &&
+ (*mxLastRow)[mnVMergeIdx]->mnCellX < xCellInfo->mnCellX )
+ ++mnVMergeIdx;
+
+ if ( xCellInfo->mnRowSpan == 0 && mnVMergeIdx < nSize )
+ {
+ RTFCellInfoPtr xLastCell( (*mxLastRow)[mnVMergeIdx] );
+ if (xLastCell->mnRowSpan)
+ xCellInfo->mxVMergeCell = xLastCell;
+ else
+ xCellInfo->mxVMergeCell = xLastCell->mxVMergeCell;
+ }
+ }
+
+ if( !maRows.empty() )
+ {
+ RTFColumnVectorPtr xColumn( maRows.back() );
+ if ( xCellInfo->mxVMergeCell )
+ {
+ if ( xColumn->empty() ||
+ xColumn->back()->mxVMergeCell != xCellInfo->mxVMergeCell )
+ xCellInfo->mxVMergeCell->mnRowSpan++;
+ }
+
+ xColumn->push_back( xCellInfo );
+ }
+
+ mnStartPara = pInfo->aSelection.nEndPara - 1;
+}
+
+void SdrTableRTFParser::InsertColumnEdge( sal_Int32 nEdge )
+{
+ auto aNextEdge = std::lower_bound( maLastEdge, maColumnEdges.end(), nEdge );
+
+ if ( aNextEdge == maColumnEdges.end() || nEdge != *aNextEdge )
+ {
+ maLastEdge = maColumnEdges.insert( aNextEdge , nEdge );
+ mnLastEdge = nEdge;
+ }
+}
+
+void SdrTableRTFParser::FillTable()
+{
+ try
+ {
+ sal_Int32 nColCount = mxTable->getColumnCount();
+ Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_SET_THROW );
+ sal_Int32 nColMax = maColumnEdges.size();
+ if( nColCount < nColMax )
+ {
+ xCols->insertByIndex( nColCount, nColMax - nColCount );
+ nColCount = mxTable->getColumnCount();
+ }
+
+ static const OUStringLiteral sWidth(u"Width");
+ sal_Int32 nCol, nLastEdge = 0;
+ for( nCol = 0; nCol < nColCount; nCol++ )
+ {
+ Reference< XPropertySet > xSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
+ sal_Int32 nWidth = maColumnEdges[nCol] - nLastEdge;
+
+ xSet->setPropertyValue( sWidth, Any( nWidth ) );
+ nLastEdge += nWidth;
+ }
+
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+ if( nRowCount < mnRowCnt )
+ {
+ Reference< XTableRows > xRows( mxTable->getRows(), UNO_SET_THROW );
+ xRows->insertByIndex( nRowCount, mnRowCnt - nRowCount );
+ }
+
+ for( sal_Int32 nRow = 0; nRow < static_cast<sal_Int32>(maRows.size()); nRow++ )
+ {
+ RTFColumnVectorPtr xColumn( maRows[nRow] );
+ nCol = 0;
+ auto aEdge = maColumnEdges.begin();
+ for( sal_Int32 nIdx = 0; nCol < nColMax && nIdx < static_cast<sal_Int32>(xColumn->size()); nIdx++ )
+ {
+ RTFCellInfoPtr xCellInfo( (*xColumn)[nIdx] );
+
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() && xCellInfo )
+ {
+ const SfxPoolItem *pPoolItem = nullptr;
+ if( xCellInfo->maItemSet.GetItemState(SDRATTR_TABLE_BORDER,false,&pPoolItem)==SfxItemState::SET)
+ xCell->SetMergedItem( *pPoolItem );
+
+ std::optional<OutlinerParaObject> pTextObject(mpOutliner->CreateParaObject( xCellInfo->mnStartPara, xCellInfo->mnParaCount ));
+ if( pTextObject )
+ {
+ SdrOutliner& rOutliner=mrTableObj.ImpGetDrawOutliner();
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText( *pTextObject );
+ mrTableObj.NbcSetOutlinerParaObjectForText( rOutliner.CreateParaObject(), xCell.get() );
+ }
+
+ sal_Int32 nLastRow = nRow;
+ if ( xCellInfo->mnRowSpan )
+ nLastRow += xCellInfo->mnRowSpan - 1;
+
+ aEdge = std::lower_bound( aEdge, maColumnEdges.end(), xCellInfo->mnCellX );
+ sal_Int32 nLastCol = nCol;
+ if ( aEdge != maColumnEdges.end() )
+ {
+ nLastCol = std::distance( maColumnEdges.begin(), aEdge);
+ ++aEdge;
+ }
+
+ if ( nLastCol > nCol || nLastRow > nRow )
+ {
+ Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( nCol, nRow, nLastCol, nLastRow ) ), UNO_QUERY_THROW );
+ if( xRange->isMergeable() )
+ xRange->merge();
+ }
+ nCol = nLastCol + 1;
+ }
+ }
+ }
+
+ tools::Rectangle aRect( mrTableObj.GetSnapRect() );
+ aRect.SetRight( aRect.Left() + nLastEdge );
+ mrTableObj.NbcSetSnapRect( aRect );
+
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+}
+
+void SdrTableRTFParser::NewCellRow()
+{
+ if( mbNewDef )
+ {
+ mbNewDef = false;
+
+ maRows.push_back( std::make_shared<std::vector<std::shared_ptr<RTFCellInfo>>>( ) );
+ }
+ mpDefMerge = nullptr;
+ maDefaultIterator = maDefaultList.begin();
+
+ NextColumn();
+
+ DBG_ASSERT( mpActDefault, "NewCellRow: pActDefault==0" );
+}
+
+void SdrTableRTFParser::NextColumn()
+{
+ if( maDefaultIterator != maDefaultList.end() )
+ mpActDefault = (*maDefaultIterator++).get();
+ else
+ mpActDefault = nullptr;
+}
+
+void SdrTableRTFParser::ProcToken( RtfImportInfo* pInfo )
+{
+ switch ( pInfo->nToken )
+ {
+ case RTF_TROWD: // denotes table row default, before RTF_CELLX
+ {
+ maDefaultList.clear();
+ mpDefMerge = nullptr;
+ mnLastToken = pInfo->nToken;
+ maLastEdge = maColumnEdges.begin();
+ mnLastEdge = 0;
+ }
+ break;
+ case RTF_CLMGF: // The first cell of cells to be merged
+ {
+ mpDefMerge = mpInsDefault.get();
+ mnLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_CLMRG: // A cell to be merged with the preceding cell
+ {
+ if ( !mpDefMerge )
+ mpDefMerge = maDefaultList.back().get();
+ DBG_ASSERT( mpDefMerge, "RTF_CLMRG: pDefMerge==0" );
+ if( mpDefMerge )
+ mpDefMerge->mnColSpan++;
+ mpInsDefault->mnColSpan = 0;
+ mnLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_CLVMGF:
+ {
+ mnLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_CLVMRG:
+ {
+ mpInsDefault->mnRowSpan = 0;
+ mnLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_CELLX: // closes cell default
+ {
+ mbNewDef = true;
+ std::shared_ptr<RTFCellDefault> pDefault( mpInsDefault.release() );
+ maDefaultList.push_back( pDefault );
+
+
+ const sal_Int32 nSize = convertTwipToMm100(pInfo->nTokenValue);
+ if ( nSize > mnLastEdge )
+ InsertColumnEdge( nSize );
+
+ pDefault->mnCellX = nSize;
+ // Record cellx in the first merged cell.
+ if ( mpDefMerge && pDefault->mnColSpan == 0 )
+ mpDefMerge->mnCellX = nSize;
+
+ mpInsDefault.reset( new RTFCellDefault( &mrItemPool ) );
+
+ mnLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_INTBL: // before the first RTF_CELL
+ {
+ if ( mnLastToken != RTF_INTBL && mnLastToken != RTF_CELL && mnLastToken != RTF_PAR )
+ {
+ NewCellRow();
+ mnLastToken = pInfo->nToken;
+ }
+ }
+ break;
+ case RTF_CELL: // denotes the end of a cell.
+ {
+ DBG_ASSERT( mpActDefault, "RTF_CELL: pActDefault==0" );
+ if ( mbNewDef || !mpActDefault )
+ NewCellRow();
+ if ( !mpActDefault )
+ mpActDefault = mpInsDefault.get();
+ if ( mpActDefault->mnColSpan > 0 )
+ {
+ InsertCell(pInfo);
+ }
+ NextColumn();
+ mnLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_ROW: // means the end of a row
+ {
+ NextRow();
+ mnLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_PAR: // Paragraph
+ mnLastToken = pInfo->nToken;
+ break;
+ default:
+ { // do not set nLastToken
+ switch ( pInfo->nToken & ~(0xff | RTF_TABLEDEF) )
+ {
+ case RTF_SHADINGDEF:
+// ((SvxRTFParser*)pInfo->pParser)->ReadBackgroundAttr(pInfo->nToken, mpInsDefault->maItemSet, sal_True );
+ break;
+ case RTF_BRDRDEF:
+ static_cast<SvxRTFParser*>(pInfo->pParser)->ReadBorderAttr(pInfo->nToken, mpInsDefault->maItemSet, true );
+ break;
+ }
+ }
+ }
+}
+
+void ImportAsRTF( SvStream& rStream, SdrTableObj& rObj )
+{
+ SdrTableRTFParser aParser( rObj );
+ aParser.Read( rStream );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tableundo.cxx b/svx/source/table/tableundo.cxx
new file mode 100644
index 000000000..66641469d
--- /dev/null
+++ b/svx/source/table/tableundo.cxx
@@ -0,0 +1,516 @@
+/* -*- 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 <sdr/properties/textproperties.hxx>
+#include <editeng/outlobj.hxx>
+
+#include <cell.hxx>
+#include "tableundo.hxx"
+#include <svx/svdotable.hxx>
+#include "tablerow.hxx"
+#include "tablecolumn.hxx"
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::table;
+
+
+namespace sdr::table {
+
+CellUndo::CellUndo( SdrObject* pObjRef, const CellRef& xCell )
+: SdrUndoAction(xCell->GetObject().getSdrModelFromSdrObject())
+ ,mxObjRef( pObjRef )
+ ,mxCell( xCell )
+ ,mbUndo( true )
+{
+ if( mxCell.is() && mxObjRef.is() )
+ {
+ getDataFromCell( maUndoData );
+ mxObjRef->AddObjectUser( *this );
+ }
+}
+
+CellUndo::~CellUndo()
+{
+ if( mxObjRef.is() )
+ mxObjRef->RemoveObjectUser( *this );
+ dispose();
+}
+
+void CellUndo::dispose()
+{
+ mxCell.clear();
+ delete maUndoData.mpProperties;
+ maUndoData.mpProperties = nullptr;
+ delete maRedoData.mpProperties;
+ maRedoData.mpProperties = nullptr;
+ maUndoData.mpOutlinerParaObject.reset();
+ maRedoData.mpOutlinerParaObject.reset();
+}
+
+void CellUndo::ObjectInDestruction(const SdrObject& )
+{
+ dispose();
+}
+
+void CellUndo::Undo()
+{
+ if( mxCell.is() && mbUndo )
+ {
+ if( maRedoData.mpProperties == nullptr )
+ getDataFromCell( maRedoData );
+
+ setDataToCell( maUndoData );
+ mbUndo = false;
+ }
+}
+
+void CellUndo::Redo()
+{
+ if( mxCell.is() && !mbUndo )
+ {
+ setDataToCell( maRedoData );
+ mbUndo = true;
+ }
+}
+
+bool CellUndo::Merge( SfxUndoAction *pNextAction )
+{
+ CellUndo* pNext = dynamic_cast< CellUndo* >( pNextAction );
+ return pNext && pNext->mxCell.get() == mxCell.get();
+}
+
+void CellUndo::setDataToCell( const Data& rData )
+{
+ if( rData.mpProperties )
+ mxCell->mpProperties.reset(Cell::CloneProperties( rData.mpProperties, *mxObjRef, *mxCell ));
+ else
+ mxCell->mpProperties.reset();
+
+ if( rData.mpOutlinerParaObject )
+ mxCell->SetOutlinerParaObject( *rData.mpOutlinerParaObject );
+ else
+ mxCell->RemoveOutlinerParaObject();
+
+ mxCell->msFormula = rData.msFormula;
+ mxCell->mfValue = rData.mfValue;
+ mxCell->mnError = rData.mnError;
+ mxCell->mbMerged = rData.mbMerged;
+ mxCell->mnRowSpan = rData.mnRowSpan;
+ mxCell->mnColSpan = rData.mnColSpan;
+
+ if(mxObjRef.is())
+ {
+ // #i120201# ActionChanged is not enough, we need to trigger TableLayouter::UpdateBorderLayout()
+ // and this is done best using ReformatText() for table objects
+ mxObjRef->ActionChanged();
+ mxObjRef->NbcReformatText();
+ }
+}
+
+void CellUndo::getDataFromCell( Data& rData )
+{
+ if( !(mxObjRef.is() && mxCell.is()) )
+ return;
+
+ if( mxCell->mpProperties )
+ rData.mpProperties = mxCell->CloneProperties( *mxObjRef, *mxCell);
+
+ if( mxCell->GetOutlinerParaObject() )
+ rData.mpOutlinerParaObject = *mxCell->GetOutlinerParaObject();
+ else
+ rData.mpOutlinerParaObject.reset();
+
+ rData.msFormula = mxCell->msFormula;
+ rData.mfValue = mxCell->mfValue;
+ rData.mnError = mxCell->mnError;
+ rData.mbMerged = mxCell->mbMerged;
+ rData.mnRowSpan = mxCell->mnRowSpan;
+ rData.mnColSpan = mxCell->mnColSpan;
+}
+
+
+// class InsertRowUndo : public SdrUndoAction
+
+
+static void Dispose( RowVector& rRows )
+{
+ for( auto& rpRow : rRows )
+ rpRow->dispose();
+}
+
+
+InsertRowUndo::InsertRowUndo( const TableModelRef& xTable, sal_Int32 nIndex, RowVector& aNewRows )
+: SdrUndoAction(xTable->getSdrTableObj()->getSdrModelFromSdrObject())
+ ,mxTable( xTable )
+ ,mnIndex( nIndex )
+ ,mbUndo( true )
+{
+ maRows.swap( aNewRows );
+}
+
+
+InsertRowUndo::~InsertRowUndo()
+{
+ if( !mbUndo )
+ Dispose( maRows );
+}
+
+
+void InsertRowUndo::Undo()
+{
+ if( mxTable.is() )
+ {
+ mxTable->UndoInsertRows( mnIndex, sal::static_int_cast< sal_Int32 >( maRows.size() ) );
+ mbUndo = false;
+ }
+}
+
+
+void InsertRowUndo::Redo()
+{
+ if( mxTable.is() )
+ {
+ mxTable->UndoRemoveRows( mnIndex, maRows );
+ mbUndo = true;
+ }
+}
+
+
+// class RemoveRowUndo : public SdrUndoAction
+
+
+RemoveRowUndo::RemoveRowUndo( const TableModelRef& xTable, sal_Int32 nIndex, RowVector& aRemovedRows )
+: SdrUndoAction(xTable->getSdrTableObj()->getSdrModelFromSdrObject())
+ ,mxTable( xTable )
+ ,mnIndex( nIndex )
+ ,mbUndo( true )
+{
+ maRows.swap( aRemovedRows );
+}
+
+
+RemoveRowUndo::~RemoveRowUndo()
+{
+ if( mbUndo )
+ Dispose( maRows );
+}
+
+
+void RemoveRowUndo::Undo()
+{
+ if( mxTable.is() )
+ {
+ mxTable->UndoRemoveRows( mnIndex, maRows );
+ mbUndo = false;
+ }
+}
+
+
+void RemoveRowUndo::Redo()
+{
+ if( mxTable.is() )
+ {
+ mxTable->UndoInsertRows( mnIndex, sal::static_int_cast< sal_Int32 >( maRows.size() ) );
+ mbUndo = true;
+ }
+}
+
+
+// class InsertColUndo : public SdrUndoAction
+
+
+static void Dispose( ColumnVector& rCols )
+{
+ for( auto& rpCol : rCols )
+ rpCol->dispose();
+}
+
+
+static void Dispose( CellVector& rCells )
+{
+ for( auto& rpCell : rCells )
+ rpCell->dispose();
+}
+
+
+InsertColUndo::InsertColUndo( const TableModelRef& xTable, sal_Int32 nIndex, ColumnVector& aNewCols, CellVector& aCells )
+: SdrUndoAction(xTable->getSdrTableObj()->getSdrModelFromSdrObject())
+ ,mxTable( xTable )
+ ,mnIndex( nIndex )
+ ,mbUndo( true )
+{
+ maColumns.swap( aNewCols );
+ maCells.swap( aCells );
+}
+
+
+InsertColUndo::~InsertColUndo()
+{
+ if( !mbUndo )
+ {
+ Dispose( maColumns );
+ Dispose( maCells );
+ }
+}
+
+
+void InsertColUndo::Undo()
+{
+ if( mxTable.is() )
+ {
+ mxTable->UndoInsertColumns( mnIndex, sal::static_int_cast< sal_Int32 >( maColumns.size() ) );
+ mbUndo = false;
+ }
+}
+
+
+void InsertColUndo::Redo()
+{
+ if( mxTable.is() )
+ {
+ mxTable->UndoRemoveColumns( mnIndex, maColumns, maCells );
+ mbUndo = true;
+ }
+}
+
+
+// class RemoveColUndo : public SdrUndoAction
+
+
+RemoveColUndo::RemoveColUndo( const TableModelRef& xTable, sal_Int32 nIndex, ColumnVector& aNewCols, CellVector& aCells )
+: SdrUndoAction(xTable->getSdrTableObj()->getSdrModelFromSdrObject())
+ ,mxTable( xTable )
+ ,mnIndex( nIndex )
+ ,mbUndo( true )
+{
+ maColumns.swap( aNewCols );
+ maCells.swap( aCells );
+}
+
+
+RemoveColUndo::~RemoveColUndo()
+{
+ if( mbUndo )
+ {
+ Dispose( maColumns );
+ Dispose( maCells );
+ }
+}
+
+
+void RemoveColUndo::Undo()
+{
+ if( mxTable.is() )
+ {
+ mxTable->UndoRemoveColumns( mnIndex, maColumns, maCells );
+ mbUndo = false;
+ }
+}
+
+
+void RemoveColUndo::Redo()
+{
+ if( mxTable.is() )
+ {
+ mxTable->UndoInsertColumns( mnIndex, sal::static_int_cast< sal_Int32 >( maColumns.size() ) );
+ mbUndo = true;
+ }
+}
+
+
+// class TableColumnUndo : public SdrUndoAction
+
+
+TableColumnUndo::TableColumnUndo( const TableColumnRef& xCol )
+: SdrUndoAction(xCol->mxTableModel->getSdrTableObj()->getSdrModelFromSdrObject())
+ ,mxCol( xCol )
+ ,mbHasRedoData( false )
+{
+ getData( maUndoData );
+}
+
+
+TableColumnUndo::~TableColumnUndo()
+{
+}
+
+
+void TableColumnUndo::Undo()
+{
+ if( !mbHasRedoData )
+ {
+ getData( maRedoData );
+ mbHasRedoData = true;
+ }
+ setData( maUndoData );
+}
+
+
+void TableColumnUndo::Redo()
+{
+ setData( maRedoData );
+}
+
+
+bool TableColumnUndo::Merge( SfxUndoAction *pNextAction )
+{
+ TableColumnUndo* pNext = dynamic_cast< TableColumnUndo* >( pNextAction );
+ return pNext && pNext->mxCol == mxCol;
+}
+
+
+void TableColumnUndo::setData( const Data& rData )
+{
+ mxCol->mnColumn = rData.mnColumn;
+ mxCol->mnWidth = rData.mnWidth;
+ mxCol->mbOptimalWidth = rData.mbOptimalWidth;
+ mxCol->mbIsVisible = rData.mbIsVisible;
+ mxCol->mbIsStartOfNewPage = rData.mbIsStartOfNewPage;
+ mxCol->maName = rData.maName;
+
+ // Trigger re-layout of the table.
+ mxCol->getModel()->setModified(true);
+}
+
+
+void TableColumnUndo::getData( Data& rData )
+{
+ rData.mnColumn = mxCol->mnColumn;
+ rData.mnWidth = mxCol->mnWidth;
+ rData.mbOptimalWidth = mxCol->mbOptimalWidth;
+ rData.mbIsVisible = mxCol->mbIsVisible;
+ rData.mbIsStartOfNewPage = mxCol->mbIsStartOfNewPage;
+ rData.maName = mxCol->maName;
+}
+
+
+// class TableRowUndo : public SdrUndoAction
+
+
+TableRowUndo::TableRowUndo( const TableRowRef& xRow )
+: SdrUndoAction(xRow->mxTableModel->getSdrTableObj()->getSdrModelFromSdrObject())
+ , mxRow( xRow )
+ , mbHasRedoData( false )
+{
+ getData( maUndoData );
+}
+
+
+TableRowUndo::~TableRowUndo()
+{
+}
+
+
+void TableRowUndo::Undo()
+{
+ if( !mbHasRedoData )
+ {
+ getData( maRedoData );
+ mbHasRedoData = true;
+ }
+ setData( maUndoData );
+}
+
+
+void TableRowUndo::Redo()
+{
+ setData( maRedoData );
+}
+
+
+bool TableRowUndo::Merge( SfxUndoAction *pNextAction )
+{
+ TableRowUndo* pNext = dynamic_cast< TableRowUndo* >( pNextAction );
+ return pNext && pNext->mxRow == mxRow;
+}
+
+
+void TableRowUndo::setData( const Data& rData )
+{
+ mxRow->mnRow = rData.mnRow;
+ mxRow->mnHeight = rData.mnHeight;
+ mxRow->mbOptimalHeight = rData.mbOptimalHeight;
+ mxRow->mbIsVisible = rData.mbIsVisible;
+ mxRow->mbIsStartOfNewPage = rData.mbIsStartOfNewPage;
+ mxRow->maName = rData.maName;
+
+ // Trigger re-layout of the table.
+ mxRow->getModel()->setModified(true);
+ }
+
+
+void TableRowUndo::getData( Data& rData )
+{
+ rData.mnRow = mxRow->mnRow;
+ rData.mnHeight = mxRow->mnHeight;
+ rData.mbOptimalHeight = mxRow->mbOptimalHeight;
+ rData.mbIsVisible = mxRow->mbIsVisible;
+ rData.mbIsStartOfNewPage = mxRow->mbIsStartOfNewPage;
+ rData.maName = mxRow->maName;
+}
+
+
+TableStyleUndo::TableStyleUndo( const SdrTableObj& rTableObj )
+: SdrUndoAction(rTableObj.getSdrModelFromSdrObject())
+ ,mxObjRef( const_cast< sdr::table::SdrTableObj*>( &rTableObj ) )
+ ,mbHasRedoData(false)
+{
+ getData( maUndoData );
+}
+
+void TableStyleUndo::Undo()
+{
+ if( !mbHasRedoData )
+ {
+ getData( maRedoData );
+ mbHasRedoData = true;
+ }
+ setData( maUndoData );
+}
+
+void TableStyleUndo::Redo()
+{
+ setData( maRedoData );
+}
+
+void TableStyleUndo::setData( const Data& rData )
+{
+ SdrTableObj* pTableObj = mxObjRef.get();
+ if( pTableObj )
+ {
+ pTableObj->setTableStyle( rData.mxTableStyle );
+ pTableObj->setTableStyleSettings( rData.maSettings );
+ }
+}
+
+void TableStyleUndo::getData( Data& rData )
+{
+ SdrTableObj* pTableObj = mxObjRef.get();
+ if( pTableObj )
+ {
+ rData.maSettings = pTableObj->getTableStyleSettings();
+ rData.mxTableStyle = pTableObj->getTableStyle();
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tableundo.hxx b/svx/source/table/tableundo.hxx
new file mode 100644
index 000000000..822f5ec64
--- /dev/null
+++ b/svx/source/table/tableundo.hxx
@@ -0,0 +1,258 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_TABLE_TABLEUNDO_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_TABLEUNDO_HXX
+
+#include <com/sun/star/container/XIndexAccess.hpp>
+
+#include <svx/svdotable.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/sdrobjectuser.hxx>
+
+#include <celltypes.hxx>
+
+namespace sdr::properties {
+ class TextProperties;
+}
+
+class OutlinerParaObject;
+
+
+namespace sdr::table {
+
+class CellUndo : public SdrUndoAction, public sdr::ObjectUser
+{
+public:
+ CellUndo( SdrObject* pObj, const CellRef& xCell );
+ virtual ~CellUndo() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual bool Merge( SfxUndoAction *pNextAction ) override;
+
+ void dispose();
+ virtual void ObjectInDestruction(const SdrObject& rObject) override;
+
+private:
+ struct Data
+ {
+ sdr::properties::TextProperties* mpProperties;
+ std::optional<OutlinerParaObject> mpOutlinerParaObject;
+
+ OUString msFormula;
+ double mfValue;
+ ::sal_Int32 mnError;
+ bool mbMerged;
+ ::sal_Int32 mnRowSpan;
+ ::sal_Int32 mnColSpan;
+
+ Data()
+ : mpProperties(nullptr)
+ , mfValue(0)
+ , mnError(0)
+ , mbMerged(false)
+ , mnRowSpan(0)
+ , mnColSpan(0)
+ {
+ }
+ };
+
+ void setDataToCell( const Data& rData );
+ void getDataFromCell( Data& rData );
+
+ tools::WeakReference<SdrObject> mxObjRef;
+ CellRef mxCell;
+ Data maUndoData;
+ Data maRedoData;
+ bool mbUndo;
+};
+
+
+class InsertRowUndo : public SdrUndoAction
+{
+public:
+ InsertRowUndo( const TableModelRef& xTable, sal_Int32 nIndex, RowVector& aNewRows );
+ virtual ~InsertRowUndo() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+private:
+ TableModelRef mxTable;
+ sal_Int32 mnIndex;
+ RowVector maRows;
+ bool mbUndo;
+};
+
+
+class RemoveRowUndo : public SdrUndoAction
+{
+public:
+ RemoveRowUndo( const TableModelRef& xTable, sal_Int32 nIndex, RowVector& aRemovedRows );
+ virtual ~RemoveRowUndo() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+private:
+ TableModelRef mxTable;
+ sal_Int32 mnIndex;
+ RowVector maRows;
+ bool mbUndo;
+};
+
+
+class InsertColUndo : public SdrUndoAction
+{
+public:
+ InsertColUndo( const TableModelRef& xTable, sal_Int32 nIndex, ColumnVector& aNewCols, CellVector& aCells );
+ virtual ~InsertColUndo() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+private:
+ TableModelRef mxTable;
+ sal_Int32 mnIndex;
+ ColumnVector maColumns;
+ CellVector maCells;
+ bool mbUndo;
+};
+
+
+class RemoveColUndo : public SdrUndoAction
+{
+public:
+ RemoveColUndo( const TableModelRef& xTable, sal_Int32 nIndex, ColumnVector& aNewCols, CellVector& aCells );
+ virtual ~RemoveColUndo() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+private:
+ TableModelRef mxTable;
+ sal_Int32 mnIndex;
+ ColumnVector maColumns;
+ CellVector maCells;
+ bool mbUndo;
+};
+
+
+class TableColumnUndo : public SdrUndoAction
+{
+public:
+ explicit TableColumnUndo( const TableColumnRef& xCol );
+ virtual ~TableColumnUndo() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual bool Merge( SfxUndoAction *pNextAction ) override;
+
+private:
+ struct Data
+ {
+ sal_Int32 mnColumn;
+ sal_Int32 mnWidth;
+ bool mbOptimalWidth;
+ bool mbIsVisible;
+ bool mbIsStartOfNewPage;
+ OUString maName;
+
+ Data()
+ : mnColumn(0)
+ , mnWidth(0)
+ , mbOptimalWidth(false)
+ , mbIsVisible(false)
+ , mbIsStartOfNewPage(false)
+ {
+ }
+ };
+
+ void setData( const Data& rData );
+ void getData( Data& rData );
+
+ TableColumnRef mxCol;
+ Data maUndoData;
+ Data maRedoData;
+ bool mbHasRedoData;
+};
+
+
+class TableRowUndo : public SdrUndoAction
+{
+public:
+ explicit TableRowUndo( const TableRowRef& xRow );
+ virtual ~TableRowUndo() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual bool Merge( SfxUndoAction *pNextAction ) override;
+
+private:
+ struct Data
+ {
+ sal_Int32 mnRow;
+ sal_Int32 mnHeight;
+ bool mbOptimalHeight;
+ bool mbIsVisible;
+ bool mbIsStartOfNewPage;
+ OUString maName;
+ };
+
+ void setData( const Data& rData );
+ void getData( Data& rData );
+
+ TableRowRef mxRow;
+ Data maUndoData;
+ Data maRedoData;
+ bool mbHasRedoData;
+};
+
+class TableStyleUndo : public SdrUndoAction
+{
+public:
+ explicit TableStyleUndo( const SdrTableObj& rTableObj );
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+private:
+ tools::WeakReference<SdrTableObj> mxObjRef;
+
+ struct Data
+ {
+ TableStyleSettings maSettings;
+ css::uno::Reference< css::container::XIndexAccess > mxTableStyle;
+ };
+
+ void setData( const Data& rData );
+ void getData( Data& rData );
+
+ Data maUndoData;
+ Data maRedoData;
+ bool mbHasRedoData;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/viewcontactoftableobj.cxx b/svx/source/table/viewcontactoftableobj.cxx
new file mode 100644
index 000000000..c550c5ec2
--- /dev/null
+++ b/svx/source/table/viewcontactoftableobj.cxx
@@ -0,0 +1,547 @@
+/* -*- 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 "viewcontactoftableobj.hxx"
+#include <svx/svdotable.hxx>
+#include <com/sun/star/table/XTable.hpp>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <sdr/primitive2d/sdrdecompositiontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <sdr/attribute/sdrtextattribute.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <editeng/borderline.hxx>
+#include <sdr/attribute/sdrfilltextattribute.hxx>
+#include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
+#include <drawinglayer/primitive2d/structuretagprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <svx/sdr/contact/viewobjectcontactofsdrobj.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/framelink.hxx>
+#include <svx/framelinkarray.hxx>
+#include <svx/sdooitm.hxx>
+#include <vcl/canvastools.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <svx/xfltrit.hxx>
+
+#include <cell.hxx>
+#include "tablelayouter.hxx"
+
+
+using editeng::SvxBorderLine;
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ namespace {
+
+ class SdrCellPrimitive2D : public BufferedDecompositionPrimitive2D
+ {
+ private:
+ basegfx::B2DHomMatrix maTransform;
+ attribute::SdrFillTextAttribute maSdrFTAttribute;
+
+ protected:
+ // local decomposition.
+ virtual void create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const override;
+
+ public:
+ SdrCellPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransform,
+ const attribute::SdrFillTextAttribute& rSdrFTAttribute)
+ : maTransform(rTransform),
+ maSdrFTAttribute(rSdrFTAttribute)
+ {
+ }
+
+ // data access
+ const basegfx::B2DHomMatrix& getTransform() const { return maTransform; }
+ const attribute::SdrFillTextAttribute& getSdrFTAttribute() const { return maSdrFTAttribute; }
+
+ // compare operator
+ virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
+
+ // provide unique ID
+ virtual sal_uInt32 getPrimitive2DID() const override;
+ };
+
+ }
+
+ void SdrCellPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const
+ {
+ // prepare unit polygon
+ const basegfx::B2DPolyPolygon aUnitPolyPolygon(basegfx::utils::createUnitPolygon());
+
+ // add fill
+ if(!getSdrFTAttribute().getFill().isDefault())
+ {
+ basegfx::B2DPolyPolygon aTransformed(aUnitPolyPolygon);
+
+ aTransformed.transform(getTransform());
+ rContainer.push_back(
+ createPolyPolygonFillPrimitive(
+ aTransformed,
+ getSdrFTAttribute().getFill(),
+ getSdrFTAttribute().getFillFloatTransGradient()));
+ }
+ else
+ {
+ // if no fill create one for HitTest and BoundRect fallback
+ rContainer.push_back(
+ createHiddenGeometryPrimitives2D(
+ true,
+ aUnitPolyPolygon,
+ getTransform()));
+ }
+
+ // add text
+ if(!getSdrFTAttribute().getText().isDefault())
+ {
+ rContainer.push_back(
+ createTextPrimitive(
+ aUnitPolyPolygon,
+ getTransform(),
+ getSdrFTAttribute().getText(),
+ attribute::SdrLineAttribute(),
+ true,
+ false));
+ }
+ }
+
+ bool SdrCellPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const SdrCellPrimitive2D& rCompare = static_cast<const SdrCellPrimitive2D&>(rPrimitive);
+
+ return (getTransform() == rCompare.getTransform()
+ && getSdrFTAttribute() == rCompare.getSdrFTAttribute());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrCellPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDRCELLPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+namespace sdr::contact
+{
+
+ namespace {
+ class ViewObjectContactOfTableObj : public ViewObjectContactOfSdrObj
+ {
+ public:
+ ViewObjectContactOfTableObj(ObjectContact& rObjectContact, ViewContact& rViewContact)
+ : ViewObjectContactOfSdrObj(rObjectContact, rViewContact)
+ {
+ }
+
+ protected:
+ virtual void createPrimitive2DSequence(DisplayInfo const& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const override;
+ };
+ } // namespace
+
+ static svx::frame::Style impGetLineStyle(
+ const sdr::table::TableLayouter& rLayouter,
+ sal_Int32 nX,
+ sal_Int32 nY,
+ bool bHorizontal,
+ sal_Int32 nColCount,
+ sal_Int32 nRowCount,
+ bool bIsRTL)
+ {
+ if(nX >= 0 && nX <= nColCount && nY >= 0 && nY <= nRowCount)
+ {
+ const SvxBorderLine* pLine = rLayouter.getBorderLine(nX, nY, bHorizontal);
+
+ if(pLine)
+ {
+ // copy line content
+ SvxBorderLine aLine(*pLine);
+
+ // check for mirroring. This shall always be done when it is
+ // not a top- or rightmost line
+ bool bMirror(aLine.isDouble());
+
+ if(bMirror)
+ {
+ if(bHorizontal)
+ {
+ // mirror all bottom lines
+ bMirror = (0 != nY);
+ }
+ else
+ {
+ // mirror all left lines
+ bMirror = (bIsRTL ? 0 != nX : nX != nColCount);
+ }
+ }
+
+ if(bMirror)
+ {
+ aLine.SetMirrorWidths( );
+ }
+
+ constexpr double fTwipsToMM(
+ o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100));
+ return svx::frame::Style(&aLine, fTwipsToMM);
+ }
+ }
+
+ // no success, copy empty line
+ return svx::frame::Style();
+ }
+
+ static void createPrimitive2DSequenceImpl(
+ sdr::table::SdrTableObj const& rTableObj,
+ bool const isTaggedPDF,
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor)
+ {
+ const uno::Reference< css::table::XTable > xTable = rTableObj.getTable();
+
+ if(xTable.is())
+ {
+ // create primitive representation for table. Cell info goes
+ // directly to aRetval, Border info to aBorderSequence and added
+ // later to get the correct overlapping
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+ drawinglayer::primitive2d::Primitive2DContainer aRetvalForShadow;
+ const sal_Int32 nRowCount(xTable->getRowCount());
+ const sal_Int32 nColCount(xTable->getColumnCount());
+ const sal_Int32 nAllCount(nRowCount * nColCount);
+ SdrPage const*const pPage(rTableObj.getSdrPageFromSdrObject());
+
+ if(nAllCount)
+ {
+ const sdr::table::TableLayouter& rTableLayouter(rTableObj.getTableLayouter());
+ const bool bIsRTL(css::text::WritingMode_RL_TB == rTableObj.GetWritingMode());
+ sdr::table::CellPos aCellPos;
+ sdr::table::CellRef xCurrentCell;
+ basegfx::B2IRectangle aCellArea;
+
+ // create range using the model data directly. This is in SdrTextObj::aRect which i will access using
+ // GetGeoRect() to not trigger any calculations. It's the unrotated geometry.
+ const basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(rTableObj.GetGeoRect());
+
+ // To create the CellBorderPrimitives, use the tooling from svx::frame::Array
+ // which is capable of creating the needed visualization. Fill it during the
+ // anyways needed run over the table.
+ svx::frame::Array aArray;
+
+ // initialize CellBorderArray for primitive creation
+ aArray.Initialize(nColCount, nRowCount);
+
+ // create single primitives per cell
+ for(aCellPos.mnRow = 0; aCellPos.mnRow < nRowCount; aCellPos.mnRow++)
+ {
+ drawinglayer::primitive2d::Primitive2DContainer row;
+ // add RowHeight to CellBorderArray for primitive creation
+ aArray.SetRowHeight(aCellPos.mnRow, rTableLayouter.getRowHeight(aCellPos.mnRow));
+
+ for(aCellPos.mnCol = 0; aCellPos.mnCol < nColCount; aCellPos.mnCol++)
+ {
+ drawinglayer::primitive2d::Primitive2DContainer cell;
+ // add ColWidth to CellBorderArray for primitive creation, only
+ // needs to be done in the 1st run
+ if(0 == aCellPos.mnRow)
+ {
+ aArray.SetColWidth(aCellPos.mnCol, rTableLayouter.getColumnWidth(aCellPos.mnCol));
+ }
+
+ // access the cell
+ xCurrentCell.set(dynamic_cast< sdr::table::Cell* >(xTable->getCellByPosition(aCellPos.mnCol, aCellPos.mnRow).get()));
+
+ if(xCurrentCell.is())
+ {
+ // copy styles for current cell to CellBorderArray for primitive creation
+ aArray.SetCellStyleLeft(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol, aCellPos.mnRow, false, nColCount, nRowCount, bIsRTL));
+ aArray.SetCellStyleRight(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol + 1, aCellPos.mnRow, false, nColCount, nRowCount, bIsRTL));
+ aArray.SetCellStyleTop(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol, aCellPos.mnRow, true, nColCount, nRowCount, bIsRTL));
+ aArray.SetCellStyleBottom(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol, aCellPos.mnRow + 1, true, nColCount, nRowCount, bIsRTL));
+
+ // ignore merged cells (all except the top-left of a merged cell)
+ if(!xCurrentCell->isMerged())
+ {
+ // check if we are the top-left of a merged cell
+ const sal_Int32 nXSpan(xCurrentCell->getColumnSpan());
+ const sal_Int32 nYSpan(xCurrentCell->getRowSpan());
+
+ if(nXSpan > 1 || nYSpan > 1)
+ {
+ // if merged, set so at CellBorderArray for primitive creation
+ aArray.SetMergedRange(aCellPos.mnCol, aCellPos.mnRow, aCellPos.mnCol + nXSpan - 1, aCellPos.mnRow + nYSpan - 1);
+ }
+ }
+ }
+
+ if(xCurrentCell.is() && !xCurrentCell->isMerged())
+ {
+ if(rTableLayouter.getCellArea(xCurrentCell, aCellPos, aCellArea))
+ {
+ // create cell transformation matrix
+ basegfx::B2DHomMatrix aCellMatrix;
+ aCellMatrix.set(0, 0, static_cast<double>(aCellArea.getWidth()));
+ aCellMatrix.set(1, 1, static_cast<double>(aCellArea.getHeight()));
+ aCellMatrix.set(0, 2, static_cast<double>(aCellArea.getMinX()) + aObjectRange.getMinX());
+ aCellMatrix.set(1, 2, static_cast<double>(aCellArea.getMinY()) + aObjectRange.getMinY());
+
+ // handle cell fillings and text
+ const SfxItemSet& rCellItemSet = xCurrentCell->GetItemSet();
+ const sal_uInt32 nTextIndex(nColCount * aCellPos.mnRow + aCellPos.mnCol);
+ const SdrText* pSdrText = rTableObj.getText(nTextIndex);
+ drawinglayer::attribute::SdrFillTextAttribute aAttribute;
+
+ if(pSdrText)
+ {
+ // #i101508# take cell's local text frame distances into account
+ const sal_Int32 nLeft(xCurrentCell->GetTextLeftDistance());
+ const sal_Int32 nRight(xCurrentCell->GetTextRightDistance());
+ const sal_Int32 nUpper(xCurrentCell->GetTextUpperDistance());
+ const sal_Int32 nLower(xCurrentCell->GetTextLowerDistance());
+
+ aAttribute = drawinglayer::primitive2d::createNewSdrFillTextAttribute(
+ rCellItemSet,
+ pSdrText,
+ &nLeft,
+ &nUpper,
+ &nRight,
+ &nLower);
+ }
+ else
+ {
+ aAttribute = drawinglayer::primitive2d::createNewSdrFillTextAttribute(
+ rCellItemSet,
+ pSdrText);
+ }
+
+ // always create cell primitives for BoundRect and HitTest
+ {
+ const drawinglayer::primitive2d::Primitive2DReference xCellReference(
+ new drawinglayer::primitive2d::SdrCellPrimitive2D(
+ aCellMatrix, aAttribute));
+ cell.append(xCellReference);
+ }
+
+ // Create cell primitive without text.
+ aAttribute
+ = drawinglayer::primitive2d::createNewSdrFillTextAttribute(
+ rCellItemSet, nullptr);
+ rtl::Reference pCellReference
+ = new drawinglayer::primitive2d::SdrCellPrimitive2D(
+ aCellMatrix, aAttribute);
+
+ sal_uInt16 nTransparence(
+ rCellItemSet.Get(XATTR_FILLTRANSPARENCE).GetValue());
+ if (nTransparence != 0)
+ {
+ pCellReference->setTransparenceForShadow(nTransparence);
+ }
+
+ const drawinglayer::primitive2d::Primitive2DReference
+ xCellReference(pCellReference);
+ aRetvalForShadow.append(xCellReference);
+ }
+ }
+ if (isTaggedPDF && pPage)
+ {
+ // heuristic: if there's a special formatting on
+ // first row, assume that it's a header row
+ auto const eType(
+ aCellPos.mnRow == 0 && rTableObj.getTableStyleSettings().mbUseFirstRow
+ ? vcl::PDFWriter::TableHeader
+ : vcl::PDFWriter::TableData);
+ cell = drawinglayer::primitive2d::Primitive2DContainer {
+ new drawinglayer::primitive2d::StructureTagPrimitive2D(
+ eType,
+ pPage->IsMasterPage(),
+ false,
+ std::move(cell)) };
+ }
+ row.append(cell);
+ }
+
+ if (isTaggedPDF && pPage)
+ {
+ row = drawinglayer::primitive2d::Primitive2DContainer {
+ new drawinglayer::primitive2d::StructureTagPrimitive2D(
+ vcl::PDFWriter::TableRow,
+ pPage->IsMasterPage(),
+ false,
+ std::move(row)) };
+ }
+ aRetval.append(row);
+ }
+
+ // now create all CellBorderPrimitives
+ drawinglayer::primitive2d::Primitive2DContainer aCellBorderPrimitives(aArray.CreateB2DPrimitiveArray());
+
+ if(!aCellBorderPrimitives.empty())
+ {
+ // this is already scaled (due to Table in non-uniform coordinates), so
+ // first transform removing scale
+ basegfx::B2DHomMatrix aTransform(
+ basegfx::utils::createScaleB2DHomMatrix(
+ 1.0 / aObjectRange.getWidth(),
+ 1.0 / aObjectRange.getHeight()));
+
+ // If RTL, mirror the whole unified table in X and move right.
+ // This is much easier than taking this into account for the whole
+ // index calculations
+ if(bIsRTL)
+ {
+ aTransform.scale(-1.0, 1.0);
+ aTransform.translate(1.0, 0.0);
+ }
+
+ // create object matrix
+ const GeoStat& rGeoStat(rTableObj.GetGeoStat());
+ const double fShearX(-rGeoStat.mfTanShearAngle);
+ const double fRotate(rGeoStat.nRotationAngle ? toRadians(36000_deg100 - rGeoStat.nRotationAngle) : 0.0);
+ const basegfx::B2DHomMatrix aObjectMatrix(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aObjectRange.getWidth(), aObjectRange.getHeight(), fShearX, fRotate,
+ aObjectRange.getMinX(), aObjectRange.getMinY()));
+
+ // add object matrix to transform. By doing so theoretically
+ // CellBorders could be also rotated/sheared for the first time ever.
+ // To completely make that work, the primitives already created in
+ // aRetval would also have to be based on ObjectMatrix, not only on
+ // ObjectRange as it currently is.
+ aTransform *= aObjectMatrix;
+
+ // create a transform primitive with this and embed CellBorders
+ // and append to retval
+ aRetval.append(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ aTransform,
+ drawinglayer::primitive2d::Primitive2DContainer(aCellBorderPrimitives)));
+
+ // Borders are always the same for shadow as well.
+ aRetvalForShadow.append(new drawinglayer::primitive2d::TransformPrimitive2D(
+ aTransform, std::move(aCellBorderPrimitives)));
+ }
+ }
+
+ if(!aRetval.empty())
+ {
+ // check and create evtl. shadow for created content
+ const SfxItemSet& rObjectItemSet = rTableObj.GetMergedItemSet();
+ const drawinglayer::attribute::SdrShadowAttribute aNewShadowAttribute(
+ drawinglayer::primitive2d::createNewSdrShadowAttribute(rObjectItemSet));
+
+ if(!aNewShadowAttribute.isDefault())
+ {
+ bool bDirectShadow
+ = rObjectItemSet.Get(SDRATTR_SHADOW, /*bSrchInParent=*/false)
+ .GetValue();
+ if (bDirectShadow)
+ {
+ // Shadow as direct formatting: no shadow for text, to be compatible
+ // with PowerPoint.
+ basegfx::B2DHomMatrix aMatrix;
+ aRetval = drawinglayer::primitive2d::createEmbeddedShadowPrimitive(
+ std::move(aRetval), aNewShadowAttribute, aMatrix, &aRetvalForShadow);
+ }
+ else
+ {
+ // Shadow as style: shadow for text, to be backwards-compatible.
+ aRetval = drawinglayer::primitive2d::createEmbeddedShadowPrimitive(
+ std::move(aRetval), aNewShadowAttribute);
+ }
+ }
+ }
+
+ rVisitor.visit(std::move(aRetval));
+ }
+ else
+ {
+ // take unrotated snap rect (direct model data) for position and size
+ const basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(rTableObj.GetGeoRect());
+
+ // create object matrix
+ const GeoStat& rGeoStat(rTableObj.GetGeoStat());
+ const double fShearX(-rGeoStat.mfTanShearAngle);
+ const double fRotate(rGeoStat.nRotationAngle ? toRadians(36000_deg100 - rGeoStat.nRotationAngle) : 0.0);
+ const basegfx::B2DHomMatrix aObjectMatrix(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aObjectRange.getWidth(), aObjectRange.getHeight(), fShearX, fRotate,
+ aObjectRange.getMinX(), aObjectRange.getMinY()));
+
+ // created an invisible outline for the cases where no visible content exists
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ drawinglayer::primitive2d::createHiddenGeometryPrimitives2D(
+ aObjectMatrix));
+
+ rVisitor.visit(xReference);
+ }
+ }
+
+ void ViewObjectContactOfTableObj::createPrimitive2DSequence(
+ DisplayInfo const& rDisplayInfo,
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ bool const isTaggedPDF(GetObjectContact().isExportTaggedPDF());
+ if (isTaggedPDF)
+ {
+ // this will be unbuffered and contain structure tags
+ const sdr::table::SdrTableObj& rTableObj =
+ static_cast<const sdr::table::SdrTableObj&>(*GetViewContact().TryToGetSdrObject());
+ return createPrimitive2DSequenceImpl(rTableObj, true, rVisitor);
+ }
+ else
+ {
+ // call it via the base class - this is supposed to be buffered
+ return sdr::contact::ViewObjectContactOfSdrObj::createPrimitive2DSequence(rDisplayInfo, rVisitor);
+ }
+ }
+
+ void ViewContactOfTableObj::createViewIndependentPrimitive2DSequence(
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ const sdr::table::SdrTableObj& rTableObj =
+ static_cast<const sdr::table::SdrTableObj&>(GetSdrObject());
+ return createPrimitive2DSequenceImpl(rTableObj, false, rVisitor);
+ }
+
+ ViewObjectContact& ViewContactOfTableObj::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+ {
+ return *new ViewObjectContactOfTableObj(rObjectContact, *this);
+ }
+
+ ViewContactOfTableObj::ViewContactOfTableObj(sdr::table::SdrTableObj& rTableObj)
+ : ViewContactOfSdrObj(rTableObj)
+ {
+ }
+
+ ViewContactOfTableObj::~ViewContactOfTableObj()
+ {
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/viewcontactoftableobj.hxx b/svx/source/table/viewcontactoftableobj.hxx
new file mode 100644
index 000000000..fbdd80538
--- /dev/null
+++ b/svx/source/table/viewcontactoftableobj.hxx
@@ -0,0 +1,45 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_TABLE_VIEWCONTACTOFTABLEOBJ_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_VIEWCONTACTOFTABLEOBJ_HXX
+
+#include <svx/sdr/contact/viewcontactofsdrobj.hxx>
+#include <svx/svdotable.hxx>
+
+namespace sdr::contact
+ {
+ class ViewContactOfTableObj : public ViewContactOfSdrObj
+ {
+ protected:
+ // This method is responsible for creating the graphical visualisation data derived ONLY from
+ // the model data
+ virtual void createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const override;
+ virtual ViewObjectContact& CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) override;
+
+ public:
+ // basic constructor, used from SdrObject.
+ explicit ViewContactOfTableObj(sdr::table::SdrTableObj& rTextObj);
+ virtual ~ViewContactOfTableObj() override;
+ };
+} // end of namespace sdr::contact
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */