Files
create/src/Gui/Selection.cpp
2016-08-21 14:03:02 +02:00

1344 lines
45 KiB
C++

/***************************************************************************
* Copyright (c) Juergen Riegel <juergen.riegel@web.de> *
* *
* 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 <assert.h>
# include <string>
# include <boost/signals.hpp>
# include <boost/bind.hpp>
# include <QApplication>
# include <QString>
# include <QStatusBar>
#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 <Base/Exception.h>
#include <Base/Console.h>
#include <Base/Interpreter.h>
#include <App/Application.h>
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <App/DocumentObjectPy.h>
#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*> 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<SelectionObserverPython*>::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::SelObj> SelectionSingleton::getCompleteSelection() const
{
std::vector<SelObj> 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::SelObj> SelectionSingleton::getSelection(const char* pDocName) const
{
std::vector<SelObj> 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<SelectionObject> SelectionSingleton::getSelectionEx(const char* pDocName, Base::Type typeId) const
{
std::vector<SelectionObject> temp;
std::map<App::DocumentObject*,SelectionObject> 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<App::DocumentObject*,SelectionObject>(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<App::DocumentObject*,SelectionObject>::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<Gui::SelectionObject> sel = this->getSelectionEx();
std::vector<App::DocumentObject*> objs; objs.reserve(sel.size()*2);
std::vector<std::string> subs; subs.reserve(sel.size()*2);
for (std::size_t iobj = 0; iobj < sel.size(); iobj++) {
Gui::SelectionObject &selitem = sel[iobj];
App::DocumentObject* obj = selitem.getObject();
const std::vector<std::string> &subnames = selitem.getSubNames();
if (subnames.size() == 0){//whole object is selected
objs.push_back(obj);
subs.push_back(std::string());
} else {
for (std::size_t 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<App::DocumentObject*> SelectionSingleton::getObjectsOfType(const Base::Type& typeId, const char* pDocName) const
{
std::vector<App::DocumentObject*> 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<App::DocumentObject*> SelectionSingleton::getObjectsOfType(const char* typeName, const char* pDocName) const
{
Base::Type typeId = Base::Type::fromName(typeName);
if (typeId == Base::Type::badType())
return std::vector<App::DocumentObject*>();
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::fromLatin1(pDocName))
.arg(QString::fromLatin1(pObjectName))
.arg(QString::fromLatin1(pSubName))
);
if (getMainWindow()) {
getMainWindow()->showMessage(msg,3000);
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()) {
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;
}
}
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);
#ifdef FC_DEBUG
Base::Console().Log("Sel : Add Selection \"%s.%s.%s(%f,%f,%f)\"\n",pDocName,pObjectName,pSubName,x,y,z);
#endif
// 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<std::string>& pSubNames)
{
_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<std::string>::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<SelectionChanges> 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);
#ifdef FC_DEBUG
Base::Console().Log("Sel : Rmv Selection \"%s.%s.%s\"\n",pDocName,pObjectName,pSubName);
#endif
}
else {
++It;
}
}
}
void SelectionSingleton::setSelection(const char* pDocName, const std::vector<App::DocumentObject*>& sel)
{
App::Document *pcDoc;
pcDoc = getDocument(pDocName);
if (!pcDoc)
return;
std::set<App::DocumentObject*> 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<App::DocumentObject*> diff_new_cur;
std::back_insert_iterator< std::vector<App::DocumentObject*> > 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<App::DocumentObject*>::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);
#ifdef FC_DEBUG
Base::Console().Log("Sel : Clear selection\n");
#endif
}
}
void SelectionSingleton::clearCompleteSelection()
{
_SelList.clear();
SelectionChanges Chng;
Chng.Type = SelectionChanges::ClrSelection;
Chng.pDocName = "";
Chng.pObjectName = "";
Chng.pSubName = "";
Notify(Chng);
signalSelectionChanged(Chng);
#ifdef FC_DEBUG
Base::Console().Log("Sel : Clear selection\n");
#endif
}
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()
{
hx = 0;
hy = 0;
hz = 0;
ActiveGate = 0;
App::GetApplication().signalDeletedObject.connect(boost::bind(&Gui::SelectionSingleton::slotDeletedObject, this, _1));
CurrentPreselection.pDocName = 0;
CurrentPreselection.pObjectName = 0;
CurrentPreselection.pSubName = 0;
CurrentPreselection.x = 0.0;
CurrentPreselection.y = 0.0;
CurrentPreselection.z = 0.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)) {
App::DocumentObjectPy* docObjPy = static_cast<App::DocumentObjectPy*>(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;
}
PyErr_Clear();
PyObject *sequence;
if (PyArg_ParseTuple(args, "O!O", &(App::DocumentObjectPy::Type),&object,&sequence)) {
App::DocumentObjectPy* docObjPy = static_cast<App::DocumentObjectPy*>(object);
App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr();
if (!docObj || !docObj->getNameInDocument()) {
PyErr_SetString(Base::BaseExceptionFreeCADError, "Cannot check invalid object");
return NULL;
}
try {
if (PyTuple_Check(sequence) || PyList_Check(sequence)) {
Py::Sequence list(sequence);
for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) {
std::string subname = static_cast<std::string>(Py::String(*it));
Selection().addSelection(docObj->getDocument()->getName(),
docObj->getNameInDocument(),
subname.c_str());
}
Py_Return;
}
}
catch (const Py::Exception&) {
// do nothing here
}
}
PyErr_SetString(PyExc_ValueError, "type must be 'DocumentObject[,subname[,x,y,z]]' or 'DocumentObject, list or tuple of subnames'");
return 0;
}
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<App::DocumentObjectPy*>(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<App::DocumentObjectPy*>(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<SelectionSingleton::SelObj> sel;
sel = Selection().getSelection(documentName);
try {
Py::List list;
for (std::vector<SelectionSingleton::SelObj>::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<SelectionSingleton::SelObj> sel;
sel = Selection().getCompleteSelection();
try {
Py::List list;
for (std::vector<SelectionSingleton::SelObj>::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<SelectionObject> sel;
sel = Selection().getSelectionEx(documentName);
try {
Py::List list;
for (std::vector<SelectionObject>::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<SelectionFilterPy*>(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;
}