Gui: Allow a selection gate per document

This commit is contained in:
theo-vt
2026-02-21 12:52:57 -05:00
parent 4ba5504e7a
commit d59471b6bd
2 changed files with 83 additions and 57 deletions
+65 -51
View File
@@ -62,6 +62,7 @@ using namespace Gui;
using namespace std;
namespace sp = std::placeholders;
// TODO-theo-vt remove!
SelectionGateFilterExternal::SelectionGateFilterExternal(const char* docName, const char* objName)
{
if (docName) {
@@ -104,10 +105,6 @@ SelectionObserver::SelectionObserver(const ViewProviderDocumentObject* vp, bool
: resolve(resolve)
, blockedSelection(false)
{
if (vp && vp->getObject() && vp->getObject()->getDocument()) {
filterDocName = vp->getObject()->getDocument()->getName();
filterObjName = vp->getObject()->getNameInDocument();
}
if (attach) {
attachSelection();
}
@@ -149,12 +146,6 @@ void SelectionObserver::attachSelection()
std::bind(&SelectionObserver::_onSelectionChanged, this, sp::_1)
);
// NOLINTEND
if (!filterDocName.empty()) {
Selection().addSelectionGate(
new SelectionGateFilterExternal(filterDocName.c_str(), filterObjName.c_str())
);
}
}
}
@@ -182,9 +173,6 @@ void SelectionObserver::detachSelection()
{
if (connectSelection.connected()) {
connectSelection.disconnect();
if (!filterDocName.empty()) {
Selection().rmvSelectionGate();
}
}
}
@@ -827,24 +815,29 @@ int SelectionSingleton::setPreselect(
)
{
if (!pDocName || !pObjectName) {
rmvPreselect();
rmvPreselect(); // Invalid request
return 0;
}
if (!pSubName) {
pSubName = "";
}
if (DocName == pDocName && FeatName == pObjectName && SubName == pSubName) {
return -1;
return -1; // Already pre-selected
}
rmvPreselect();
if (ActiveGate && signal != SelectionChanges::MsgSource::Internal) {
App::Document* pDoc = getDocument(pDocName);
if (!pDoc || !pObjectName) {
return 0;
}
App::Document* pDoc = getDocument(pDocName);
if (!pDoc) {
return 0; // Invalid request
}
auto foundGate = docSelectionGate.find(pDoc);
if (foundGate != docSelectionGate.end() && signal != SelectionChanges::MsgSource::Internal) {
SelectionGate* gate = foundGate->second;
App::ElementNamePair elementName;
auto pObject = pDoc->getObject(pObjectName);
if (!pObject) {
@@ -852,14 +845,14 @@ int SelectionSingleton::setPreselect(
}
const char* subelement = pSubName;
if (gateResolve != ResolveMode::NoResolve) {
if (gate->resolveMode != ResolveMode::NoResolve) {
auto& newElementName = elementName.newName;
auto& oldElementName = elementName.oldName;
pObject = App::GeoFeature::resolveElement(pObject, pSubName, elementName);
if (!pObject) {
return 0;
}
if (gateResolve > ResolveMode::OldStyleElement) {
if (gate->resolveMode > ResolveMode::OldStyleElement) {
subelement = !newElementName.empty() ? newElementName.c_str()
: oldElementName.c_str();
}
@@ -867,10 +860,10 @@ int SelectionSingleton::setPreselect(
subelement = oldElementName.c_str();
}
}
if (!ActiveGate->allow(pObject->getDocument(), pObject, subelement)) {
if (!gate->allow(pObject->getDocument(), pObject, subelement)) {
QString msg;
if (ActiveGate->notAllowedReason.length() > 0) {
msg = QObject::tr(ActiveGate->notAllowedReason.c_str());
if (gate->notAllowedReason.length() > 0) {
msg = QObject::tr(gate->notAllowedReason.c_str());
}
else {
msg = QCoreApplication::translate("SelectionFilter", "Not allowed:");
@@ -1058,7 +1051,9 @@ void SelectionSingleton::rmvPreselect(bool signal)
hy = 0;
hz = 0;
if (ActiveGate && getMainWindow()) {
App::Document* activeDoc = getDocument(nullptr);
auto foundGate = docSelectionGate.find(activeDoc);
if (foundGate != docSelectionGate.end() && getMainWindow()) {
Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView();
mdi->restoreOverrideCursor();
}
@@ -1075,33 +1070,42 @@ const SelectionChanges& SelectionSingleton::getPreselection() const
}
// add a SelectionGate to control what is selectable
void SelectionSingleton::addSelectionGate(Gui::SelectionGate* gate, ResolveMode resolve)
void SelectionSingleton::addSelectionGate(Gui::SelectionGate* gate, ResolveMode resolve, const char* pDocName)
{
if (ActiveGate) {
rmvSelectionGate();
App::Document* doc = getDocument(pDocName);
if (!doc) {
return;
}
rmvSelectionGate(doc);
ActiveGate = gate;
gateResolve = resolve;
gate->resolveMode = resolve;
docSelectionGate[doc] = gate;
}
// remove the active SelectionGate
void SelectionSingleton::rmvSelectionGate()
void SelectionSingleton::rmvSelectionGate(App::Document* doc)
{
if (ActiveGate) {
delete ActiveGate;
ActiveGate = nullptr;
auto foundGate = docSelectionGate.find(doc);
if (foundGate != docSelectionGate.end()) {
delete foundGate->second;
docSelectionGate.erase(foundGate);
Gui::Document* doc = Gui::Application::Instance->activeDocument();
if (doc) {
// if a document is about to be closed it has no MDI view any more
Gui::MDIView* mdi = doc->getActiveView();
if (mdi) {
// if a document is about to be closed it has no MDI view any more
if (Gui::Document* guiDoc = Gui::Application::Instance->getDocument(doc)) {
if (Gui::MDIView* mdi = guiDoc->getActiveView()) {
mdi->restoreOverrideCursor();
}
}
}
}
void SelectionSingleton::rmvSelectionGate(const char* pDocName)
{
App::Document* doc = getDocument(pDocName);
if (!doc) {
return;
}
rmvSelectionGate(doc);
}
App::Document* SelectionSingleton::getDocument(const char* pDocName) const
@@ -1217,16 +1221,26 @@ bool SelectionSingleton::addSelection(
temp.y = y;
temp.z = z;
App::Document* doc = getDocument(pDocName);
if (!doc) {
return false;
}
auto foundGate = docSelectionGate.find(doc);
// check for a Selection Gate
if (ActiveGate) {
if (foundGate != docSelectionGate.end()) {
SelectionGate* gate = foundGate->second;
const char* subelement = nullptr;
auto pObject
= getObjectOfType(temp, App::DocumentObject::getClassTypeId(), gateResolve, &subelement);
if (!ActiveGate->allow(pObject ? pObject->getDocument() : temp.pDoc, pObject, subelement)) {
auto pObject = getObjectOfType(
temp,
App::DocumentObject::getClassTypeId(),
gate->resolveMode,
&subelement
);
if (!gate->allow(pObject ? pObject->getDocument() : temp.pDoc, pObject, subelement)) {
if (getMainWindow()) {
QString msg;
if (ActiveGate->notAllowedReason.length() > 0) {
msg = QObject::tr(ActiveGate->notAllowedReason.c_str());
if (gate->notAllowedReason.length() > 0) {
msg = QObject::tr(gate->notAllowedReason.c_str());
}
else {
msg = QCoreApplication::translate(
@@ -1238,7 +1252,7 @@ bool SelectionSingleton::addSelection(
Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView();
mdi->setOverrideCursor(Qt::ForbiddenCursor);
}
ActiveGate->notAllowedReason.clear();
gate->notAllowedReason.clear();
QApplication::beep();
return false;
}
@@ -2178,8 +2192,7 @@ SelectionSingleton::SelectionSingleton()
hx = 0;
hy = 0;
hz = 0;
ActiveGate = nullptr;
gateResolve = ResolveMode::OldStyleElement;
// NOLINTBEGIN
App::GetApplication().signalDeletedObject.connect(
std::bind(&Gui::SelectionSingleton::slotDeletedObject, this, sp::_1)
@@ -3063,13 +3076,14 @@ PyObject* SelectionSingleton::sAddSelectionGate(PyObject* /*self*/, PyObject* ar
PyObject* SelectionSingleton::sRemoveSelectionGate(PyObject* /*self*/, PyObject* args)
{
if (!PyArg_ParseTuple(args, "")) {
const char* pDocName = "";
if (!PyArg_ParseTuple(args, "|s", &pDocName)) {
return nullptr;
}
PY_TRY
{
Selection().rmvSelectionGate();
Selection().rmvSelectionGate(pDocName);
Py_Return;
}
PY_CATCH;
+18 -6
View File
@@ -284,6 +284,7 @@ public:
* literal into QT_TR_NOOP() for translatability.
*/
std::string notAllowedReason;
ResolveMode resolveMode;
};
/** SelectionGateFilterExternal
@@ -415,10 +416,19 @@ public:
void setPreselectCoord(float x, float y, float z);
/// returns the present preselection
const SelectionChanges& getPreselection() const;
/// add a SelectionGate to control what is selectable
void addSelectionGate(Gui::SelectionGate* gate, ResolveMode resolve = ResolveMode::OldStyleElement);
/// remove the active SelectionGate
void rmvSelectionGate();
/// add a SelectionGate to control what is selectable in a document's scope, by default the
/// active document is selected
// which is usually the intended behavior
void addSelectionGate(
Gui::SelectionGate* gate,
ResolveMode resolve = ResolveMode::OldStyleElement,
const char* pDocName = nullptr
);
/// remove the document's SelectionGate, by default the active document is selected, which is
/// usually the intended behavior
void rmvSelectionGate(const char* pDocName = nullptr);
/// remove the document's SelectionGate (assumes valid pointer)
void rmvSelectionGate(App::Document* doc);
int disableCommandLog();
int enableCommandLog(bool silent = false);
@@ -833,13 +843,15 @@ protected:
static SelectionSingleton* _pcSingleton;
// each document can have at most 1 selection gate active
std::map<App::Document*, Gui::SelectionGate*> docSelectionGate;
// Preselection helpers - it's a mess
std::string DocName;
std::string FeatName;
std::string SubName;
float hx, hy, hz;
Gui::SelectionGate* ActiveGate;
ResolveMode gateResolve;
int logDisabled = 0;
bool logHasSelection = false;