From 8e00addabf26c9a1a8408c06a4e1467a3d4cfdef Mon Sep 17 00:00:00 2001 From: tetektoza Date: Tue, 19 Aug 2025 12:39:18 +0200 Subject: [PATCH] Core: Allow deleting objects in group recursively Add a special marker that will signal to VPs that they should delete their children recursively under the group. --- src/Gui/ViewProviderGroupExtension.cpp | 109 +++++++++++++++++++++---- 1 file changed, 94 insertions(+), 15 deletions(-) diff --git a/src/Gui/ViewProviderGroupExtension.cpp b/src/Gui/ViewProviderGroupExtension.cpp index c8f24853b6..2d08b8d9f4 100644 --- a/src/Gui/ViewProviderGroupExtension.cpp +++ b/src/Gui/ViewProviderGroupExtension.cpp @@ -27,12 +27,14 @@ #include #include +#include #include #include #include #include "ViewProviderGroupExtension.h" #include "ViewProviderDocumentObject.h" +#include "Application.h" #include "Command.h" #include "Document.h" #include "MainWindow.h" @@ -40,6 +42,51 @@ using namespace Gui; +namespace { + // helper function to recursively delete group contents while respecting view provider onDelete methods + void deleteGroupContentsRecursively(App::GroupExtension* group, ViewProvider* groupViewProvider) { + if (!group) { + return; + } + + std::vector children = group->Group.getValues(); + + for (App::DocumentObject* child : children) { + if (!child || !child->isAttachedToDocument() || child->isRemoving()) { + continue; + } + + // if the child is a group, recursively delete its contents first + if (child->hasExtension(App::GroupExtension::getExtensionClassTypeId())) { + auto* childGroup = child->getExtensionByType(); + Gui::Document* guiDoc = Application::Instance->getDocument(child->getDocument()); + ViewProvider* childVP = guiDoc ? guiDoc->getViewProvider(child) : nullptr; + deleteGroupContentsRecursively(childGroup, childVP); + } + + Gui::Document* guiDoc = Application::Instance->getDocument(child->getDocument()); + if (guiDoc) { + ViewProvider* vp = guiDoc->getViewProvider(child); + if (vp) { + // give group_recursive_deletion marker to the VP to mark that the deletion + // is supposed to delete all of it's children + std::vector groupDeletionMarker = {"group_recursive_deletion"}; + bool shouldDelete = vp->onDelete(groupDeletionMarker); + + if (!shouldDelete) { + return; + } + } + } + + // if the object still exists and wasn't deleted by its view provider, delete it directly + if (child->isAttachedToDocument() && !child->isRemoving()) { + child->getDocument()->removeObject(child->getNameInDocument()); + } + } + } +} + EXTENSION_PROPERTY_SOURCE(Gui::ViewProviderGroupExtension, Gui::ViewProviderExtension) ViewProviderGroupExtension::ViewProviderGroupExtension() @@ -170,24 +217,56 @@ void ViewProviderGroupExtension::extensionHide() { ViewProviderExtension::extensionHide(); } -bool ViewProviderGroupExtension::extensionOnDelete(const std::vector< std::string >& ) { +bool ViewProviderGroupExtension::extensionOnDelete(const std::vector< std::string >& subNames) { auto* group = getExtendedViewProvider()->getObject()->getExtensionByType(); - // If the group is nonempty ask the user if they want to delete its content - if (group->Group.getSize() > 0) { - QMessageBox::StandardButton choice = - QMessageBox::question(getMainWindow(), QObject::tr ( "Delete group content?" ), - QObject::tr ( "The %1 is not empty, delete its content as well?") - .arg ( QString::fromUtf8 ( getExtendedViewProvider()->getObject()->Label.getValue () ) ), - QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes ); - - if (choice == QMessageBox::Yes) { - Gui::Command::doCommand(Gui::Command::Doc, - "App.getDocument(\"%s\").getObject(\"%s\").removeObjectsFromDocument()" - , getExtendedViewProvider()->getObject()->getDocument()->getName() - , getExtendedViewProvider()->getObject()->getNameInDocument()); - } + + std::vector directChildren = group->Group.getValues(); + + // just delete without messagebox if group is empty + if (directChildren.empty()) { + return true; } + + std::vector allDescendants; + if (getExtendedViewProvider()->getObject()->isDerivedFrom()) { + auto* docGroup = static_cast(getExtendedViewProvider()->getObject()); + allDescendants = docGroup->getAllChildren(); + } else { + allDescendants = directChildren; + } + + QString message; + if (allDescendants.size() == directChildren.size()) { + message = QObject::tr("The group '%1' contains %2 object(s). Do you want to delete them as well?") + .arg(QString::fromUtf8(getExtendedViewProvider()->getObject()->Label.getValue())) + .arg(allDescendants.size()); + } else { + // if we have nested groups + message = QObject::tr("The group '%1' contains %2 direct children and %3 total descendants (including nested groups). Do you want to delete all of them recursively?") + .arg(QString::fromUtf8(getExtendedViewProvider()->getObject()->Label.getValue())) + .arg(directChildren.size()) + .arg(allDescendants.size()); + } + + QMessageBox::StandardButton choice = QMessageBox::question( + getMainWindow(), + QObject::tr("Delete group contents recursively?"), + message, + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, + QMessageBox::Yes + ); + + if (choice == QMessageBox::Cancel) { + // don't delete anything if user has cancelled + return false; + } + else if (choice == QMessageBox::Yes) { + // delete all of the children recursively and call their viewprovider method + deleteGroupContentsRecursively(group, getExtendedViewProvider()); + } + // if user has specified "No" then delete the group but move children to the parent or root + return true; }