From cc89deccbfe2f6eb18b53648b68ec03693a3c618 Mon Sep 17 00:00:00 2001 From: DeepSOIC Date: Thu, 9 Jul 2015 22:03:32 +0300 Subject: [PATCH] SelectionGate: add capability to display why not allowed. --- src/Gui/Selection.cpp | 27 +- src/Gui/Selection.cpp.orig | 1313 ++++++++++++++++++++++++++++++++++++ src/Gui/Selection.h | 8 + 3 files changed, 1342 insertions(+), 6 deletions(-) create mode 100644 src/Gui/Selection.cpp.orig diff --git a/src/Gui/Selection.cpp b/src/Gui/Selection.cpp index f5d593b00..2aeb87ffa 100644 --- a/src/Gui/Selection.cpp +++ b/src/Gui/Selection.cpp @@ -485,13 +485,21 @@ bool SelectionSingleton::setPreselect(const char* pDocName, const char* pObjectN if (pObjectName) { App::DocumentObject* pObject = pDoc->getObject(pObjectName); if (!ActiveGate->allow(pDoc,pObject,pSubName)) { - snprintf(buf,512,"Not allowed: %s.%s.%s ",pDocName - ,pObjectName - ,pSubName - ); + QString msg; + if (ActiveGate->notAllowedReason.length() > 0){ + msg = QObject::tr(ActiveGate->notAllowedReason.c_str()); + } else { + msg = QCoreApplication::translate("SelectionFilter","Not allowed:"); + } + msg.append( + QObject::tr(" %1.%2.%3 ") + .arg(QString::fromAscii(pDocName)) + .arg(QString::fromAscii(pObjectName)) + .arg(QString::fromAscii(pSubName)) + ); if (getMainWindow()) { - getMainWindow()->showMessage(QString::fromLatin1(buf),3000); + getMainWindow()->showMessage(msg,3000); Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); mdi->setOverrideCursor(QCursor(Qt::ForbiddenCursor)); } @@ -665,10 +673,17 @@ bool SelectionSingleton::addSelection(const char* pDocName, const char* pObjectN if (ActiveGate) { if (!ActiveGate->allow(temp.pDoc,temp.pObject,pSubName)) { if (getMainWindow()) { - getMainWindow()->showMessage(QString::fromLatin1("Selection not allowed by filter"),5000); + QString msg; + if (ActiveGate->notAllowedReason.length() > 0) { + msg = QObject::tr(ActiveGate->notAllowedReason.c_str()); + } else { + msg = QCoreApplication::translate("SelectionFilter","Selection not allowed by filter"); + } + getMainWindow()->showMessage(msg,5000); Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); mdi->setOverrideCursor(Qt::ForbiddenCursor); } + ActiveGate->notAllowedReason.clear(); QApplication::beep(); return false; } diff --git a/src/Gui/Selection.cpp.orig b/src/Gui/Selection.cpp.orig new file mode 100644 index 000000000..019c82725 --- /dev/null +++ b/src/Gui/Selection.cpp.orig @@ -0,0 +1,1313 @@ +/*************************************************************************** + * Copyright (c) Juergen Riegel * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +# include +# include +# include +# include +# include +# include +#endif + +/// Here the FreeCAD includes sorted by Base,App,Gui...... +#include "Application.h" +#include "Document.h" +#include "Selection.h" +#include "SelectionFilter.h" +#include "SelectionObjectPy.h" +#include "View3DInventor.h" +#include +#include +#include +#include +#include +#include +#include +#include "MainWindow.h" + + + +using namespace Gui; +using namespace std; + +SelectionObserver::SelectionObserver() +{ + attachSelection(); +} + +SelectionObserver::~SelectionObserver() +{ + detachSelection(); +} + +bool SelectionObserver::blockConnection(bool block) +{ + bool ok = connectSelection.blocked(); + if (block) + connectSelection.block(); + else + connectSelection.unblock(); + return ok; +} + +bool SelectionObserver::isConnectionBlocked() const +{ + return connectSelection.blocked(); +} + +void SelectionObserver::attachSelection() +{ + if (!connectSelection.connected()) { + connectSelection = Selection().signalSelectionChanged.connect(boost::bind + (&SelectionObserver::onSelectionChanged, this, _1)); + } +} + +void SelectionObserver::detachSelection() +{ + if (connectSelection.connected()) { + connectSelection.disconnect(); + } +} + +// ------------------------------------------- + +std::vector SelectionObserverPython::_instances; + +SelectionObserverPython::SelectionObserverPython(const Py::Object& obj) : inst(obj) +{ +} + +SelectionObserverPython::~SelectionObserverPython() +{ +} + +void SelectionObserverPython::addObserver(const Py::Object& obj) +{ + _instances.push_back(new SelectionObserverPython(obj)); +} + +void SelectionObserverPython::removeObserver(const Py::Object& obj) +{ + SelectionObserverPython* obs=0; + for (std::vector::iterator it = + _instances.begin(); it != _instances.end(); ++it) { + if ((*it)->inst == obj) { + obs = *it; + _instances.erase(it); + break; + } + } + + delete obs; +} + +void SelectionObserverPython::onSelectionChanged(const SelectionChanges& msg) +{ + switch (msg.Type) + { + case SelectionChanges::AddSelection: + addSelection(msg); + break; + case SelectionChanges::RmvSelection: + removeSelection(msg); + break; + case SelectionChanges::SetSelection: + setSelection(msg); + break; + case SelectionChanges::ClrSelection: + clearSelection(msg); + break; + case SelectionChanges::SetPreselect: + setPreselection(msg); + break; + case SelectionChanges::RmvPreselect: + removePreselection(msg); + break; + default: + break; + } +} + +void SelectionObserverPython::addSelection(const SelectionChanges& msg) +{ + Base::PyGILStateLocker lock; + try { + if (this->inst.hasAttr(std::string("addSelection"))) { + Py::Callable method(this->inst.getAttr(std::string("addSelection"))); + Py::Tuple args(4); + args.setItem(0, Py::String(msg.pDocName ? msg.pDocName : "")); + args.setItem(1, Py::String(msg.pObjectName ? msg.pObjectName : "")); + args.setItem(2, Py::String(msg.pSubName ? msg.pSubName : "")); + Py::Tuple tuple(3); + tuple[0] = Py::Float(msg.x); + tuple[1] = Py::Float(msg.y); + tuple[2] = Py::Float(msg.z); + args.setItem(3, tuple); + method.apply(args); + } + } + catch (Py::Exception&) { + Base::PyException e; // extract the Python error text + e.ReportException(); + } +} + +void SelectionObserverPython::removeSelection(const SelectionChanges& msg) +{ + Base::PyGILStateLocker lock; + try { + if (this->inst.hasAttr(std::string("removeSelection"))) { + Py::Callable method(this->inst.getAttr(std::string("removeSelection"))); + Py::Tuple args(3); + args.setItem(0, Py::String(msg.pDocName ? msg.pDocName : "")); + args.setItem(1, Py::String(msg.pObjectName ? msg.pObjectName : "")); + args.setItem(2, Py::String(msg.pSubName ? msg.pSubName : "")); + method.apply(args); + } + } + catch (Py::Exception&) { + Base::PyException e; // extract the Python error text + e.ReportException(); + } +} + +void SelectionObserverPython::setSelection(const SelectionChanges& msg) +{ + Base::PyGILStateLocker lock; + try { + if (this->inst.hasAttr(std::string("setSelection"))) { + Py::Callable method(this->inst.getAttr(std::string("setSelection"))); + Py::Tuple args(1); + args.setItem(0, Py::String(msg.pDocName ? msg.pDocName : "")); + method.apply(args); + } + } + catch (Py::Exception&) { + Base::PyException e; // extract the Python error text + e.ReportException(); + } +} + +void SelectionObserverPython::clearSelection(const SelectionChanges& msg) +{ + Base::PyGILStateLocker lock; + try { + if (this->inst.hasAttr(std::string("clearSelection"))) { + Py::Callable method(this->inst.getAttr(std::string("clearSelection"))); + Py::Tuple args(1); + args.setItem(0, Py::String(msg.pDocName ? msg.pDocName : "")); + method.apply(args); + } + } + catch (Py::Exception&) { + Base::PyException e; // extract the Python error text + e.ReportException(); + } +} + +void SelectionObserverPython::setPreselection(const SelectionChanges& msg) +{ + Base::PyGILStateLocker lock; + try { + if (this->inst.hasAttr(std::string("setPreselection"))) { + Py::Callable method(this->inst.getAttr(std::string("setPreselection"))); + Py::Tuple args(3); + args.setItem(0, Py::String(msg.pDocName ? msg.pDocName : "")); + args.setItem(1, Py::String(msg.pObjectName ? msg.pObjectName : "")); + args.setItem(2, Py::String(msg.pSubName ? msg.pSubName : "")); + method.apply(args); + } + } + catch (Py::Exception&) { + Base::PyException e; // extract the Python error text + e.ReportException(); + } +} + +void SelectionObserverPython::removePreselection(const SelectionChanges& msg) +{ + Base::PyGILStateLocker lock; + try { + if (this->inst.hasAttr(std::string("removePreselection"))) { + Py::Callable method(this->inst.getAttr(std::string("removePreselection"))); + Py::Tuple args(3); + args.setItem(0, Py::String(msg.pDocName ? msg.pDocName : "")); + args.setItem(1, Py::String(msg.pObjectName ? msg.pObjectName : "")); + args.setItem(2, Py::String(msg.pSubName ? msg.pSubName : "")); + method.apply(args); + } + } + catch (Py::Exception&) { + Base::PyException e; // extract the Python error text + e.ReportException(); + } +} + +// ------------------------------------------- + +bool SelectionSingleton::hasSelection() const +{ + return !_SelList.empty(); +} + +std::vector SelectionSingleton::getCompleteSelection() const +{ + std::vector temp; + SelObj tempSelObj; + + for(std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { + tempSelObj.DocName = It->DocName.c_str(); + tempSelObj.FeatName = It->FeatName.c_str(); + tempSelObj.SubName = It->SubName.c_str(); + tempSelObj.TypeName = It->TypeName.c_str(); + tempSelObj.pObject = It->pObject; + tempSelObj.pDoc = It->pDoc; + temp.push_back(tempSelObj); + } + + return temp; +} + +std::vector SelectionSingleton::getSelection(const char* pDocName) const +{ + std::vector temp; + SelObj tempSelObj; + + App::Document *pcDoc; + pcDoc = getDocument(pDocName); + + if (!pcDoc) + return temp; + + for(std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { + if (It->pDoc == pcDoc) { + tempSelObj.DocName = It->DocName.c_str(); + tempSelObj.FeatName = It->FeatName.c_str(); + tempSelObj.SubName = It->SubName.c_str(); + tempSelObj.TypeName = It->TypeName.c_str(); + tempSelObj.pObject = It->pObject; + tempSelObj.pDoc = It->pDoc; + tempSelObj.x = It->x; + tempSelObj.y = It->y; + tempSelObj.z = It->z; + temp.push_back(tempSelObj); + } + } + + return temp; +} + +bool SelectionSingleton::hasSelection(const char* doc) const +{ + App::Document *pcDoc; + pcDoc = getDocument(doc); + if (!pcDoc) + return false; + for(std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { + if (It->pDoc == pcDoc) { + return true; + } + } + + return false; +} + +std::vector SelectionSingleton::getSelectionEx(const char* pDocName, Base::Type typeId) const +{ + std::vector temp; + std::map SortMap; + + // check the type + if (typeId == Base::Type::badType()) + return temp; + + App::Document *pcDoc; + string DocName; + + pcDoc = getDocument(pDocName); + + if (!pcDoc) + return temp; + + for (std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { + if (It->pDoc == pcDoc) { + // right type? + if (It->pObject->getTypeId().isDerivedFrom(typeId)){ + // if the object has already an entry + if (SortMap.find(It->pObject) != SortMap.end()){ + // only add sub-element + if (!It->SubName.empty()) { + SortMap[It->pObject].SubNames.push_back(It->SubName); + SortMap[It->pObject].SelPoses.push_back(Base::Vector3d(It->x,It->y,It->z)); + } + } + else { + // create a new entry + SelectionObject tempSelObj; + tempSelObj.DocName = It->DocName; + tempSelObj.FeatName = It->FeatName; + tempSelObj.TypeName = It->TypeName.c_str(); + if (!It->SubName.empty()) { + tempSelObj.SubNames.push_back(It->SubName); + tempSelObj.SelPoses.push_back(Base::Vector3d(It->x,It->y,It->z)); + } + SortMap.insert(std::pair(It->pObject,tempSelObj)); + } + } + } + } + + // The map looses the order thus we have to go again through the list and pick up the SelectionObject from the map + for (std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { + std::map::iterator Jt = SortMap.find(It->pObject); + if (Jt != SortMap.end()) { + temp.push_back(Jt->second); + SortMap.erase(Jt); + } + } + + return temp; +} + +int SelectionSingleton::getAsPropertyLinkSubList(App::PropertyLinkSubList &prop) const +{ + std::vector sel = this->getSelectionEx(); + std::vector objs; objs.reserve(sel.size()*2); + std::vector subs; subs.reserve(sel.size()*2); + for( int iobj = 0 ; iobj < sel.size() ; iobj++ ){ + Gui::SelectionObject &selitem = sel[iobj]; + App::DocumentObject* obj = selitem.getObject(); + const std::vector &subnames = selitem.getSubNames(); + if (subnames.size() == 0){//whole object is selected + objs.push_back(obj); + subs.push_back(std::string()); + } else { + for( int isub = 0 ; isub < subnames.size() ; isub++ ){ + objs.push_back(obj); + subs.push_back(subnames[isub]); + } + } + } + assert(objs.size()==subs.size()); + prop.setValues(objs, subs); + return objs.size(); +} + +vector SelectionSingleton::getObjectsOfType(const Base::Type& typeId, const char* pDocName) const +{ + std::vector temp; + App::Document *pcDoc; + + pcDoc = getDocument(pDocName); + + if (!pcDoc) + return temp; + + for (std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { + if (It->pDoc == pcDoc && It->pObject && It->pObject->getTypeId().isDerivedFrom(typeId)) { + temp.push_back(It->pObject); + } + } + + return temp; +} + +std::vector SelectionSingleton::getObjectsOfType(const char* typeName, const char* pDocName) const +{ + Base::Type typeId = Base::Type::fromName(typeName); + if (typeId == Base::Type::badType()) + return std::vector(); + return getObjectsOfType(typeId, pDocName); +} + +unsigned int SelectionSingleton::countObjectsOfType(const Base::Type& typeId, const char* pDocName) const +{ + unsigned int iNbr=0; + App::Document *pcDoc; + + pcDoc = getDocument(pDocName); + + if (!pcDoc) + return 0; + + for (std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { + if (It->pDoc == pcDoc && It->pObject && It->pObject->getTypeId().isDerivedFrom(typeId)) { + iNbr++; + } + } + + return iNbr; +} + +unsigned int SelectionSingleton::countObjectsOfType(const char* typeName, const char* pDocName) const +{ + Base::Type typeId = Base::Type::fromName(typeName); + if (typeId == Base::Type::badType()) + return 0; + return countObjectsOfType(typeId, pDocName); +} + +bool SelectionSingleton::setPreselect(const char* pDocName, const char* pObjectName, const char* pSubName, float x, float y, float z) +{ + static char buf[513]; + + if (DocName != "") + rmvPreselect(); + + if (ActiveGate) { + App::Document* pDoc = getDocument(pDocName); + if (pDoc) { + if (pObjectName) { + App::DocumentObject* pObject = pDoc->getObject(pObjectName); + if (!ActiveGate->allow(pDoc,pObject,pSubName)) { + QString msg; + if (ActiveGate->notAllowedReason.length() > 0){ + msg = QObject::tr(ActiveGate->notAllowedReason.c_str()); + } else { + msg = QCoreApplication::translate("SelectionFilter","Not allowed:"); + } + msg.append( + QObject::tr(" %1.%2.%3 ") + .arg(QString::fromAscii(pDocName)) + .arg(QString::fromAscii(pObjectName)) + .arg(QString::fromAscii(pSubName)) + ); + + if (getMainWindow()) { +<<<<<<< 18faa737da36035dd5ff84b1a5e26e38fd5194e1 + getMainWindow()->showMessage(QString::fromLatin1(buf),3000); +======= + getMainWindow()->showMessage(msg,3000); +>>>>>>> SelectionGate: add capability to display why not allowed. + Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); + mdi->setOverrideCursor(QCursor(Qt::ForbiddenCursor)); + } + return false; + } + + } + else + return ActiveGate->allow(pDoc,0,0); + } + else + return false; + + } + + DocName = pDocName; + FeatName= pObjectName; + SubName = pSubName; + hx = x; + hy = y; + hz = z; + + // set up the change object + SelectionChanges Chng; + Chng.pDocName = DocName.c_str(); + Chng.pObjectName = FeatName.c_str(); + Chng.pSubName = SubName.c_str(); + Chng.x = x; + Chng.y = y; + Chng.z = z; + Chng.Type = SelectionChanges::SetPreselect; + + // set the current preselection + CurrentPreselection = Chng; + + snprintf(buf,512,"Preselected: %s.%s.%s (%f,%f,%f)",Chng.pDocName + ,Chng.pObjectName + ,Chng.pSubName + ,x,y,z); + + //FIXME: We shouldn't replace the possibly defined edit cursor + //with the arrow cursor. But it seems that we don't even have to. + //if (getMainWindow()){ + // getMainWindow()->showMessage(QString::fromLatin1(buf),3000); + // Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); + // mdi->restoreOverrideCursor(); + //} + + Notify(Chng); + signalSelectionChanged(Chng); + + //Base::Console().Log("Sel : Add preselect %s \n",pObjectName); + + // allows the preselection + return true; +} + +void SelectionSingleton::setPreselectCoord( float x, float y, float z) +{ + static char buf[513]; + + // if nothing is in preselect ignore + if(!CurrentPreselection.pObjectName) return; + + CurrentPreselection.x = x; + CurrentPreselection.y = y; + CurrentPreselection.z = z; + + snprintf(buf,512,"Preselected: %s.%s.%s (%f,%f,%f)",CurrentPreselection.pDocName + ,CurrentPreselection.pObjectName + ,CurrentPreselection.pSubName + ,x,y,z); + + if (getMainWindow()) + getMainWindow()->showMessage(QString::fromLatin1(buf),3000); +} + +void SelectionSingleton::rmvPreselect() +{ + if (DocName == "") + return; + + SelectionChanges Chng; + Chng.pDocName = DocName.c_str(); + Chng.pObjectName = FeatName.c_str(); + Chng.pSubName = SubName.c_str(); + Chng.Type = SelectionChanges::RmvPreselect; + + // reset the current preselection + CurrentPreselection.pDocName =0; + CurrentPreselection.pObjectName = 0; + CurrentPreselection.pSubName = 0; + CurrentPreselection.x = 0.0; + CurrentPreselection.y = 0.0; + CurrentPreselection.z = 0.0; + + // notify observing objects + Notify(Chng); + signalSelectionChanged(Chng); + + DocName = ""; + FeatName= ""; + SubName = ""; + hx = 0; + hy = 0; + hz = 0; + + if (ActiveGate && getMainWindow()) { + Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); + mdi->restoreOverrideCursor(); + } + + //Base::Console().Log("Sel : Rmv preselect \n"); +} + +const SelectionChanges &SelectionSingleton::getPreselection(void) const +{ + return CurrentPreselection; +} + +// add a SelectionGate to control what is selectable +void SelectionSingleton::addSelectionGate(Gui::SelectionGate *gate) +{ + if (ActiveGate) + rmvSelectionGate(); + + ActiveGate = gate; + +} + +// remove the active SelectionGate +void SelectionSingleton::rmvSelectionGate(void) +{ + if (ActiveGate) { + delete ActiveGate; + ActiveGate=0; + Gui::Document* doc = Gui::Application::Instance->activeDocument(); + if (doc) { + Gui::MDIView* mdi = doc->getActiveView(); + mdi->restoreOverrideCursor(); + } + } +} + + +App::Document* SelectionSingleton::getDocument(const char* pDocName) const +{ + if (pDocName) + return App::GetApplication().getDocument(pDocName); + else + return App::GetApplication().getActiveDocument(); +} + +bool SelectionSingleton::addSelection(const char* pDocName, const char* pObjectName, const char* pSubName, float x, float y, float z) +{ + // already in ? + if (isSelected(pDocName, pObjectName, pSubName)) + return true; + + _SelObj temp; + + temp.pDoc = getDocument(pDocName); + + if (temp.pDoc) { + if(pObjectName) + temp.pObject = temp.pDoc->getObject(pObjectName); + else + temp.pObject = 0; + + // check for a Selection Gate + if (ActiveGate) { + if (!ActiveGate->allow(temp.pDoc,temp.pObject,pSubName)) { + if (getMainWindow()) { +<<<<<<< 18faa737da36035dd5ff84b1a5e26e38fd5194e1 + getMainWindow()->showMessage(QString::fromLatin1("Selection not allowed by filter"),5000); +======= + QString msg; + if (ActiveGate->notAllowedReason.length() > 0) { + msg = QObject::tr(ActiveGate->notAllowedReason.c_str()); + } else { + msg = QCoreApplication::translate("SelectionFilter","Selection not allowed by filter"); + } + getMainWindow()->showMessage(msg,5000); +>>>>>>> SelectionGate: add capability to display why not allowed. + Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); + mdi->setOverrideCursor(Qt::ForbiddenCursor); + } + ActiveGate->notAllowedReason.clear(); + QApplication::beep(); + return false; + } + } + + temp.DocName = pDocName; + temp.FeatName = pObjectName ? pObjectName : ""; + temp.SubName = pSubName ? pSubName : ""; + temp.x = x; + temp.y = y; + temp.z = z; + + if (temp.pObject) + temp.TypeName = temp.pObject->getTypeId().getName(); + + _SelList.push_back(temp); + + SelectionChanges Chng; + + Chng.pDocName = pDocName; + Chng.pObjectName = pObjectName ? pObjectName : ""; + Chng.pSubName = pSubName ? pSubName : ""; + Chng.x = x; + Chng.y = y; + Chng.z = z; + Chng.Type = SelectionChanges::AddSelection; + + + Notify(Chng); + signalSelectionChanged(Chng); + + Base::Console().Log("Sel : Add Selection \"%s.%s.%s(%f,%f,%f)\"\n",pDocName,pObjectName,pSubName,x,y,z); + + // allow selection + return true; + } + else { + // neither an existing nor active document available + // this can often happen when importing .iv files + Base::Console().Error("Cannot add to selection: no document '%s' found.\n", pDocName); + return false; + } +} + +bool SelectionSingleton::addSelection(const char* pDocName, const char* pObjectName, const std::vector& pSubNames) +{ + // already in ? + //if (isSelected(pDocName, pObjectName, pSubName)) + // return true; + + _SelObj temp; + + temp.pDoc = getDocument(pDocName); + + if (temp.pDoc) { + if(pObjectName) + temp.pObject = temp.pDoc->getObject(pObjectName); + else + temp.pObject = 0; + + if (temp.pObject) + temp.TypeName = temp.pObject->getTypeId().getName(); + + temp.DocName = pDocName; + temp.FeatName = pObjectName ? pObjectName : ""; + for (std::vector::const_iterator it = pSubNames.begin(); it != pSubNames.end(); ++it) { + temp.SubName = it->c_str(); + temp.x = 0; + temp.y = 0; + temp.z = 0; + + _SelList.push_back(temp); + } + + SelectionChanges Chng; + + Chng.pDocName = pDocName; + Chng.pObjectName = pObjectName ? pObjectName : ""; + Chng.pSubName = ""; + Chng.x = 0; + Chng.y = 0; + Chng.z = 0; + Chng.Type = SelectionChanges::AddSelection; + + Notify(Chng); + signalSelectionChanged(Chng); + + // allow selection + return true; + } + else { + // neither an existing nor active document available + // this can often happen when importing .iv files + Base::Console().Error("Cannot add to selection: no document '%s' found.\n", pDocName); + return false; + } +} + +void SelectionSingleton::rmvSelection(const char* pDocName, const char* pObjectName, const char* pSubName) +{ + std::vector rmvList; + + for (std::list<_SelObj>::iterator It = _SelList.begin();It != _SelList.end();) { + if ((It->DocName == pDocName && !pObjectName) || + (It->DocName == pDocName && pObjectName && It->FeatName == pObjectName && !pSubName) || + (It->DocName == pDocName && pObjectName && It->FeatName == pObjectName && pSubName && It->SubName == pSubName)) + { + // save in tmp. string vars + std::string tmpDocName = It->DocName; + std::string tmpFeaName = It->FeatName; + std::string tmpSubName = It->SubName; + + // destroy the _SelObj item + It = _SelList.erase(It); + + SelectionChanges Chng; + Chng.pDocName = tmpDocName.c_str(); + Chng.pObjectName = tmpFeaName.c_str(); + Chng.pSubName = tmpSubName.c_str(); + Chng.Type = SelectionChanges::RmvSelection; + + Notify(Chng); + signalSelectionChanged(Chng); + + rmvList.push_back(Chng); + Base::Console().Log("Sel : Rmv Selection \"%s.%s.%s\"\n",pDocName,pObjectName,pSubName); + } + else { + ++It; + } + } +} + +void SelectionSingleton::setSelection(const char* pDocName, const std::vector& sel) +{ + App::Document *pcDoc; + pcDoc = getDocument(pDocName); + if (!pcDoc) + return; + + std::set cur_sel, new_sel; + new_sel.insert(sel.begin(), sel.end()); + + // Make sure to keep the order of the currently selected objects + std::list<_SelObj> temp; + for (std::list<_SelObj>::const_iterator it = _SelList.begin(); it != _SelList.end(); ++it) { + if (it->pDoc != pcDoc) + temp.push_back(*it); + else { + cur_sel.insert(it->pObject); + if (new_sel.find(it->pObject) != new_sel.end()) + temp.push_back(*it); + } + } + + // Get the objects we must add to the selection + std::vector diff_new_cur; + std::back_insert_iterator< std::vector > biit(diff_new_cur); + std::set_difference(new_sel.begin(), new_sel.end(), cur_sel.begin(), cur_sel.end(), biit); + + _SelObj obj; + for (std::vector::const_iterator it = diff_new_cur.begin(); it != diff_new_cur.end(); ++it) { + obj.pDoc = pcDoc; + obj.pObject = *it; + obj.DocName = pDocName; + obj.FeatName = (*it)->getNameInDocument(); + obj.SubName = ""; + obj.TypeName = (*it)->getTypeId().getName(); + obj.x = 0.0f; + obj.y = 0.0f; + obj.z = 0.0f; + temp.push_back(obj); + } + + if (cur_sel == new_sel) // nothing has changed + return; + + _SelList = temp; + + SelectionChanges Chng; + Chng.Type = SelectionChanges::SetSelection; + Chng.pDocName = pDocName; + Chng.pObjectName = ""; + Chng.pSubName = ""; + + Notify(Chng); + signalSelectionChanged(Chng); +} + +void SelectionSingleton::clearSelection(const char* pDocName) +{ + App::Document* pDoc; + pDoc = getDocument(pDocName); + + // the document 'pDocName' has already been removed + if (!pDoc && !pDocName) { + clearCompleteSelection(); + } + else { + std::string docName; + if (pDocName) + docName = pDocName; + else + docName = pDoc->getName(); // active document + std::list<_SelObj> selList; + for (std::list<_SelObj>::iterator it = _SelList.begin(); it != _SelList.end(); ++it) { + if (it->DocName != docName) + selList.push_back(*it); + } + + _SelList = selList; + + SelectionChanges Chng; + Chng.Type = SelectionChanges::ClrSelection; + Chng.pDocName = docName.c_str(); + Chng.pObjectName = ""; + Chng.pSubName = ""; + + Notify(Chng); + signalSelectionChanged(Chng); + + Base::Console().Log("Sel : Clear selection\n"); + } +} + +void SelectionSingleton::clearCompleteSelection() +{ + _SelList.clear(); + + SelectionChanges Chng; + Chng.Type = SelectionChanges::ClrSelection; + Chng.pDocName = ""; + Chng.pObjectName = ""; + Chng.pSubName = ""; + + + Notify(Chng); + signalSelectionChanged(Chng); + + Base::Console().Log("Sel : Clear selection\n"); +} + +bool SelectionSingleton::isSelected(const char* pDocName, const char* pObjectName, const char* pSubName) const +{ + const char* tmpDocName = pDocName ? pDocName : ""; + const char* tmpFeaName = pObjectName ? pObjectName : ""; + const char* tmpSubName = pSubName ? pSubName : ""; + for (std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) + if (It->DocName == tmpDocName && It->FeatName == tmpFeaName && It->SubName == tmpSubName) + return true; + return false; +} + +bool SelectionSingleton::isSelected(App::DocumentObject* obj, const char* pSubName) const +{ + if (!obj) return false; + + for(list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { + if (It->pObject == obj) { + if (pSubName) { + if (It->SubName == pSubName) + return true; + } + else { + return true; + } + } + } + + return false; +} + +void SelectionSingleton::slotDeletedObject(const App::DocumentObject& Obj) +{ + // remove also from the selection, if selected + Selection().rmvSelection( Obj.getDocument()->getName(), Obj.getNameInDocument() ); +} + + +//************************************************************************** +// Construction/Destruction + +/** + * A constructor. + * A more elaborate description of the constructor. + */ +SelectionSingleton::SelectionSingleton() +{ + ActiveGate = 0; + App::GetApplication().signalDeletedObject.connect(boost::bind(&Gui::SelectionSingleton::slotDeletedObject, this, _1)); + CurrentPreselection.pDocName = 0; + CurrentPreselection.pObjectName = 0; + CurrentPreselection.pSubName = 0; + +} + +/** + * A destructor. + * A more elaborate description of the destructor. + */ +SelectionSingleton::~SelectionSingleton() +{ +} + +SelectionSingleton* SelectionSingleton::_pcSingleton = NULL; + +SelectionSingleton& SelectionSingleton::instance(void) +{ + if (_pcSingleton == NULL) + _pcSingleton = new SelectionSingleton; + return *_pcSingleton; +} + +void SelectionSingleton::destruct (void) +{ + if (_pcSingleton != NULL) + delete _pcSingleton; + _pcSingleton = 0; +} + +//************************************************************************** +// Python stuff + +// SelectionSingleton Methods // Methods structure +PyMethodDef SelectionSingleton::Methods[] = { + {"addSelection", (PyCFunction) SelectionSingleton::sAddSelection, 1, + "addSelection(object,[string,float,float,float]) -- Add an object to the selection\n" + "where string is the sub-element name and the three floats represent a 3d point"}, + {"removeSelection", (PyCFunction) SelectionSingleton::sRemoveSelection, 1, + "removeSelection(object) -- Remove an object from the selection"}, + {"clearSelection" , (PyCFunction) SelectionSingleton::sClearSelection, 1, + "clearSelection([string]) -- Clear the selection\n" + "Clear the selection to the given document name. If no document is\n" + "given the complete selection is cleared."}, + {"isSelected", (PyCFunction) SelectionSingleton::sIsSelected, 1, + "isSelected(object) -- Check if a given object is selected"}, + {"countObjectsOfType", (PyCFunction) SelectionSingleton::sCountObjectsOfType, 1, + "countObjectsOfType(string, [string]) -- Get the number of selected objects\n" + "The first argument defines the object type e.g. \"Part::Feature\" and the\n" + "second argumeht defines the document name. If no document name is given the\n" + "currently active document is used"}, + {"getSelection", (PyCFunction) SelectionSingleton::sGetSelection, 1, + "getSelection([string]) -- Return a list of selected objets\n" + "Return a list of selected objects for a given document name. If no\n" + "document name is given the selection for the active document is returned."}, + {"getCompleteSelection", (PyCFunction) SelectionSingleton::sGetCompleteSelection, 1, + "getCompleteSelection() -- Return a list of selected objects of all documents."}, + {"getSelectionEx", (PyCFunction) SelectionSingleton::sGetSelectionEx, 1, + "getSelectionEx([string]) -- Return a list of SelectionObjects\n" + "Return a list of SelectionObjects for a given document name. If no\n" + "document is given the selection of the active document is returned.\n" + "The SelectionObjects contain a variety of information about the selection,\n" + "e.g. sub-element names."}, + {"getSelectionObject", (PyCFunction) SelectionSingleton::sGetSelectionObject, 1, + "getSelectionObject(doc,obj,sub,(x,y,z)) -- Return a SelectionObject"}, + {"addObserver", (PyCFunction) SelectionSingleton::sAddSelObserver, 1, + "addObserver(Object) -- Install an observer\n"}, + {"removeObserver", (PyCFunction) SelectionSingleton::sRemSelObserver, 1, + "removeObserver(Object) -- Uninstall an observer\n"}, + {"addSelectionGate", (PyCFunction) SelectionSingleton::sAddSelectionGate, 1, + "addSelectionGate(String|Filter|Gate) -- activate the selection gate.\n" + "The selection gate will prohibit all selections which do not match\n" + "the given selection filter string.\n" + " Examples strings are:\n" + "'SELECT Part::Feature SUBELEMENT Edge',\n" + "'SELECT Robot::RobotObject'\n" + "\n" + "You can also set an instance of SelectionFilter:\n" + "filter = Gui.Selection.Filter('SELECT Part::Feature SUBELEMENT Edge')\n" + "Gui.Selection.addSelectionGate(filter)\n" + "\n" + "And the most flexible approach is to write your own selection gate class\n" + "that implements the method 'allow'\n" + "class Gate:\n" + " def allow(self,doc,obj,sub):\n" + " return (sub[0:4] == 'Face')\n" + "Gui.Selection.addSelectionGate(Gate())"}, + {"removeSelectionGate", (PyCFunction) SelectionSingleton::sRemoveSelectionGate, 1, + "removeSelectionGate() -- remove the active selection gate\n"}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +PyObject *SelectionSingleton::sAddSelection(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) +{ + PyObject *object; + char* subname=0; + float x=0,y=0,z=0; + if (!PyArg_ParseTuple(args, "O!|sfff", &(App::DocumentObjectPy::Type),&object,&subname,&x,&y,&z)) + return NULL; // NULL triggers exception + + App::DocumentObjectPy* docObjPy = static_cast(object); + App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr(); + if (!docObj || !docObj->getNameInDocument()) { + PyErr_SetString(Base::BaseExceptionFreeCADError, "Cannot check invalid object"); + return NULL; + } + + Selection().addSelection(docObj->getDocument()->getName(), + docObj->getNameInDocument(), + subname,x,y,z); + + Py_Return; +} + +PyObject *SelectionSingleton::sRemoveSelection(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) +{ + PyObject *object; + char* subname=0; + if (!PyArg_ParseTuple(args, "O!|s", &(App::DocumentObjectPy::Type),&object,&subname)) + return NULL; // NULL triggers exception + + App::DocumentObjectPy* docObjPy = static_cast(object); + App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr(); + if (!docObj || !docObj->getNameInDocument()) { + PyErr_SetString(Base::BaseExceptionFreeCADError, "Cannot check invalid object"); + return NULL; + } + + Selection().rmvSelection(docObj->getDocument()->getName(), + docObj->getNameInDocument(), + subname); + + Py_Return; +} + +PyObject *SelectionSingleton::sClearSelection(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) +{ + char *documentName=0; + if (!PyArg_ParseTuple(args, "|s", &documentName)) // convert args: Python->C + return NULL; // NULL triggers exception + documentName ? Selection().clearSelection(documentName) : Selection().clearCompleteSelection(); + Py_Return; +} + +PyObject *SelectionSingleton::sIsSelected(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) +{ + PyObject *object; + char* subname=0; + if (!PyArg_ParseTuple(args, "O!|s", &(App::DocumentObjectPy::Type), &object, &subname)) + return NULL; // NULL triggers exception + + App::DocumentObjectPy* docObj = static_cast(object); + bool ok = Selection().isSelected(docObj->getDocumentObjectPtr(), subname); + return Py_BuildValue("O", (ok ? Py_True : Py_False)); +} + +PyObject *SelectionSingleton::sCountObjectsOfType(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) +{ + char* objecttype; + char* document=0; + if (!PyArg_ParseTuple(args, "s|s", &objecttype, &document)) + return NULL; + + unsigned int count = Selection().countObjectsOfType(objecttype, document); + return PyInt_FromLong(count); +} + +PyObject *SelectionSingleton::sGetSelection(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) +{ + char *documentName=0; + if (!PyArg_ParseTuple(args, "|s", &documentName)) // convert args: Python->C + return NULL; // NULL triggers exception + + std::vector sel; + sel = Selection().getSelection(documentName); + + try { + Py::List list; + for (std::vector::iterator it = sel.begin(); it != sel.end(); ++it) { + list.append(Py::asObject(it->pObject->getPyObject())); + } + return Py::new_reference_to(list); + } + catch (Py::Exception&) { + return 0; + } +} + +PyObject *SelectionSingleton::sGetCompleteSelection(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) +{ + if (!PyArg_ParseTuple(args, "")) // convert args: Python->C + return NULL; // NULL triggers exception + + std::vector sel; + sel = Selection().getCompleteSelection(); + + try { + Py::List list; + for (std::vector::iterator it = sel.begin(); it != sel.end(); ++it) { + list.append(Py::asObject(it->pObject->getPyObject())); + } + return Py::new_reference_to(list); + } + catch (Py::Exception&) { + return 0; + } +} + +PyObject *SelectionSingleton::sGetSelectionEx(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) +{ + char *documentName=0; + if (!PyArg_ParseTuple(args, "|s", &documentName)) // convert args: Python->C + return NULL; // NULL triggers exception + + std::vector sel; + sel = Selection().getSelectionEx(documentName); + + try { + Py::List list; + for (std::vector::iterator it = sel.begin(); it != sel.end(); ++it) { + list.append(Py::asObject(it->getPyObject())); + } + return Py::new_reference_to(list); + } + catch (Py::Exception&) { + return 0; + } +} + +PyObject *SelectionSingleton::sGetSelectionObject(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) +{ + char *docName, *objName, *subName; + PyObject* tuple=0; + if (!PyArg_ParseTuple(args, "sss|O!", &docName, &objName, &subName, + &PyTuple_Type, &tuple)) + return NULL; + + try { + SelectionObject selObj; + selObj.DocName = docName; + selObj.FeatName = objName; + std::string sub = subName; + if (!sub.empty()) { + selObj.SubNames.push_back(sub); + if (tuple) { + Py::Tuple t(tuple); + double x = (double)Py::Float(t.getItem(0)); + double y = (double)Py::Float(t.getItem(1)); + double z = (double)Py::Float(t.getItem(2)); + selObj.SelPoses.push_back(Base::Vector3d(x,y,z)); + } + } + + return selObj.getPyObject(); + } + catch (const Py::Exception&) { + return 0; + } + catch (const Base::Exception& e) { + PyErr_SetString(Base::BaseExceptionFreeCADError, e.what()); + return 0; + } +} + +PyObject *SelectionSingleton::sAddSelObserver(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) +{ + PyObject* o; + if (!PyArg_ParseTuple(args, "O",&o)) + return NULL; + PY_TRY { + SelectionObserverPython::addObserver(Py::Object(o)); + Py_Return; + } PY_CATCH; +} + +PyObject *SelectionSingleton::sRemSelObserver(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) +{ + PyObject* o; + if (!PyArg_ParseTuple(args, "O",&o)) + return NULL; + PY_TRY { + SelectionObserverPython::removeObserver(Py::Object(o)); + Py_Return; + } PY_CATCH; +} + +PyObject *SelectionSingleton::sAddSelectionGate(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) +{ + char* filter; + if (PyArg_ParseTuple(args, "s",&filter)) { + PY_TRY { + Selection().addSelectionGate(new SelectionFilterGate(filter)); + Py_Return; + } PY_CATCH; + } + + PyErr_Clear(); + PyObject* filterPy; + if (PyArg_ParseTuple(args, "O!",SelectionFilterPy::type_object(),&filterPy)) { + PY_TRY { + Selection().addSelectionGate(new SelectionFilterGatePython(static_cast(filterPy))); + Py_Return; + } PY_CATCH; + } + + PyErr_Clear(); + PyObject* gate; + if (PyArg_ParseTuple(args, "O",&gate)) { + PY_TRY { + Selection().addSelectionGate(new SelectionGatePython(Py::Object(gate, false))); + Py_Return; + } PY_CATCH; + } + + PyErr_SetString(PyExc_ValueError, "Argument is neither string nor SelectionFiler nor SelectionGate"); + return 0; +} + +PyObject *SelectionSingleton::sRemoveSelectionGate(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; // NULL triggers exception + + PY_TRY { + Selection().rmvSelectionGate(); + } PY_CATCH; + + Py_Return; +} diff --git a/src/Gui/Selection.h b/src/Gui/Selection.h index 064ec402d..1ec24569c 100644 --- a/src/Gui/Selection.h +++ b/src/Gui/Selection.h @@ -178,6 +178,14 @@ class GuiExport SelectionGate public: virtual ~SelectionGate(){} virtual bool allow(App::Document*,App::DocumentObject*, const char*)=0; + + /** + * @brief notAllowedReason is a string that sets the message to be + * displayed in statusbar for cluing the user on why is the selection not + * allowed. Set this variable in allow() implementation. Enclose the + * literal into QT_TR_NOOP() for translatability. + */ + std::string notAllowedReason; };