/****************************************************************************** * Copyright (c)2012 Jan Rheinlaender * * * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Utils.h" #include "ui_TaskFeaturePick.h" #include "TaskFeaturePick.h" #include #include #include #include #include #include using namespace PartDesignGui; using namespace Attacher; // TODO Do ve should snap here to App:Part or GeoFeatureGroup/DocumentObjectGroup ? (2015-09-04, Fat-Zer) const QString TaskFeaturePick::getFeatureStatusString(const featureStatus st) { switch (st) { case validFeature: return tr("Valid"); case invalidShape: return tr("Invalid shape"); case noWire: return tr("No wire in sketch"); case isUsed: return tr("Sketch already used by other feature"); case otherBody: return tr("Belongs to another body"); case otherPart: return tr("Belongs to another part"); case notInBody: return tr("Doesn't belongs to any body"); case basePlane: return tr("Base plane"); case afterTip: return tr("Feature is located after the tip feature"); } return QString(); } TaskFeaturePick::TaskFeaturePick(std::vector& objects, const std::vector& status, QWidget* parent) : TaskBox(Gui::BitmapFactory().pixmap("edit-select-box"), tr("Select feature"), true, parent) , ui(new Ui_TaskFeaturePick) , doSelection(false) { proxy = new QWidget(this); ui->setupUi(proxy); connect(ui->checkUsed, SIGNAL(toggled(bool)), this, SLOT(onUpdate(bool))); connect(ui->checkOtherBody, SIGNAL(toggled(bool)), this, SLOT(onUpdate(bool))); connect(ui->checkOtherPart, SIGNAL(toggled(bool)), this, SLOT(onUpdate(bool))); connect(ui->radioIndependent, SIGNAL(toggled(bool)), this, SLOT(onUpdate(bool))); connect(ui->radioDependent, SIGNAL(toggled(bool)), this, SLOT(onUpdate(bool))); connect(ui->radioXRef, SIGNAL(toggled(bool)), this, SLOT(onUpdate(bool))); connect(ui->listWidget, SIGNAL(itemSelectionChanged()), this, SLOT(onItemSelectionChanged())); enum { axisBit=0, planeBit = 1}; // NOTE: generally there shouldn't be more then one origin std::map > originVisStatus; auto statusIt = status.cbegin(); auto objIt = objects.begin(); assert(status.size() == objects.size()); for (; statusIt != status.end(); ++statusIt, ++objIt) { QListWidgetItem* item = new QListWidgetItem( QString::fromLatin1("%1 (%2)") .arg(QString::fromUtf8((*objIt)->Label.getValue())) .arg(getFeatureStatusString(*statusIt) ) ); item->setData(Qt::UserRole, QString::fromLatin1((*objIt)->getNameInDocument())); ui->listWidget->addItem(item); App::Document* pDoc = (*objIt)->getDocument(); documentName = pDoc->getName(); //check if we need to set any origin in temporary visibility mode if (*statusIt != invalidShape && (*objIt)->isDerivedFrom ( App::OriginFeature::getClassTypeId () )) { App::Origin *origin = static_cast (*objIt)->getOrigin (); if (origin) { if ((*objIt)->isDerivedFrom (App::Plane::getClassTypeId())) { originVisStatus[ origin ].set (planeBit, true); } else if ( (*objIt)->isDerivedFrom (App::Line::getClassTypeId())) { originVisStatus[ origin ].set (axisBit, true); } } } } // Setup the origin's temporary visibility for (const auto & originPair: originVisStatus) { const auto &origin = originPair.first; Gui::ViewProviderOrigin* vpo = static_cast ( Gui::Application::Instance->getViewProvider ( origin ) ); if (vpo) { vpo->setTemporaryVisibility( originVisStatus[origin][axisBit], originVisStatus[origin][planeBit]); origins.push_back(vpo); } } // TODO may be update origin API to show only some objects (2015-08-31, Fat-Zer) groupLayout()->addWidget(proxy); statuses = status; updateList(); } TaskFeaturePick::~TaskFeaturePick() { for(Gui::ViewProviderOrigin* vpo : origins) vpo->resetTemporaryVisibility(); } void TaskFeaturePick::updateList() { int index = 0; for (std::vector::const_iterator st = statuses.begin(); st != statuses.end(); st++) { QListWidgetItem* item = ui->listWidget->item(index); switch (*st) { case validFeature: item->setHidden(false); break; case invalidShape: item->setHidden(true); break; case isUsed: item->setHidden(!ui->checkUsed->isChecked()); break; case noWire: item->setHidden(true); break; case otherBody: item->setHidden(!ui->checkOtherBody->isChecked()); break; case otherPart: item->setHidden(!ui->checkOtherPart->isChecked()); break; case notInBody: item->setHidden(!ui->checkOtherPart->isChecked()); break; case basePlane: item->setHidden(false); break; case afterTip: item->setHidden(true); break; } index++; } } void TaskFeaturePick::onUpdate(bool) { bool enable = false; if (ui->checkOtherBody->isChecked() || ui->checkOtherPart->isChecked()) enable = true; ui->radioDependent->setEnabled(enable); ui->radioIndependent->setEnabled(enable); ui->radioXRef->setEnabled(enable); updateList(); } std::vector TaskFeaturePick::getFeatures() { features.clear(); QListIterator i(ui->listWidget->selectedItems()); while (i.hasNext()) { auto item = i.next(); if (item->isHidden()) continue; QString t = item->data(Qt::UserRole).toString(); features.push_back(t); } std::vector result; for (std::vector::const_iterator s = features.begin(); s != features.end(); ++s) result.push_back(App::GetApplication().getDocument(documentName.c_str())->getObject(s->toLatin1().data())); return result; } std::vector TaskFeaturePick::buildFeatures() { int index = 0; std::vector result; auto activeBody = PartDesignGui::getBody(false); if (!activeBody) return result; auto activePart = PartDesignGui::getPartFor(activeBody, false); for (std::vector::const_iterator st = statuses.begin(); st != statuses.end(); st++) { QListWidgetItem* item = ui->listWidget->item(index); if (item->isSelected() && !item->isHidden()) { QString t = item->data(Qt::UserRole).toString(); auto obj = App::GetApplication().getDocument(documentName.c_str())->getObject(t.toLatin1().data()); //build the dependend copy or reference if wanted by the user if (*st == otherBody || *st == otherPart || *st == notInBody) { if (!ui->radioXRef->isChecked()) { auto copy = makeCopy(obj, "", ui->radioIndependent->isChecked()); if (*st == otherBody) { activeBody->addFeature(copy); } else if (*st == otherPart) { auto oBody = PartDesignGui::getBodyFor(obj, false); if (!oBody) activePart->addObject(copy); else activeBody->addFeature(copy); } else if (*st == notInBody) { activeBody->addFeature(copy); // doesn't supposed to get here anything but sketch but to be on the safe side better to check if (copy->getTypeId().isDerivedFrom(Sketcher::SketchObject::getClassTypeId())) { Sketcher::SketchObject *sketch = static_cast(copy); PartDesignGui::fixSketchSupport(sketch); } } result.push_back(copy); } else { result.push_back(obj); } } else { result.push_back(obj); } break; } index++; } return result; } App::DocumentObject* TaskFeaturePick::makeCopy(App::DocumentObject* obj, std::string sub, bool independent) { App::DocumentObject* copy = nullptr; if( independent && (obj->isDerivedFrom(Sketcher::SketchObject::getClassTypeId()) || obj->isDerivedFrom(PartDesign::FeaturePrimitive::getClassTypeId()))) { //we do know that the created instance is a document object, as obj is one. But we do not know which //exact type auto name = std::string("Copy") + std::string(obj->getNameInDocument()); copy = App::GetApplication().getActiveDocument()->addObject(obj->getTypeId().getName(), name.c_str()); //copy over all properties std::vector props; std::vector cprops; obj->getPropertyList(props); copy->getPropertyList(cprops); auto it = cprops.begin(); for( App::Property* prop : props ) { //independent copys dont have links and are not attached if(independent && ( prop->getTypeId() == App::PropertyLink::getClassTypeId() || prop->getTypeId() == App::PropertyLinkList::getClassTypeId() || prop->getTypeId() == App::PropertyLinkSub::getClassTypeId() || prop->getTypeId() == App::PropertyLinkSubList::getClassTypeId()|| ( prop->getGroup() && strcmp(prop->getGroup(),"Attachment")==0) )) { ++it; continue; } App::Property* cprop = *it++; if( strcmp(prop->getName(), "Label") == 0 ) { static_cast(cprop)->setValue(name.c_str()); continue; } cprop->Paste(*prop); //we are a independent copy, therefore no external geometry was copied. WE therefore can delete all //contraints if(obj->isDerivedFrom(Sketcher::SketchObject::getClassTypeId())) static_cast(copy)->delConstraintsToExternal(); } } else { std::string name; if(!independent) name = std::string("Reference"); else name = std::string("Copy"); name += std::string(obj->getNameInDocument()); std::string entity; if(!sub.empty()) entity = sub; Part::PropertyPartShape* shapeProp = nullptr; // TODO Replace it with commands (2015-09-11, Fat-Zer) if(obj->isDerivedFrom(Part::Datum::getClassTypeId())) { copy = App::GetApplication().getActiveDocument()->addObject( obj->getClassTypeId().getName(), name.c_str() ); //we need to reference the individual datums and make again datums. This is important as //datum adjust their size dependend on the part size, hence simply copying the shape is //not enough long int mode = mmDeactivated; Part::Datum *datumCopy = static_cast(copy); if(obj->getTypeId() == PartDesign::Point::getClassTypeId()) { mode = mm0Vertex; } else if(obj->getTypeId() == PartDesign::Line::getClassTypeId()) { mode = mm1TwoPoints; } else if(obj->getTypeId() == PartDesign::Plane::getClassTypeId()) { mode = mmFlatFace; } else return copy; // TODO Recheck this. This looks strange in case of independent copy (2015-10-31, Fat-Zer) if(!independent) { datumCopy->Support.setValue(obj, entity.c_str()); datumCopy->MapMode.setValue(mode); } else if(!entity.empty()) { datumCopy->Shape.setValue(static_cast(obj)->Shape.getShape().getSubShape(entity.c_str())); } else { datumCopy->Shape.setValue(static_cast(obj)->Shape.getValue()); } } else if(obj->getTypeId() == PartDesign::ShapeBinder::getClassTypeId() || obj->isDerivedFrom(Part::Feature::getClassTypeId())) { copy = App::GetApplication().getActiveDocument()->addObject("PartDesign::ShapeBinder", name.c_str()); if(!independent) static_cast(copy)->Support.setValue(obj, entity.c_str()); else shapeProp = &static_cast(copy)->Shape; } if(independent && shapeProp) { if(entity.empty()) shapeProp->setValue(static_cast(obj)->Shape.getValue()); else shapeProp->setValue(static_cast(obj)->Shape.getShape().getSubShape(entity.c_str())); } } return copy; } void TaskFeaturePick::onSelectionChanged(const Gui::SelectionChanges& /*msg*/) { if (doSelection) return; doSelection = true; ui->listWidget->clearSelection(); for (Gui::SelectionSingleton::SelObj obj : Gui::Selection().getSelection()) { for (int row = 0; row < ui->listWidget->count(); row++) { QListWidgetItem *item = ui->listWidget->item(row); QString t = item->data(Qt::UserRole).toString(); if (t.compare(QString::fromLatin1(obj.FeatName))==0) { item->setSelected(true); } } } doSelection = false; } void TaskFeaturePick::onItemSelectionChanged() { if (doSelection) return; doSelection = true; ui->listWidget->blockSignals(true); Gui::Selection().clearSelection(); for (int row = 0; row < ui->listWidget->count(); row++) { QListWidgetItem *item = ui->listWidget->item(row); QString t = item->data(Qt::UserRole).toString(); if (item->isSelected()) { Gui::Selection().addSelection(documentName.c_str(), t.toLatin1()); } } ui->listWidget->blockSignals(false); doSelection = false; } void TaskFeaturePick::showExternal(bool val) { ui->checkOtherBody->setChecked(val); ui->checkOtherPart->setChecked(val); updateList(); } //************************************************************************** //************************************************************************** // TaskDialog //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TaskDlgFeaturePick::TaskDlgFeaturePick(std::vector &objects, const std::vector &status, boost::function)> afunc, boost::function)> wfunc) : TaskDialog(), accepted(false) { pick = new TaskFeaturePick(objects, status); Content.push_back(pick); acceptFunction = afunc; workFunction = wfunc; } TaskDlgFeaturePick::~TaskDlgFeaturePick() { //do the work now as before in accept() the dialog is still open, hence the work //function could not open annother dialog if (accepted) workFunction(pick->buildFeatures()); } //==== calls from the TaskView =============================================================== void TaskDlgFeaturePick::open() { } void TaskDlgFeaturePick::clicked(int) { } bool TaskDlgFeaturePick::accept() { accepted = acceptFunction(pick->getFeatures()); return accepted; } bool TaskDlgFeaturePick::reject() { accepted = false; return true; } void TaskDlgFeaturePick::showExternal(bool val) { pick->showExternal(val); } #include "moc_TaskFeaturePick.cpp"