/*************************************************************************** * Copyright (c) 2011 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 # include # include # include # include # include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "TaskFeatureParameters.h" #include "StyleParameters.h" #include "ViewProvider.h" #include "ViewProviderPy.h" using namespace PartDesignGui; PROPERTY_SOURCE_WITH_EXTENSIONS(PartDesignGui::ViewProvider, PartGui::ViewProviderPart) ViewProvider::ViewProvider() { ViewProviderSuppressibleExtension::initExtension(this); ViewProviderAttachExtension::initExtension(this); ViewProviderPreviewExtension::initExtension(this); } ViewProvider::~ViewProvider() = default; void ViewProvider::beforeDelete() { ViewProviderPart::beforeDelete(); } void ViewProvider::attach(App::DocumentObject* pcObject) { ViewProviderPart::attach(pcObject); auto* styleParameterManager = Base::provideService(); if (auto addSubFeature = getObject()) { bool isAdditive = addSubFeature->getAddSubType() == PartDesign::FeatureAddSub::Additive; PreviewColor.setValue( isAdditive ? styleParameterManager->resolve(StyleParameters::PreviewAdditiveColor) : styleParameterManager->resolve(StyleParameters::PreviewSubtractiveColor)); } } bool ViewProvider::doubleClicked() { try { QString text = QObject::tr("Edit %1").arg(QString::fromUtf8(getObject()->Label.getValue())); Gui::Command::openCommand(text.toUtf8()); Gui::cmdSetEdit(pcObject); } catch (const Base::Exception&) { Gui::Command::abortCommand(); } return true; } void ViewProvider::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) { QIcon iconObject = mergeGreyableOverlayIcons(Gui::BitmapFactory().pixmap("Part_ColorFace.svg")); QAction* act = menu->addAction(iconObject, QObject::tr("Set Face Colors"), receiver, member); act->setData(QVariant((int)ViewProvider::Color)); // Call the extensions Gui::ViewProvider::setupContextMenu(menu, receiver, member); } bool ViewProvider::setEdit(int ModNum) { if (ModNum == ViewProvider::Transform) { if (forwardToLink()) { return true; } // this is feature so we need to forward the transform to the body forwardedViewProvider = getBodyViewProvider(); return forwardedViewProvider->startEditing(ModNum); } else if (ModNum == ViewProvider::Default) { // When double-clicking on the item for this feature the // object unsets and sets its edit mode without closing // the task panel Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); TaskDlgFeatureParameters *featureDlg = qobject_cast(dlg); // NOTE: if the dialog is not partDesigan dialog the featureDlg will be NULL if (featureDlg && featureDlg->getViewObject() != this) { featureDlg = nullptr; // another feature left open its task panel } if (dlg && !featureDlg) { QMessageBox msgBox(Gui::getMainWindow()); msgBox.setText(QObject::tr("A dialog is already open in the task panel")); msgBox.setInformativeText(QObject::tr("Close this dialog?")); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); if (msgBox.exec() == QMessageBox::Yes) { Gui::Control().reject(); } else { return false; } } previouslyShownViewProvider = dynamic_cast( Gui::Application::Instance->getViewProvider(getBodyViewProvider()->getShownFeature()) ); // clear the selection (convenience) Gui::Selection().clearSelection(); // always change to PartDesign WB, remember where we come from oldWb = Gui::Command::assureWorkbench("PartDesignWorkbench"); // start the edit dialog if if (!featureDlg) { featureDlg = this->getEditDialog(); if (!featureDlg) { // Shouldn't generally happen throw Base::RuntimeError ("Failed to create new edit dialog."); } } Gui::Control().showDialog(featureDlg); return true; } else { return PartGui::ViewProviderPart::setEdit(ModNum); } } TaskDlgFeatureParameters *ViewProvider::getEditDialog() { throw Base::NotImplementedError("getEditDialog() not implemented"); } void ViewProvider::unsetEdit(int ModNum) { showPreview(false); // return to the WB we were in before editing the PartDesign feature if (!oldWb.empty()) { Gui::Command::assureWorkbench(oldWb.c_str()); } // ensure that after edit we still show the same feature if (previouslyShownViewProvider) { previouslyShownViewProvider->show(); } if (ModNum == ViewProvider::Default) { // when pressing ESC make sure to close the dialog Gui::Control().closeDialog(); } else { PartGui::ViewProviderPart::unsetEdit(ModNum); } } void ViewProvider::updateData(const App::Property* prop) { if (strcmp(prop->getName(), "PreviewShape") == 0) { updatePreview(); } else if (auto* previewExtension = getObject()->getExtensionByType(true)) { if (!previewExtension->isPreviewFresh() && isEditing()) { previewExtension->updatePreview(); } } inherited::updateData(prop); } void ViewProvider::attachPreview() { ViewProviderPreviewExtension::attachPreview(); auto* styleParameterManager = Base::provideService(); const double opacity = styleParameterManager->resolve(StyleParameters::PreviewToolOpacity).value; const double lineWidth = styleParameterManager->resolve(StyleParameters::PreviewLineWidth).value; pcPreviewShape->lineWidth = static_cast(lineWidth); pcToolPreview = new PartGui::SoPreviewShape; pcToolPreview->transparency = 1.0F - static_cast(opacity); pcToolPreview->color.connectFrom(&pcPreviewShape->color); pcPreviewRoot->addChild(pcToolPreview); } void ViewProvider::updatePreview() { ViewProviderPreviewExtension::updatePreview(); if (auto* addSubFeature = getObject()) { // we only want to show the additional tool preview for subtractive features if (addSubFeature->getAddSubType() != PartDesign::FeatureAddSub::Subtractive) { return; } Part::TopoShape toolShape = addSubFeature->AddSubShape.getShape(); updatePreviewShape(toolShape, pcToolPreview); } else { updatePreviewShape({}, pcToolPreview); } } void ViewProvider::makeChildrenVisible() { for (const auto child : claimChildren()) { if (auto vp = Gui::Application::Instance->getViewProvider(child)) { vp->show(); } } } void ViewProvider::onChanged(const App::Property* prop) { //if the object is inside of a body we make sure it is the only visible one on activation if(prop == &Visibility && Visibility.getValue()) { Part::BodyBase* body = Part::BodyBase::findBodyOf(getObject()); if(body) { //hide all features in the body other than this object for(App::DocumentObject* obj : body->Group.getValues()) { if(obj->isDerivedFrom() && obj != getObject()) { auto vpd = freecad_cast( Gui::Application::Instance->getViewProvider(obj)); if(vpd && vpd->Visibility.getValue()) vpd->Visibility.setValue(false); } } } } PartGui::ViewProviderPartExt::onChanged(prop); } Gui::ViewProvider* ViewProvider::startEditing(int ModNum) { // in case of transform we forward the request to body if (ModNum == Transform) { forwardedViewProvider = nullptr; if (!ViewProviderPart::startEditing(ModNum)) { return nullptr; } return forwardedViewProvider; } return ViewProviderPart::startEditing(ModNum); } void ViewProvider::setTipIcon(bool onoff) { isSetTipIcon = onoff; signalChangeIcon(); } QIcon ViewProvider::mergeColorfulOverlayIcons(const QIcon& orig) const { QIcon mergedicon = orig; if (isSetTipIcon) { static QPixmap px(Gui::BitmapFactory().pixmapFromSvg("PartDesign_Overlay_Tip", QSize(10, 10))); mergedicon = Gui::BitmapFactoryInst::mergePixmap(mergedicon, px, Gui::BitmapFactoryInst::BottomRight); } return Gui::ViewProvider::mergeColorfulOverlayIcons (mergedicon); } bool ViewProvider::onDelete(const std::vector&) { PartDesign::Feature* feature = getObject(); App::DocumentObject* previousfeat = feature->BaseFeature.getValue(); // Visibility - we want: // 1. If the visible object is not the one being deleted, we leave that one visible. // 2. If the visible object is the one being deleted, we make the previous object visible. if (isShow() && previousfeat && Gui::Application::Instance->getViewProvider(previousfeat)) { Gui::Application::Instance->getViewProvider(previousfeat)->show(); } // find surrounding features in the tree Part::BodyBase* body = PartDesign::Body::findBodyOf(getObject()); if (body) { // Deletion from the tree of a feature is handled by Document.removeObject, which has no clue // about what a body is. Therefore, Bodies, although an "activable" container, know nothing // about what happens at Document level with the features they contain. // // The Deletion command StdCmdDelete::activated, however does notify the viewprovider corresponding // to the feature (not body) of the imminent deletion (before actually doing it). // // Consequently, the only way of notifying a body of the imminent deletion of one of its features // so as to do the clean up required (moving basefeature references, tip management) is from the // viewprovider, so we call it here. // // fixes (#3084) FCMD_OBJ_CMD(body, "removeObject(" << Gui::Command::getObjectCmd(feature) << ')'); } makeChildrenVisible(); return true; } Part::TopoShape ViewProvider::getPreviewShape() const { if (auto feature = getObject()->getExtensionByType(true)) { // Feature is responsible for generating proper shape and this ViewProvider // is using it instead of more normal `Shape` property. return feature->PreviewShape.getShape(); } return {}; } void ViewProvider::showPreviousFeature(bool enable) { PartDesign::Feature* feature {getObject()}; PartDesign::Feature* baseFeature { nullptr }; ViewProvider* baseFeatureViewProvider { nullptr }; if (!feature) { return; } baseFeature = dynamic_cast(feature->BaseFeature.getValue()); if (baseFeature) { baseFeatureViewProvider = freecad_cast(Gui::Application::Instance->getViewProvider(baseFeature)); } if (!baseFeatureViewProvider) { baseFeatureViewProvider = this; } if (enable) { baseFeatureViewProvider->show(); hide(); } else { baseFeatureViewProvider->hide(); show(); } } void ViewProvider::setBodyMode(bool bodymode) { std::vector props; getPropertyList(props); auto vp = getBodyViewProvider(); if(!vp) return; for(App::Property* prop : props) { //we keep visibility and selectibility per object if(prop == &Visibility || prop == &Selectable) continue; //we hide only properties which are available in the body, not special ones if(!vp->getPropertyByName(prop->getName())) continue; prop->setStatus(App::Property::Hidden, bodymode); } } void ViewProvider::makeTemporaryVisible(bool onoff) { //make sure to not use the overridden versions, as they change properties if (onoff) { if (VisualTouched) { updateVisual(); } Gui::ViewProvider::show(); } else Gui::ViewProvider::hide(); } PyObject* ViewProvider::getPyObject() { if (!pyViewObject) pyViewObject = new ViewProviderPy(this); pyViewObject->IncRef(); return pyViewObject; } ViewProviderBody* ViewProvider::getBodyViewProvider() { auto body = PartDesign::Body::findBodyOf(getObject()); auto doc = getDocument(); if(body && doc) { auto vp = doc->getViewProvider(body); if(vp && vp->isDerivedFrom()) return static_cast(vp); } return nullptr; } namespace Gui { /// @cond DOXERR PROPERTY_SOURCE_TEMPLATE(PartDesignGui::ViewProviderPython, PartDesignGui::ViewProvider) /// @endcond // explicit template instantiation template class PartDesignGuiExport ViewProviderFeaturePythonT; }