SmartArt Edit UI: remove node
Removes data node from data model including associated presentation nodes,
transition nodes and all connections between them (child-parent and
presentation-of).
It still doesn't update order of remaining connections after removal, so
empty entries can happen. Additional step is needed or using better data
structures.
Change-Id: I96e0752b6ec5a19ae8e972dbd421314e6c442b53
Reviewed-on: https://gerrit.libreoffice.org/79279
Tested-by: Jenkins
Reviewed-by: Grzegorz Araminowicz <grzegorz.araminowicz@collabora.com>
diff --git a/cui/source/dialogs/DiagramDialog.cxx b/cui/source/dialogs/DiagramDialog.cxx
index 4891ef9..f3a4a06 100644
--- a/cui/source/dialogs/DiagramDialog.cxx
+++ b/cui/source/dialogs/DiagramDialog.cxx
@@ -20,10 +20,12 @@ DiagramDialog::DiagramDialog(weld::Window* pWindow,
, mpBtnOk(m_xBuilder->weld_button("btnOk"))
, mpBtnCancel(m_xBuilder->weld_button("btnCancel"))
, mpBtnAdd(m_xBuilder->weld_button("btnAdd"))
, mpBtnRemove(m_xBuilder->weld_button("btnRemove"))
, mpTreeDiagram(m_xBuilder->weld_tree_view("treeDiagram"))
, mpTextAdd(m_xBuilder->weld_text_view("textAdd"))
{
mpBtnAdd->connect_clicked(LINK(this, DiagramDialog, OnAddClick));
mpBtnRemove->connect_clicked(LINK(this, DiagramDialog, OnRemoveClick));
populateTree(nullptr, OUString());
@@ -40,15 +42,28 @@ IMPL_LINK_NOARG(DiagramDialog, OnAddClick, weld::Button&, void)
OUString sText = mpTextAdd->get_text();
if (!sText.isEmpty())
{
OUString sNodeId = mpDiagramData->addNode(sText);
std::unique_ptr<weld::TreeIter> pEntry(mpTreeDiagram->make_iterator());
mpTreeDiagram->insert(nullptr, -1, &sText, nullptr, nullptr, nullptr, nullptr, false,
mpTreeDiagram->insert(nullptr, -1, &sText, &sNodeId, nullptr, nullptr, nullptr, false,
pEntry.get());
mpTreeDiagram->select(*pEntry);
mpDiagramData->addNode(sText);
comphelper::dispatchCommand(".uno:RegenerateDiagram", {});
}
}
IMPL_LINK_NOARG(DiagramDialog, OnRemoveClick, weld::Button&, void)
{
std::unique_ptr<weld::TreeIter> pEntry(mpTreeDiagram->make_iterator());
if (mpTreeDiagram->get_selected(pEntry.get()))
{
if (mpDiagramData->removeNode(mpTreeDiagram->get_id(*pEntry)))
{
mpTreeDiagram->remove(*pEntry);
comphelper::dispatchCommand(".uno:RegenerateDiagram", {});
}
}
}
void DiagramDialog::populateTree(const weld::TreeIter* pParent, const OUString& rParentId)
{
auto aItems = mpDiagramData->getChildren(rParentId);
diff --git a/cui/source/inc/DiagramDialog.hxx b/cui/source/inc/DiagramDialog.hxx
index 461ffee..e971440 100644
--- a/cui/source/inc/DiagramDialog.hxx
+++ b/cui/source/inc/DiagramDialog.hxx
@@ -27,10 +27,12 @@ private:
std::unique_ptr<weld::Button> mpBtnOk;
std::unique_ptr<weld::Button> mpBtnCancel;
std::unique_ptr<weld::Button> mpBtnAdd;
std::unique_ptr<weld::Button> mpBtnRemove;
std::unique_ptr<weld::TreeView> mpTreeDiagram;
std::unique_ptr<weld::TextView> mpTextAdd;
DECL_LINK(OnAddClick, weld::Button&, void);
DECL_LINK(OnRemoveClick, weld::Button&, void);
void populateTree(const weld::TreeIter* pParent, const OUString& rParentId);
};
diff --git a/cui/uiconfig/ui/diagramdialog.ui b/cui/uiconfig/ui/diagramdialog.ui
index 98a6771..c05031f1 100644
--- a/cui/uiconfig/ui/diagramdialog.ui
+++ b/cui/uiconfig/ui/diagramdialog.ui
@@ -129,6 +129,20 @@
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="btnRemove">
<property name="label">gtk-remove</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
diff --git a/include/svx/DiagramDataInterface.hxx b/include/svx/DiagramDataInterface.hxx
index aaa3a46..f64dc3a 100644
--- a/include/svx/DiagramDataInterface.hxx
+++ b/include/svx/DiagramDataInterface.hxx
@@ -38,8 +38,10 @@ public:
virtual std::vector<std::pair<OUString, OUString>>
getChildren(const OUString& rParentId) const = 0;
// add new top-level node to data model
virtual void addNode(const OUString& rText) = 0;
// add new top-level node to data model, returns its id
virtual OUString addNode(const OUString& rText) = 0;
virtual bool removeNode(const OUString& rNodeId) = 0;
protected:
~DiagramDataInterface() throw() {}
diff --git a/oox/source/drawingml/diagram/datamodel.cxx b/oox/source/drawingml/diagram/datamodel.cxx
index 1f1794b..6b7d05e 100644
--- a/oox/source/drawingml/diagram/datamodel.cxx
+++ b/oox/source/drawingml/diagram/datamodel.cxx
@@ -29,6 +29,7 @@
#include <svx/DiagramDataInterface.hxx>
#include <comphelper/xmltools.hxx>
#include <unordered_set>
#include <iostream>
#include <fstream>
@@ -137,6 +138,12 @@ std::vector<std::pair<OUString, OUString>> DiagramData::getChildren(const OUStri
pChild->second->msModelId,
pChild->second->mpShape->getTextBody()->getParagraphs().front()->getRuns().front()->getText());
}
// HACK: empty items shouldn't appear there
aChildren.erase(std::remove_if(aChildren.begin(), aChildren.end(),
[](const std::pair<OUString, OUString>& aItem) { return aItem.first.isEmpty(); }),
aChildren.end());
return aChildren;
}
@@ -154,7 +161,7 @@ void DiagramData::addConnection(sal_Int32 nType, const OUString& sSourceId, cons
rCxn.mnSourceOrder = nMaxOrd + 1;
}
void DiagramData::addNode(const OUString& rText)
OUString DiagramData::addNode(const OUString& rText)
{
const dgm::Point& rDataRoot = *getRootPoint();
OUString sPresRoot;
@@ -163,11 +170,13 @@ void DiagramData::addNode(const OUString& rText)
sPresRoot = aCxn.msDestId;
if (sPresRoot.isEmpty())
return;
return OUString();
OUString sNewNodeId = OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8);
dgm::Point aDataPoint;
aDataPoint.mnType = XML_node;
aDataPoint.msModelId = OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8);
aDataPoint.msModelId = sNewNodeId;
aDataPoint.mpShape.reset(new Shape());
aDataPoint.mpShape->setTextBody(std::make_shared<TextBody>());
TextRunPtr pTextRun(new TextRun());
@@ -208,6 +217,59 @@ void DiagramData::addNode(const OUString& rText)
maPoints.push_back(aPresPoint);
build();
return sNewNodeId;
}
bool DiagramData::removeNode(const OUString& rNodeId)
{
// check if it doesn't have children
for (const auto& aCxn : maConnections)
if (aCxn.mnType == XML_parOf && aCxn.msSourceId == rNodeId)
{
SAL_WARN("oox.drawingml", "Node has children - can't be removed");
return false;
}
dgm::Connection aParCxn;
for (const auto& aCxn : maConnections)
if (aCxn.mnType == XML_parOf && aCxn.msDestId == rNodeId)
aParCxn = aCxn;
std::unordered_set<OUString> aIdsToRemove;
aIdsToRemove.insert(rNodeId);
if (!aParCxn.msParTransId.isEmpty())
aIdsToRemove.insert(aParCxn.msParTransId);
if (!aParCxn.msSibTransId.isEmpty())
aIdsToRemove.insert(aParCxn.msSibTransId);
for (const dgm::Point& rPoint : maPoints)
if (aIdsToRemove.count(rPoint.msPresentationAssociationId))
aIdsToRemove.insert(rPoint.msModelId);
// instert also transition nodes
for (const auto& aCxn : maConnections)
if (aIdsToRemove.count(aCxn.msSourceId) || aIdsToRemove.count(aCxn.msDestId))
if (!aCxn.msPresId.isEmpty())
aIdsToRemove.insert(aCxn.msPresId);
// remove connections
maConnections.erase(std::remove_if(maConnections.begin(), maConnections.end(),
[aIdsToRemove](const dgm::Connection& rCxn) {
return aIdsToRemove.count(rCxn.msSourceId) || aIdsToRemove.count(rCxn.msDestId);
}),
maConnections.end());
// remove data and presentation nodes
maPoints.erase(std::remove_if(maPoints.begin(), maPoints.end(),
[aIdsToRemove](const dgm::Point& rPoint) {
return aIdsToRemove.count(rPoint.msModelId);
}),
maPoints.end());
// TODO: fix source/dest order
build();
return true;
}
#ifdef DEBUG_OOX_DIAGRAM
diff --git a/oox/source/drawingml/diagram/datamodel.hxx b/oox/source/drawingml/diagram/datamodel.hxx
index b4c7ce7..7f7f0f4 100644
--- a/oox/source/drawingml/diagram/datamodel.hxx
+++ b/oox/source/drawingml/diagram/datamodel.hxx
@@ -182,7 +182,8 @@ public:
void dump() const;
OUString getString() const override;
std::vector<std::pair<OUString, OUString>> getChildren(const OUString& rParentId) const override;
void addNode(const OUString& rText) override;
OUString addNode(const OUString& rText) override;
bool removeNode(const OUString& rNodeId) override;
private:
void getChildrenString(OUStringBuffer& rBuf, const dgm::Point* pPoint, sal_Int32 nLevel) const;