icon-themes/colibre/sd/res/addtablestyle.png |binary
 include/svx/sdr/table/tablecontroller.hxx    |    3 
 sd/inc/bitmaps.hlst                          |    1 
 sd/inc/strings.hrc                           |    2 
 sd/qa/unit/data/odg/tablestyles.fodg         |   22 +
 sd/qa/unit/export-tests.cxx                  |   35 ++
 sd/source/core/drawdoc4.cxx                  |    5 
 sd/source/core/stlsheet.cxx                  |    9 
 sd/source/ui/inc/TableDesignPane.hxx         |   16 +
 sd/source/ui/table/TableDesignPane.cxx       |  317 +++++++++++++++++++++++++--
 sd/uiconfig/simpress/ui/tabledesignpanel.ui  |  118 ++++++++++
 svx/source/table/tablecontroller.cxx         |   56 ++--
 svx/source/table/tabledesign.cxx             |  126 ++++++++++
 xmloff/source/table/XMLTableExport.cxx       |   14 +
 14 files changed, 674 insertions(+), 50 deletions(-)

New commits:
commit f23d3661ab04601650db95f846081317fc06801d
Author:     Maxim Monastirsky <momonas...@gmail.com>
AuthorDate: Sun Nov 13 14:50:08 2022 +0200
Commit:     Maxim Monastirsky <momonas...@gmail.com>
CommitDate: Mon Nov 21 14:39:14 2022 +0100

    sd: table design editing
    
    Change-Id: I75559c80da015a13ee078bdda06f6f4975fe5946
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/140943
    Tested-by: Jenkins
    Reviewed-by: Maxim Monastirsky <momonas...@gmail.com>

diff --git a/icon-themes/colibre/sd/res/addtablestyle.png 
b/icon-themes/colibre/sd/res/addtablestyle.png
new file mode 100644
index 000000000000..8e4422cb733f
Binary files /dev/null and b/icon-themes/colibre/sd/res/addtablestyle.png differ
diff --git a/include/svx/sdr/table/tablecontroller.hxx 
b/include/svx/sdr/table/tablecontroller.hxx
index e71eba7cf608..bb02fc271f52 100644
--- a/include/svx/sdr/table/tablecontroller.hxx
+++ b/include/svx/sdr/table/tablecontroller.hxx
@@ -87,6 +87,9 @@ public:
         const SdrTableObj& rObj,
         const rtl::Reference< sdr::SelectionController >& xRefController);
 
+    static SvxBoxItem TextDistancesToSvxBoxItem(const SfxItemSet& rAttrSet);
+    static void SvxBoxItemToTextDistances(const SvxBoxItem& pOriginalItem, 
SfxItemSet& rAttrSet);
+
     SVX_DLLPRIVATE void MergeAttrFromSelectedCells(SfxItemSet& rAttr, bool 
bOnlyHardAttr) const;
     SVX_DLLPRIVATE void SetAttrToSelectedCells(const SfxItemSet& rAttr, bool 
bReplaceAll);
     void SetAttrToSelectedShape(const SfxItemSet& rAttr);
diff --git a/sd/inc/bitmaps.hlst b/sd/inc/bitmaps.hlst
index e1c798cfae4e..32e81ced59fa 100644
--- a/sd/inc/bitmaps.hlst
+++ b/sd/inc/bitmaps.hlst
@@ -78,6 +78,7 @@ inline constexpr OUStringLiteral BMP_POINTER_ICON = 
u"sd/res/pointericon.png";
 inline constexpr OUStringLiteral BMP_WAIT_ICON = u"sd/res/waiticon.png";
 inline constexpr OUStringLiteral BMP_FADE_EFFECT_INDICATOR = 
u"sd/res/fade_effect_indicator.png";
 inline constexpr OUStringLiteral BMP_CUSTOM_ANIMATION_INDICATOR = 
u"sd/res/click_16.png";
+inline constexpr OUStringLiteral BMP_INSERT_TABLESTYLE = 
u"sd/res/addtablestyle.png";
 // Presenter Screen bitmaps:
 inline constexpr OUStringLiteral BMP_PRESENTERSCREEN_BORDER_LEFT = 
u"sd/res/presenterscreen-BorderLeft.png";
 inline constexpr OUStringLiteral 
BMP_PRESENTERSCREEN_BUTTON_SLIDE_NEXT_DISABLED = 
u"sd/res/presenterscreen-ButtonSlideNextDisabled.png";
diff --git a/sd/inc/strings.hrc b/sd/inc/strings.hrc
index 05326f9e461a..662acd59db12 100644
--- a/sd/inc/strings.hrc
+++ b/sd/inc/strings.hrc
@@ -484,5 +484,7 @@
 #define RID_SVXSTR_MENU_LAST                            
NC_("RID_SVXSTR_MENU_LAST", "~Last Slide")
 
 #define STR_CLOSE_PANE                                  NC_("STR_CLOSE_PANE", 
"Close Pane")
+#define STR_INSERT_TABLESTYLE                           
NC_("STR_INSERT_TABLESTYLE", "Add a new design")
+#define STR_REMOVE_TABLESTYLE                           
NC_("STR_REMOVE_TABLESTYLE", "The selected style is in use in this 
document.\nIf you will delete this style, tables using it will revert to the 
default style.\nDo you still wish to delete this style?\n")
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/inc/TableDesignPane.hxx 
b/sd/source/ui/inc/TableDesignPane.hxx
index 042eb6137662..a3658bb30bf2 100644
--- a/sd/source/ui/inc/TableDesignPane.hxx
+++ b/sd/source/ui/inc/TableDesignPane.hxx
@@ -25,7 +25,7 @@
 #include <vcl/weld.hxx>
 
 namespace com::sun::star::beans { class XPropertySet; }
-namespace com::sun::star::container { class XIndexAccess; }
+namespace com::sun::star::container { class XIndexAccess; class 
XNameContainer; }
 namespace com::sun::star::drawing { class XDrawView; }
 
 namespace sd
@@ -52,12 +52,16 @@ class TableValueSet final : public ValueSet
 {
 private:
     bool m_bModal;
+    Link<const Point*, void> maContextMenuHandler;
 public:
     TableValueSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow);
+    virtual bool Command(const CommandEvent& rEvent) override;
     virtual void Resize() override;
     virtual void StyleUpdated() override;
     void updateSettings();
     void setModal(bool bModal) { m_bModal = bModal; }
+    void SetContextMenuHandler(const Link<const Point*, void>& rLink) { 
maContextMenuHandler = rLink; }
+    static constexpr int getMaxRowCount() { return 3; }
 };
 
 class TableDesignWidget final
@@ -71,20 +75,29 @@ public:
 
     void ApplyOptions();
     void ApplyStyle();
+    void InsertStyle();
+    void CloneStyle();
+    void ResetStyle();
+    void DeleteStyle();
+    void EditStyle(std::string_view rCommand);
 
 private:
     void addListener();
     void removeListener();
     void updateControls();
+    void selectStyle(std::u16string_view rStyle);
+    void setDocumentModified();
 
     void FillDesignPreviewControl();
 
     DECL_LINK(EventMultiplexerListener, tools::EventMultiplexerEvent&, void);
+    DECL_LINK(implContextMenuHandler, const Point*, void);
     DECL_LINK(implValueSetHdl, ValueSet*, void);
     DECL_LINK(implCheckBoxHdl, weld::Toggleable&, void);
 
     ViewShellBase& mrBase;
 
+    std::unique_ptr<weld::Menu> m_xMenu;
     std::unique_ptr<TableValueSet> m_xValueSet;
     std::unique_ptr<weld::CustomWeld> m_xValueSetWin;
     std::unique_ptr<weld::CheckButton> m_aCheckBoxes[CB_COUNT];
@@ -92,6 +105,7 @@ private:
     css::uno::Reference< css::beans::XPropertySet > mxSelectedTable;
     css::uno::Reference< css::drawing::XDrawView > mxView;
     css::uno::Reference< css::container::XIndexAccess > mxTableFamily;
+    css::uno::Reference< css::container::XNameContainer > mxCellFamily;
 };
 
 class TableDesignPane final : public PanelLayout
diff --git a/sd/source/ui/table/TableDesignPane.cxx 
b/sd/source/ui/table/TableDesignPane.cxx
index fbed413d3160..07371e6e9980 100644
--- a/sd/source/ui/table/TableDesignPane.cxx
+++ b/sd/source/ui/table/TableDesignPane.cxx
@@ -24,14 +24,18 @@
 #include <com/sun/star/beans/XPropertySet.hpp>
 #include <com/sun/star/drawing/XDrawView.hpp>
 #include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
 #include <com/sun/star/view/XSelectionSupplier.hpp>
 #include <com/sun/star/style/XStyle.hpp>
 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
 
 #include <comphelper/sequence.hxx>
 #include <sfx2/viewfrm.hxx>
+#include <vcl/commandevent.hxx>
 #include <vcl/image.hxx>
 #include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
 #include <vcl/virdev.hxx>
 
 #include <tools/debug.hxx>
@@ -44,15 +48,21 @@
 #include <sfx2/dispatch.hxx>
 #include <svx/svxids.hrc>
 #include <svx/svdetc.hxx>
+#include <svx/svxdlg.hxx>
 #include <editeng/boxitem.hxx>
 #include <editeng/borderline.hxx>
 #include <editeng/colritem.hxx>
 #include <editeng/eeitem.hxx>
 #include <svx/sdr/table/tabledesign.hxx>
+#include <svx/sdr/table/tablecontroller.hxx>
 #include <o3tl/enumrange.hxx>
 
 #include <TableDesignPane.hxx>
 
+#include <stlsheet.hxx>
+#include <strings.hrc>
+#include <sdresid.hxx>
+#include <bitmaps.hlst>
 #include <ViewShell.hxx>
 #include <ViewShellBase.hxx>
 #include <EventMultiplexer.hxx>
@@ -89,6 +99,7 @@ const std::string_view gPropNames[CB_COUNT] =
 
 TableDesignWidget::TableDesignWidget(weld::Builder& rBuilder, ViewShellBase& 
rBase)
     : mrBase(rBase)
+    , m_xMenu(rBuilder.weld_menu("menu"))
     , m_xValueSet(new 
TableValueSet(rBuilder.weld_scrolled_window("previewswin", true)))
     , m_xValueSetWin(new weld::CustomWeld(rBuilder, "previews", *m_xValueSet))
 {
@@ -97,6 +108,7 @@ TableDesignWidget::TableDesignWidget(weld::Builder& 
rBuilder, ViewShellBase& rBa
     m_xValueSet->setModal(false);
     m_xValueSet->SetColor();
     m_xValueSet->SetSelectHdl(LINK(this, TableDesignWidget, implValueSetHdl));
+    m_xValueSet->SetContextMenuHandler(LINK(this, TableDesignWidget, 
implContextMenuHandler));
 
     for (sal_uInt16 i = CB_HEADER_ROW; i <= CB_BANDED_COLUMNS; ++i)
     {
@@ -114,6 +126,7 @@ TableDesignWidget::TableDesignWidget(weld::Builder& 
rBuilder, ViewShellBase& rBa
         Reference< XStyleFamiliesSupplier > xFamiliesSupp( 
xController->getModel(), UNO_QUERY_THROW );
         Reference< XNameAccess > xFamilies( xFamiliesSupp->getStyleFamilies() 
);
         mxTableFamily.set( xFamilies->getByName( "table" ), UNO_QUERY_THROW );
+        mxCellFamily.set( xFamilies->getByName( "cell" ), UNO_QUERY_THROW );
     }
     catch (const Exception&)
     {
@@ -129,6 +142,263 @@ TableDesignWidget::~TableDesignWidget()
     removeListener();
 }
 
+void TableDesignWidget::setDocumentModified()
+{
+    try
+    {
+        Reference<XController> xController(mrBase.GetController(), 
UNO_SET_THROW);
+        Reference<util::XModifiable> xModifiable(xController->getModel(), 
UNO_QUERY_THROW);
+        xModifiable->setModified(true);
+    }
+    catch (Exception&)
+    {
+        TOOLS_WARN_EXCEPTION( "sd", 
"TableDesignWidget::setDocumentModified()");
+    }
+}
+
+IMPL_LINK(TableDesignWidget, implContextMenuHandler, const Point*, pPoint, 
void)
+{
+    auto nClickedItemId = pPoint ? m_xValueSet->GetItemId(*pPoint) : 
m_xValueSet->GetSelectedItemId();
+
+    try
+    {
+        if (nClickedItemId > mxTableFamily->getCount())
+            return;
+
+        if (nClickedItemId)
+        {
+            Reference<XStyle> xStyle(mxTableFamily->getByIndex(nClickedItemId 
- 1), UNO_QUERY_THROW);
+
+            m_xMenu->set_visible("clone", true);
+            m_xMenu->set_visible("format", true);
+            m_xMenu->set_visible("delete", xStyle->isUserDefined());
+            m_xMenu->set_visible("reset", !xStyle->isUserDefined());
+            m_xMenu->set_sensitive("reset", 
Reference<util::XModifiable>(xStyle, UNO_QUERY_THROW)->isModified());
+        }
+        else
+        {
+            m_xMenu->set_visible("clone", false);
+            m_xMenu->set_visible("format", false);
+            m_xMenu->set_visible("delete", false);
+            m_xMenu->set_visible("reset", false);
+        }
+    }
+    catch (Exception&)
+    {
+        TOOLS_WARN_EXCEPTION( "sd", 
"TableDesignWidget::implContextMenuHandler()");
+    }
+
+    m_xValueSet->SelectItem(nClickedItemId);
+
+    Point aPosition = pPoint ? *pPoint : 
m_xValueSet->GetItemRect(nClickedItemId).Center();
+    OString aCommand = m_xMenu->popup_at_rect(m_xValueSet->GetDrawingArea(), 
::tools::Rectangle(aPosition, Size(1,1)));
+
+    if (aCommand == "new")
+        InsertStyle();
+    else if (aCommand == "clone")
+        CloneStyle();
+    else if (aCommand == "delete")
+        DeleteStyle();
+    else if (aCommand == "reset")
+        ResetStyle();
+    else if (!aCommand.isEmpty())
+        EditStyle(aCommand);
+}
+
+namespace
+{
+    OUString getNewValidTableStyleName(const Reference<XNameContainer>& 
rTableFamily)
+    {
+        OUString aName;
+        sal_Int32 nIndex = 1;
+        do
+        {
+            aName = "table" + OUString::number(nIndex++);
+        }
+        while(rTableFamily->hasByName(aName));
+
+        return aName;
+    }
+}
+
+void TableDesignWidget::InsertStyle()
+{
+    try
+    {
+        Reference<XSingleServiceFactory> xFactory(mxTableFamily, 
UNO_QUERY_THROW);
+        Reference<XNameContainer> xTableFamily(mxTableFamily, UNO_QUERY_THROW);
+        Reference<XNameReplace> xTableStyle(xFactory->createInstance(), 
UNO_QUERY_THROW);
+        const OUString aName(getNewValidTableStyleName(xTableFamily));
+        xTableFamily->insertByName(aName, Any(xTableStyle));
+
+        Reference<XStyle> xCellStyle(mxCellFamily->getByName("default"), 
UNO_QUERY_THROW);
+
+        xTableStyle->replaceByName("body", Any(xCellStyle));
+        xTableStyle->replaceByName("odd-rows" , Any(xCellStyle));
+        xTableStyle->replaceByName("odd-columns" , Any(xCellStyle));
+        xTableStyle->replaceByName("first-row" , Any(xCellStyle));
+        xTableStyle->replaceByName("first-column" , Any(xCellStyle));
+        xTableStyle->replaceByName("last-row" , Any(xCellStyle));
+        xTableStyle->replaceByName("last-column" , Any(xCellStyle));
+
+        updateControls();
+        selectStyle(aName);
+        setDocumentModified();
+    }
+    catch (Exception&)
+    {
+        TOOLS_WARN_EXCEPTION( "sd", "TableDesignWidget::InsertStyle()");
+    }
+}
+
+void TableDesignWidget::CloneStyle()
+{
+    try
+    {
+        Reference<XIndexAccess> 
xSrcTableStyle(mxTableFamily->getByIndex(m_xValueSet->GetSelectedItemId() - 1), 
UNO_QUERY_THROW);
+
+        Reference<XSingleServiceFactory> xFactory(mxTableFamily, 
UNO_QUERY_THROW);
+        Reference<XNameContainer> xTableFamily(mxTableFamily, UNO_QUERY_THROW);
+        Reference<XIndexReplace> xDestTableStyle(xFactory->createInstance(), 
UNO_QUERY_THROW);
+        const OUString aName(getNewValidTableStyleName(xTableFamily));
+        xTableFamily->insertByName(aName, Any(xDestTableStyle));
+
+        for (sal_Int32 i = 0; i < xDestTableStyle->getCount(); ++i)
+        {
+            Reference<XStyle> xSrcCellStyle(xSrcTableStyle->getByIndex(i), 
UNO_QUERY);
+            if (xSrcCellStyle && xSrcCellStyle->isUserDefined())
+            {
+                Reference<XSingleServiceFactory> xCellFactory(mxCellFamily, 
UNO_QUERY_THROW);
+                Reference<XStyle> 
xDestCellStyle(xCellFactory->createInstance(), UNO_QUERY_THROW);
+                
xDestCellStyle->setParentStyle(xSrcCellStyle->getParentStyle());
+                mxCellFamily->insertByName(xDestCellStyle->getName(), 
Any(xDestCellStyle));
+
+                rtl::Reference xSrcStyleSheet = 
static_cast<SdStyleSheet*>(xSrcCellStyle.get());
+                rtl::Reference xDestStyleSheet = 
static_cast<SdStyleSheet*>(xDestCellStyle.get());
+
+                
xDestStyleSheet->GetItemSet().Put(xSrcStyleSheet->GetItemSet());
+
+                xDestTableStyle->replaceByIndex(i, Any(xDestCellStyle));
+            }
+            else
+                xDestTableStyle->replaceByIndex(i, Any(xSrcCellStyle));
+        }
+
+        updateControls();
+        selectStyle(aName);
+        setDocumentModified();
+    }
+    catch (Exception&)
+    {
+        TOOLS_WARN_EXCEPTION( "sd", "TableDesignWidget::CloneStyle()");
+    }
+}
+
+void TableDesignWidget::ResetStyle()
+{
+    try
+    {
+        Reference<XIndexReplace> 
xTableStyle(mxTableFamily->getByIndex(m_xValueSet->GetSelectedItemId() - 1), 
UNO_QUERY_THROW);
+
+        for (sal_Int32 i = 0; i < xTableStyle->getCount(); ++i)
+        {
+            Reference<XStyle> xCellStyle(xTableStyle->getByIndex(i), 
UNO_QUERY);
+            if (xCellStyle && xCellStyle->isUserDefined())
+                xTableStyle->replaceByIndex(i, 
mxCellFamily->getByName(xCellStyle->getParentStyle()));
+        }
+
+        Reference<util::XModifiable>(xTableStyle, 
UNO_QUERY_THROW)->setModified(false);
+
+        updateControls();
+        setDocumentModified();
+    }
+    catch (Exception&)
+    {
+        TOOLS_WARN_EXCEPTION( "sd", "TableDesignWidget::ResetStyle()");
+    }
+}
+
+void TableDesignWidget::DeleteStyle()
+{
+    try
+    {
+        Reference<XStyle> 
xTableStyle(mxTableFamily->getByIndex(m_xValueSet->GetSelectedItemId() - 1), 
UNO_QUERY_THROW);
+
+        if (xTableStyle->isInUse())
+        {
+            std::unique_ptr<weld::MessageDialog> 
xBox(Application::CreateMessageDialog(
+                m_xValueSet->GetDrawingArea(), VclMessageType::Question, 
VclButtonsType::YesNo, SdResId(STR_REMOVE_TABLESTYLE)));
+
+            if (xBox->run() != RET_YES)
+                return;
+        }
+
+        Reference<XNameContainer>(mxTableFamily, 
UNO_QUERY_THROW)->removeByName(xTableStyle->getName());
+
+        updateControls();
+        setDocumentModified();
+    }
+    catch (Exception&)
+    {
+        TOOLS_WARN_EXCEPTION( "sd", "TableDesignWidget::DeleteStyle()");
+    }
+}
+
+void TableDesignWidget::EditStyle(std::string_view rCommand)
+{
+    try
+    {
+        Reference<XNameReplace> 
xTableStyle(mxTableFamily->getByIndex(m_xValueSet->GetSelectedItemId() - 1), 
UNO_QUERY_THROW);
+        Reference<XStyle> 
xCellStyle(xTableStyle->getByName(OUString::fromUtf8(rCommand)), 
UNO_QUERY_THROW);
+
+        bool bUserDefined = xCellStyle->isUserDefined();
+        if (!bUserDefined)
+        {
+            Reference<XSingleServiceFactory> xFactory(mxCellFamily, 
UNO_QUERY_THROW);
+            Reference<XStyle> xNewStyle(xFactory->createInstance(), 
UNO_QUERY_THROW);
+            xNewStyle->setParentStyle(xCellStyle->getName());
+            xCellStyle = xNewStyle;
+        }
+
+        rtl::Reference xStyleSheet = 
static_cast<SdStyleSheet*>(xCellStyle.get());
+        SfxItemSet aNewAttr(xStyleSheet->GetItemSet());
+
+        // merge drawing layer text distance items into SvxBoxItem used by the 
dialog
+        SvxBoxItem 
aBoxItem(sdr::table::SvxTableController::TextDistancesToSvxBoxItem(aNewAttr));
+        aNewAttr.Put(aBoxItem);
+
+        // inner borders do not apply to a cell style
+        SvxBoxInfoItem aBoxInfoItem(aNewAttr.Get(SDRATTR_TABLE_BORDER_INNER));
+        aBoxInfoItem.SetTable(false);
+        aNewAttr.Put(aBoxInfoItem);
+
+        SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+        ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact ? 
pFact->CreateSvxFormatCellsDialog(
+            mrBase.GetFrameWeld(), &aNewAttr, 
*mrBase.GetDrawView()->GetModel(), true) : nullptr);
+        if (pDlg && pDlg->Execute() == RET_OK)
+        {
+            if (!bUserDefined)
+            {
+                mxCellFamily->insertByName(xCellStyle->getName(), 
Any(xCellStyle));
+                xTableStyle->replaceByName(OUString::fromUtf8(rCommand), 
Any(xCellStyle));
+            }
+
+            SfxItemSet aNewSet(*pDlg->GetOutputItemSet());
+            
sdr::table::SvxTableController::SvxBoxItemToTextDistances(aBoxItem, aNewSet);
+            sdr::properties::CleanupFillProperties(aNewSet);
+            xStyleSheet->GetItemSet().Put(aNewSet);
+            xStyleSheet->Broadcast(SfxHint(SfxHintId::DataChanged));
+
+            updateControls();
+            setDocumentModified();
+        }
+    }
+    catch (Exception&)
+    {
+        TOOLS_WARN_EXCEPTION( "sd", "TableDesignWidget::EditStyle()");
+    }
+}
+
 static SfxBindings* getBindings( ViewShellBase const & rBase )
 {
     if( rBase.GetMainViewShell() && rBase.GetMainViewShell()->GetViewFrame() )
@@ -162,6 +432,11 @@ void TableDesignWidget::ApplyStyle()
             Reference< XNameAccess > xNames( mxTableFamily, UNO_QUERY_THROW );
             sStyleName = xNames->getElementNames()[nIndex];
         }
+        else if (nIndex == mxTableFamily->getCount())
+        {
+            InsertStyle();
+            return;
+        }
 
         if( sStyleName.isEmpty() )
             return;
@@ -281,6 +556,15 @@ void TableDesignWidget::onSelectionChanged()
     }
 }
 
+bool TableValueSet::Command(const CommandEvent& rEvent)
+{
+    if (rEvent.GetCommand() != CommandEventId::ContextMenu)
+        return ValueSet::Command(rEvent);
+
+    maContextMenuHandler.Call(rEvent.IsMouseEvent() ? 
&rEvent.GetMousePosPixel() : nullptr);
+    return true;
+}
+
 void TableValueSet::Resize()
 {
     ValueSet::Resize();
@@ -302,10 +586,10 @@ void TableValueSet::Resize()
     if (nRowCount < 1)
         nRowCount = 1;
 
-    int nVisibleRowCount = (aValueSetSize.Height()+2) / aItemSize.Height();
+    int nVisibleRowCount = std::min(nRowCount, getMaxRowCount());
 
     SetColCount (static_cast<sal_uInt16>(nColumnCount));
-    SetLineCount (static_cast<sal_uInt16>(nRowCount));
+    SetLineCount (static_cast<sal_uInt16>(nVisibleRowCount));
 
     if( !m_bModal )
     {
@@ -364,25 +648,24 @@ void TableDesignWidget::updateControls()
     m_xValueSet->updateSettings();
     m_xValueSet->Resize();
 
-    sal_uInt16 nSelection = 0;
     if( mxSelectedTable.is() )
     {
         Reference< XNamed > xNamed( mxSelectedTable->getPropertyValue( 
"TableTemplate" ), UNO_QUERY );
         if( xNamed.is() )
-        {
-            const OUString sStyleName( xNamed->getName() );
+            selectStyle(xNamed->getName());
+    }
+}
 
-            Reference< XNameAccess > xNames( mxTableFamily, UNO_QUERY );
-            if( xNames.is() )
-            {
-                Sequence< OUString > aNames( xNames->getElementNames() );
-                sal_Int32 nIndex = comphelper::findValue(aNames, sStyleName);
-                if (nIndex != -1)
-                    nSelection = static_cast<sal_uInt16>(nIndex) + 1;
-            }
-        }
+void TableDesignWidget::selectStyle(std::u16string_view rStyle)
+{
+    Reference< XNameAccess > xNames( mxTableFamily, UNO_QUERY );
+    if( xNames.is() )
+    {
+        Sequence< OUString > aNames( xNames->getElementNames() );
+        sal_Int32 nIndex = comphelper::findValue(aNames, rStyle);
+        if (nIndex != -1)
+            m_xValueSet->SelectItem(static_cast<sal_uInt16>(nIndex) + 1);
     }
-    m_xValueSet->SelectItem( nSelection );
 }
 
 void TableDesignWidget::addListener()
@@ -735,8 +1018,10 @@ void TableDesignWidget::FillDesignPreviewControl()
         {
             TOOLS_WARN_EXCEPTION( "sd", 
"sd::TableDesignWidget::FillDesignPreviewControl()");
         }
+        m_xValueSet->InsertItem(++nCount, Image(StockImage::Yes, 
BMP_INSERT_TABLESTYLE), SdResId(STR_INSERT_TABLESTYLE));
+
         sal_Int32 nCols = 3;
-        sal_Int32 nRows = (nCount+2)/3;
+        sal_Int32 nRows = std::min<sal_Int32>((nCount+2)/3, 
TableValueSet::getMaxRowCount());
         m_xValueSet->SetColCount(nCols);
         m_xValueSet->SetLineCount(nRows);
         WinBits nStyle = m_xValueSet->GetStyle() & ~WB_VSCROLL;
diff --git a/sd/uiconfig/simpress/ui/tabledesignpanel.ui 
b/sd/uiconfig/simpress/ui/tabledesignpanel.ui
index 0c3ec5e403e0..c820774d1bec 100644
--- a/sd/uiconfig/simpress/ui/tabledesignpanel.ui
+++ b/sd/uiconfig/simpress/ui/tabledesignpanel.ui
@@ -142,4 +142,122 @@
       </packing>
     </child>
   </object>
+  <object class="GtkMenu" id="menu">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <child>
+      <object class="GtkMenuItem" id="new">
+        <property name="visible">True</property>
+        <property name="can-focus">False</property>
+        <property name="label" translatable="yes" 
context="tabledesignpanel|menunew">New</property>
+        <property name="use-underline">True</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkMenuItem" id="clone">
+        <property name="visible">True</property>
+        <property name="can-focus">False</property>
+        <property name="label" translatable="yes" 
context="tabledesignpanel|menuclone">Clone</property>
+        <property name="use-underline">True</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkMenuItem" id="delete">
+        <property name="visible">True</property>
+        <property name="can-focus">False</property>
+        <property name="label" translatable="yes" 
context="tabledesignpanel|menudelete">Delete</property>
+        <property name="use-underline">True</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkMenuItem" id="reset">
+        <property name="visible">True</property>
+        <property name="can-focus">False</property>
+        <property name="label" translatable="yes" 
context="tabledesignpanel|menureset">Reset</property>
+        <property name="use-underline">True</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkMenuItem" id="format">
+        <property name="visible">True</property>
+        <property name="can-focus">False</property>
+        <property name="label" translatable="yes" 
context="tabledesignpanel|menuformat">Format</property>
+        <property name="use-underline">True</property>
+        <child type="submenu">
+          <object class="GtkMenu">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <child>
+              <object class="GtkMenuItem" id="first-row">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label" translatable="yes" 
context="tabledesignpanel|menufirstrow">Header row...</property>
+                <property name="use-underline">True</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkMenuItem" id="last-row">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label" translatable="yes" 
context="tabledesignpanel|menulastrow">Total row...</property>
+                <property name="use-underline">True</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkMenuItem" id="odd-rows">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label" translatable="yes" 
context="tabledesignpanel|menuoddrows">Banded rows...</property>
+                <property name="use-underline">True</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkSeparatorMenuItem">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkMenuItem" id="first-column">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label" translatable="yes" 
context="tabledesignpanel|menufirstcolumn">First column...</property>
+                <property name="use-underline">True</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkMenuItem" id="last-column">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label" translatable="yes" 
context="tabledesignpanel|menulastcolumn">Last column...</property>
+                <property name="use-underline">True</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkMenuItem" id="odd-columns">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label" translatable="yes" 
context="tabledesignpanel|menuoddcolumns">Banded columns...</property>
+                <property name="use-underline">True</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkSeparatorMenuItem">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkMenuItem" id="body">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label" translatable="yes" 
context="tabledesignpanel|menubody">Other cells...</property>
+                <property name="use-underline">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
 </interface>
diff --git a/svx/source/table/tablecontroller.cxx 
b/svx/source/table/tablecontroller.cxx
index 9b3d8687a855..84b6bd93f674 100644
--- a/svx/source/table/tablecontroller.cxx
+++ b/svx/source/table/tablecontroller.cxx
@@ -890,18 +890,34 @@ void SvxTableController::onSelect( sal_uInt16 nSId )
     gotoCell( aStart, true, nullptr );
 }
 
-namespace
+SvxBoxItem SvxTableController::TextDistancesToSvxBoxItem(const SfxItemSet& 
rAttrSet)
 {
-    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;
-    }
+    // 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::SvxBoxItemToTextDistances(const SvxBoxItem& 
pOriginalItem, SfxItemSet& rAttrSet)
+{
+    const SvxBoxItem* pNewItem( rAttrSet.GetItemIfSet( SDRATTR_TABLE_BORDER ) 
);
+    if ( !pNewItem )
+        return;
+
+    if( pNewItem->GetDistance( SvxBoxItemLine::LEFT ) != 
pOriginalItem.GetDistance( SvxBoxItemLine::LEFT ) )
+        rAttrSet.Put(makeSdrTextLeftDistItem( pNewItem->GetDistance( 
SvxBoxItemLine::LEFT ) ) );
+
+    if( pNewItem->GetDistance( SvxBoxItemLine::RIGHT ) != 
pOriginalItem.GetDistance( SvxBoxItemLine::RIGHT ) )
+        rAttrSet.Put(makeSdrTextRightDistItem( pNewItem->GetDistance( 
SvxBoxItemLine::RIGHT ) ) );
+
+    if( pNewItem->GetDistance( SvxBoxItemLine::TOP ) != 
pOriginalItem.GetDistance( SvxBoxItemLine::TOP ) )
+        rAttrSet.Put(makeSdrTextUpperDistItem( pNewItem->GetDistance( 
SvxBoxItemLine::TOP ) ) );
+
+    if( pNewItem->GetDistance( SvxBoxItemLine::BOTTOM ) != 
pOriginalItem.GetDistance( SvxBoxItemLine::BOTTOM ) )
+        rAttrSet.Put(makeSdrTextLowerDistItem( pNewItem->GetDistance( 
SvxBoxItemLine::BOTTOM ) ) );
 }
 
 void SvxTableController::onFormatTable(const SfxRequest& rReq)
@@ -919,7 +935,7 @@ void SvxTableController::onFormatTable(const SfxRequest& 
rReq)
     SfxItemSet aNewAttr(rModel.GetItemPool());
 
     // merge drawing layer text distance items into SvxBoxItem used by the 
dialog
-    SvxBoxItem aBoxItem(mergeDrawinglayerTextDistancesAndSvxBoxItem(aNewAttr));
+    SvxBoxItem aBoxItem(TextDistancesToSvxBoxItem(aNewAttr));
 
     SvxBoxInfoItem aBoxInfoItem( aNewAttr.Get( SDRATTR_TABLE_BORDER_INNER ) );
 
@@ -966,19 +982,7 @@ void SvxTableController::onFormatTable(const SfxRequest& 
rReq)
                 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 ) ) );
+            SvxBoxItemToTextDistances(aBoxItem, aNewSet);
 
             if (checkTableObject() && mxTable.is())
             {
@@ -3210,7 +3214,7 @@ void 
SvxTableController::FillCommonBorderAttrFromSelectedCells( SvxBoxItem& rBox
             nCellPosFlags |= (nCol > aEnd.mnCol)    ? CellPosFlag::After : 
CellPosFlag::NONE;
 
             const SfxItemSet& rSet = xCell->GetItemSet();
-            SvxBoxItem 
aCellBoxItem(mergeDrawinglayerTextDistancesAndSvxBoxItem(rSet));
+            SvxBoxItem aCellBoxItem(TextDistancesToSvxBoxItem(rSet));
             lcl_MergeCommonBorderAttr( aLinesState, aCellBoxItem, 
nCellPosFlags );
         }
     }
diff --git a/svx/source/table/tabledesign.cxx b/svx/source/table/tabledesign.cxx
index d4fe8cda4460..7c097199f9e9 100644
--- a/svx/source/table/tabledesign.cxx
+++ b/svx/source/table/tabledesign.cxx
@@ -23,10 +23,10 @@
 #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/XIndexReplace.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/XModifiable.hpp>
 #include <com/sun/star/util/XModifyListener.hpp>
 #include <com/sun/star/form/XReset.hpp>
 
@@ -60,7 +60,7 @@ namespace sdr::table {
 
 typedef std::map< OUString, sal_Int32 > CellStyleNameMap;
 
-typedef ::comphelper::WeakComponentImplHelper< XStyle, XNameReplace, 
XServiceInfo, XIndexAccess, XModifyBroadcaster, XModifyListener, XPropertySet > 
TableDesignStyleBase;
+typedef ::comphelper::WeakComponentImplHelper< XStyle, XNameReplace, 
XServiceInfo, XIndexReplace, XModifiable, XModifyListener, XPropertySet > 
TableDesignStyleBase;
 
 namespace {
 
@@ -97,6 +97,9 @@ public:
     virtual sal_Int32 SAL_CALL getCount() override ;
     virtual Any SAL_CALL getByIndex( sal_Int32 Index ) override;
 
+    // XIndexReplace
+    virtual void SAL_CALL replaceByIndex( sal_Int32 Index, const Any& Element 
) override;
+
     // XNameReplace
     virtual void SAL_CALL replaceByName( const OUString& aName, const Any& 
aElement ) override;
 
@@ -109,6 +112,10 @@ public:
     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;
 
+    // XModifiable
+    virtual sal_Bool SAL_CALL isModified() override;
+    virtual void SAL_CALL setModified( sal_Bool bModified ) override;
+
     // XModifyBroadcaster
     virtual void SAL_CALL addModifyListener( const Reference< XModifyListener 
>& aListener ) override;
     virtual void SAL_CALL removeModifyListener( const Reference< 
XModifyListener >& aListener ) override;
@@ -356,6 +363,21 @@ Any SAL_CALL TableDesignStyle::getByIndex( sal_Int32 Index 
)
 }
 
 
+// XIndexReplace
+
+void SAL_CALL TableDesignStyle::replaceByIndex( sal_Int32 Index, const Any& 
aElement )
+{
+    if( (Index < 0) || (Index >= style_count) )
+        throw IndexOutOfBoundsException();
+
+    const CellStyleNameMap& rMap = getCellStyleNameMap();
+    auto iter = std::find_if(rMap.begin(), rMap.end(),
+        [&Index](const auto& item) { return Index == item.second; });
+    if (iter != rMap.end())
+        replaceByName(iter->first, aElement);
+}
+
+
 // XNameReplace
 
 
@@ -450,6 +472,19 @@ void TableDesignStyle::removeVetoableChangeListener( const 
OUString&,const Refer
 {
 }
 
+// XModifiable
+
+sal_Bool TableDesignStyle::isModified()
+{
+    return mbModified;
+}
+
+void TableDesignStyle::setModified( sal_Bool bModified )
+{
+    mbModified = bModified;
+    notifyModifyListener();
+}
+
 
 // XModifyBroadcaster
 
commit 5d683e8f6c9e677d7b6bc829a4ce9ee384f4fbd8
Author:     Maxim Monastirsky <momonas...@gmail.com>
AuthorDate: Sun Nov 13 11:05:28 2022 +0200
Commit:     Maxim Monastirsky <momonas...@gmail.com>
CommitDate: Mon Nov 21 14:38:59 2022 +0100

    Export modified table styles
    
    In addition to actually used styles, we want to
    export user defined styles, as well as built in
    styles that were modified.
    
    Detecting the real modification state might be
    challenging in some cases, e.g. when importing
    the style from an existing document. So for
    simplicity, we consider a style as "modified"
    if at least one of its child cell styles is user
    defined. The UI for modifying cell styles will be
    implemented in such a way that built in styles
    will never be modified themselves. Instead, a
    user defined style will be created on the fly,
    with the corresponding built in style set as its
    parent, and only that new style will be modified.
    
    It is still possible to modify built in cell
    styles via the UNO API, but such styles won't be
    exported unless actually used in the document.
    
    Change-Id: Ic73eb460fd024f23016553e134397e07259f5c90
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143011
    Tested-by: Jenkins
    Reviewed-by: Maxim Monastirsky <momonas...@gmail.com>

diff --git a/sd/qa/unit/data/odg/tablestyles.fodg 
b/sd/qa/unit/data/odg/tablestyles.fodg
index 7b54e73955f9..3672ad8e4a70 100644
--- a/sd/qa/unit/data/odg/tablestyles.fodg
+++ b/sd/qa/unit/data/odg/tablestyles.fodg
@@ -2,6 +2,28 @@
 <office:document 
xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" 
xmlns:smil="urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0" 
xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" 
xmlns:css3t="http://www.w3.org/TR/css3-text/"; 
xmlns:grddl="http://www.w3.org/2003/g/data-view#"; 
xmlns:xhtml="http://www.w3.org/1999/xhtml"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xmlns:xsd="http://www.w3.org/2001/XMLSchema"; 
xmlns:xforms="http://www.w3.org/2002/xforms"; 
xmlns:dom="http://www.w3.org/2001/xml-events"; 
xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" 
xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" 
xmlns:math="http://www.w3.org/1998/Math/MathML"; 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:ooo="http://openoffice.org/2004/office"; 
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 
xmlns:ooow="http://openoffice.org/2004/writer"; 
xmlns:xlink="http://www.w3.org/1999/xli
 nk" xmlns:drawooo="http://openoffice.org/2010/draw"; 
xmlns:oooc="http://openoffice.org/2004/calc"; 
xmlns:dc="http://purl.org/dc/elements/1.1/"; 
xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0"
 xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" 
xmlns:tableooo="http://openoffice.org/2009/table"; 
xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" 
xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" 
xmlns:rpt="http://openoffice.org/2005/report"; 
xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0"
 xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" 
xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" 
xmlns:officeooo="http://openoffice.org/2009/office"; 
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" 
xmlns:field="urn:openoffice:names:experimental:o
 oo-ms-interop:xmlns:field:1.0" 
xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" 
xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" 
xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0"
 office:version="1.3" 
office:mimetype="application/vnd.oasis.opendocument.graphics">
   <office:scripts/>
   <office:font-face-decls/>
+  <office:styles>
+    <style:style style:name="default" style:family="table-cell"/>
+    <style:style style:name="userdefined" style:family="table-cell"/>
+    <table:table-template table:name="default">
+      <table:first-row table:style-name="userdefined"/>
+      <table:last-row table:style-name="userdefined"/>
+      <table:first-column table:style-name="userdefined"/>
+      <table:last-column table:style-name="userdefined"/>
+      <table:body table:style-name="userdefined"/>
+      <table:odd-rows table:style-name="userdefined"/>
+      <table:odd-columns table:style-name="userdefined"/>
+    </table:table-template>
+    <table:table-template table:name="userdefined">
+      <table:first-row table:style-name="default"/>
+      <table:last-row table:style-name="default"/>
+      <table:first-column table:style-name="default"/>
+      <table:last-column table:style-name="default"/>
+      <table:body table:style-name="default"/>
+      <table:odd-rows table:style-name="default"/>
+      <table:odd-columns table:style-name="default"/>
+    </table:table-template>
+  </office:styles>
   <office:automatic-styles>
     <style:page-layout style:name="PM0">
       <style:page-layout-properties fo:margin-top="1cm" fo:margin-bottom="1cm" 
fo:margin-left="1cm" fo:margin-right="1cm" fo:page-width="21.59cm" 
fo:page-height="27.94cm" style:print-orientation="portrait"/>
diff --git a/sd/qa/unit/export-tests.cxx b/sd/qa/unit/export-tests.cxx
index 288e9acacf99..7d7f5c747cf9 100644
--- a/sd/qa/unit/export-tests.cxx
+++ b/sd/qa/unit/export-tests.cxx
@@ -102,6 +102,7 @@ public:
     void testColumnsODG();
     void testTdf112126();
     void testCellProperties();
+    void testUserTableStyles();
 
     CPPUNIT_TEST_SUITE(SdExportTest);
 
@@ -151,6 +152,7 @@ public:
     CPPUNIT_TEST(testColumnsODG);
     CPPUNIT_TEST(testTdf112126);
     CPPUNIT_TEST(testCellProperties);
+    CPPUNIT_TEST(testUserTableStyles);
     CPPUNIT_TEST_SUITE_END();
 
     virtual void registerNamespaces(xmlXPathContextPtr& pXmlXPathCtx) override
@@ -1767,6 +1769,11 @@ void SdExportTest::testTdf112126()
 
 void SdExportTest::testCellProperties()
 {
+    // Silence unrelated failure:
+    // Error: element "table:table-template" is missing 
"first-row-start-column" attribute
+    // Looks like an oversight in the schema, as the docs claim this attribute 
is deprecated.
+    skipValidation();
+
     loadFromURL(u"odg/tablestyles.fodg");
     saveAndReload("draw8");
 
@@ -1792,6 +1799,34 @@ void SdExportTest::testCellProperties()
     
CPPUNIT_ASSERT_EQUAL(drawing::TextVerticalAdjust::TextVerticalAdjust_CENTER, 
aTextAdjust);
 }
 
+void SdExportTest::testUserTableStyles()
+{
+    // Silence unrelated failure:
+    // Error: element "table:table-template" is missing 
"first-row-start-column" attribute
+    // Looks like an oversight in the schema, as the docs claim this attribute 
is deprecated.
+    skipValidation();
+
+    loadFromURL(u"odg/tablestyles.fodg");
+    saveAndReload("draw8");
+
+    uno::Reference<style::XStyleFamiliesSupplier> 
xStyleFamiliesSupplier(mxComponent,
+                                                                         
uno::UNO_QUERY);
+    uno::Reference<container::XNameAccess> xStyleFamily(
+        xStyleFamiliesSupplier->getStyleFamilies()->getByName("table"), 
uno::UNO_QUERY);
+
+    uno::Reference<style::XStyle> 
xTableStyle(xStyleFamily->getByName("default"), uno::UNO_QUERY);
+    CPPUNIT_ASSERT(!xTableStyle->isUserDefined());
+
+    uno::Reference<container::XNameAccess> xNameAccess(xTableStyle, 
uno::UNO_QUERY);
+    uno::Reference<style::XStyle> 
xCellStyle(xNameAccess->getByName("first-row"), uno::UNO_QUERY);
+    CPPUNIT_ASSERT(xCellStyle);
+    CPPUNIT_ASSERT_EQUAL(OUString("userdefined"), xCellStyle->getName());
+
+    CPPUNIT_ASSERT(xStyleFamily->hasByName("userdefined"));
+    xTableStyle.set(xStyleFamily->getByName("userdefined"), uno::UNO_QUERY);
+    CPPUNIT_ASSERT(xTableStyle->isUserDefined());
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(SdExportTest);
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sd/source/core/drawdoc4.cxx b/sd/source/core/drawdoc4.cxx
index a4e1eb5a0c7c..0b8898e3095b 100644
--- a/sd/source/core/drawdoc4.cxx
+++ b/sd/source/core/drawdoc4.cxx
@@ -21,6 +21,7 @@
 
 #include <com/sun/star/style/XStyle.hpp>
 #include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/form/XReset.hpp>
 #include <i18nlangtag/languagetag.hxx>
 #include <i18nlangtag/mslangid.hxx>
 #include <sfx2/dispatch.hxx>
@@ -785,6 +786,10 @@ void SdDrawDocument::CreateDefaultCellStyles()
     Any aYellow3( implMakeSolidCellStyle( pSSPool, "yellow3" , 
aDefaultCellStyleName, Color(255,204,153)));
 
     implCreateTableTemplate( xTableFamily, "yellow" , aYellow1, aYellow3, 
aYellow2 );
+
+    Reference<form::XReset> xReset(xTableFamily, UNO_QUERY);
+    if (xReset)
+        xReset->reset();
 }
 
 // Number of pages that reference a master page
diff --git a/sd/source/core/stlsheet.cxx b/sd/source/core/stlsheet.cxx
index 71cf76709ed1..14a0b13dd15c 100644
--- a/sd/source/core/stlsheet.cxx
+++ b/sd/source/core/stlsheet.cxx
@@ -309,6 +309,15 @@ bool SdStyleSheet::IsUsed() const
             bResult = std::any_of(aModifyListeners.begin(), 
aModifyListeners.end(),
                 [](const Reference<XInterface>& rListener) {
                     Reference< XStyle > xStyle( rListener, UNO_QUERY );
+                    try
+                    {
+                        Reference<XPropertySet> xPropertySet(xStyle, 
UNO_QUERY_THROW);
+                        if 
(xPropertySet->getPropertyValue("IsPhysical").get<bool>())
+                            return true;
+                    }
+                    catch (const Exception&)
+                    {
+                    }
                     return xStyle.is() && xStyle->isInUse();
                 });
         }
diff --git a/svx/source/table/tabledesign.cxx b/svx/source/table/tabledesign.cxx
index ec316972232d..d4fe8cda4460 100644
--- a/svx/source/table/tabledesign.cxx
+++ b/svx/source/table/tabledesign.cxx
@@ -28,6 +28,7 @@
 #include <com/sun/star/beans/XPropertySet.hpp>
 #include <com/sun/star/util/XModifyBroadcaster.hpp>
 #include <com/sun/star/util/XModifyListener.hpp>
+#include <com/sun/star/form/XReset.hpp>
 
 #include <vcl/svapp.hxx>
 
@@ -47,6 +48,7 @@
 #include <map>
 
 
+using namespace css;
 using namespace ::com::sun::star::uno;
 using namespace ::com::sun::star::style;
 using namespace ::com::sun::star::lang;
@@ -58,7 +60,7 @@ namespace sdr::table {
 
 typedef std::map< OUString, sal_Int32 > CellStyleNameMap;
 
-typedef ::comphelper::WeakComponentImplHelper< XStyle, XNameReplace, 
XServiceInfo, XIndexAccess, XModifyBroadcaster, XModifyListener > 
TableDesignStyleBase;
+typedef ::comphelper::WeakComponentImplHelper< XStyle, XNameReplace, 
XServiceInfo, XIndexAccess, XModifyBroadcaster, XModifyListener, XPropertySet > 
TableDesignStyleBase;
 
 namespace {
 
@@ -98,6 +100,15 @@ public:
     // XNameReplace
     virtual void SAL_CALL replaceByName( const OUString& aName, const Any& 
aElement ) 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;
+
     // XModifyBroadcaster
     virtual void SAL_CALL addModifyListener( const Reference< XModifyListener 
>& aListener ) override;
     virtual void SAL_CALL removeModifyListener( const Reference< 
XModifyListener >& aListener ) override;
@@ -107,12 +118,14 @@ public:
     virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) 
override;
 
     void notifyModifyListener();
+    void resetUserDefined();
 
     // this function is called upon disposing the component
     virtual void disposing(std::unique_lock<std::mutex>& aGuard) override;
 
     static const CellStyleNameMap& getCellStyleNameMap();
 
+    bool mbUserDefined, mbModified;
     OUString msName;
     Reference< XStyle > maCellStyles[style_count];
     comphelper::OInterfaceContainerHelper4<XModifyListener> maModifyListeners;
@@ -124,7 +137,7 @@ typedef std::vector< Reference< XStyle > > 
TableDesignStyleVector;
 
 namespace {
 
-class TableDesignFamily : public ::cppu::WeakImplHelper< XNameContainer, 
XNamed, XIndexAccess, XSingleServiceFactory,  XServiceInfo, XComponent, 
XPropertySet >
+class TableDesignFamily : public ::cppu::WeakImplHelper< XNameContainer, 
XNamed, XIndexAccess, XSingleServiceFactory,  XServiceInfo, XComponent, 
XPropertySet, form::XReset >
 {
 public:
     // XServiceInfo
@@ -174,12 +187,19 @@ public:
     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;
 
+    // XReset
+    virtual void SAL_CALL reset() override;
+    virtual void SAL_CALL addResetListener( const 
Reference<form::XResetListener>& aListener ) override;
+    virtual void SAL_CALL removeResetListener( const 
Reference<form::XResetListener>& aListener ) override;
+
     TableDesignStyleVector  maDesigns;
 };
 
 }
 
 TableDesignStyle::TableDesignStyle()
+    : mbUserDefined(true)
+    , mbModified(false)
 {
 }
 
@@ -221,7 +241,12 @@ Sequence< OUString > SAL_CALL 
TableDesignStyle::getSupportedServiceNames()
 // XStyle
 sal_Bool SAL_CALL TableDesignStyle::isUserDefined()
 {
-    return false;
+    return mbUserDefined;
+}
+
+void TableDesignStyle::resetUserDefined()
+{
+    mbUserDefined = false;
 }
 
 sal_Bool SAL_CALL TableDesignStyle::isInUse()
@@ -367,6 +392,9 @@ void SAL_CALL TableDesignStyle::replaceByName( const 
OUString& rName, const Any&
     if( xNewBroadcaster.is() )
         xNewBroadcaster->addModifyListener( xListener );
 
+    if (xNewStyle && xNewStyle->isUserDefined())
+        mbModified = true;
+
     maCellStyles[nIndex] = xNewStyle;
 }
 
@@ -387,6 +415,41 @@ void 
TableDesignStyle::disposing(std::unique_lock<std::mutex>& aGuard)
     }
 }
 
+// XPropertySet
+
+Reference<XPropertySetInfo> TableDesignStyle::getPropertySetInfo()
+{
+    return {};
+}
+
+void TableDesignStyle::setPropertyValue( const OUString&, const Any& )
+{
+}
+
+Any TableDesignStyle::getPropertyValue( const OUString& PropertyName )
+{
+    if (PropertyName != "IsPhysical")
+        throw UnknownPropertyException("unknown property: " + PropertyName, 
static_cast<OWeakObject *>(this));
+
+    return Any(mbModified || mbUserDefined);
+}
+
+void TableDesignStyle::addPropertyChangeListener( const OUString&, const 
Reference<XPropertyChangeListener>& )
+{
+}
+
+void TableDesignStyle::removePropertyChangeListener( const OUString&, const 
Reference<XPropertyChangeListener>& )
+{
+}
+
+void TableDesignStyle::addVetoableChangeListener( const OUString&, const 
Reference<XVetoableChangeListener>& )
+{
+}
+
+void TableDesignStyle::removeVetoableChangeListener( const OUString&,const 
Reference<XVetoableChangeListener>& )
+{
+}
+
 
 // XModifyBroadcaster
 
@@ -602,6 +665,9 @@ void SAL_CALL TableDesignFamily::replaceByName( const 
OUString& rName, const Any
         [&rName](const Reference<XStyle>& rpStyle) { return rpStyle->getName() 
== rName; });
     if (iter != maDesigns.end())
     {
+        if (!(*iter)->isUserDefined())
+            static_cast<TableDesignStyle*>(xStyle.get())->resetUserDefined();
+
         Reference<XComponent> xComponent(*iter, UNO_QUERY);
         if (xComponent)
             xComponent->dispose();
@@ -707,6 +773,21 @@ void TableDesignFamily::removeVetoableChangeListener( 
const OUString& , const Re
     OSL_FAIL( "###unexpected!" );
 }
 
+// XReset
+
+void TableDesignFamily::reset()
+{
+    for (const auto& aDesign : maDesigns)
+        static_cast<TableDesignStyle*>(aDesign.get())->resetUserDefined();
+}
+
+void TableDesignFamily::addResetListener( const 
Reference<form::XResetListener>& )
+{
+}
+
+void TableDesignFamily::removeResetListener( const 
Reference<form::XResetListener>& )
+{
+}
 
 Reference< XNameAccess > CreateTableDesignFamily()
 {
diff --git a/xmloff/source/table/XMLTableExport.cxx 
b/xmloff/source/table/XMLTableExport.cxx
index 54bfb268d054..f415bb171af9 100644
--- a/xmloff/source/table/XMLTableExport.cxx
+++ b/xmloff/source/table/XMLTableExport.cxx
@@ -598,14 +598,24 @@ void XMLTableExport::exportTableTemplates()
             SvtSaveOptions::ODFSaneDefaultVersion eVersion = 
mrExport.getSaneDefaultVersion();
 
             Reference< XStyle > xTableStyle( xTableFamily->getByIndex( nIndex 
), UNO_QUERY_THROW );
-            if( !xTableStyle->isInUse() )
+            Reference<XPropertySet> xTableStylePropSet( xTableStyle, 
UNO_QUERY_THROW );
+            bool bPhysical = false;
+
+            try
+            {
+                xTableStylePropSet->getPropertyValue("IsPhysical") >>= 
bPhysical;
+            }
+            catch(const Exception&)
+            {
+            }
+
+            if (!xTableStyle->isInUse() && !bPhysical)
                 continue;
 
             const TableStyleElement* pElements;
             if (mbWriter)
             {
                 mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, 
xTableStyle->getName());
-                Reference<XPropertySet> xTableStylePropSet(xTableStyle, 
UNO_QUERY_THROW);
                 pElements = getWriterSpecificTableStyleAttributes();
                 while(pElements->meElement != XML_TOKEN_END)
                 {

Reply via email to