diff --git a/src/Gui/TaskView/TaskDialog.cpp b/src/Gui/TaskView/TaskDialog.cpp index b2a783000..f53908056 100644 --- a/src/Gui/TaskView/TaskDialog.cpp +++ b/src/Gui/TaskView/TaskDialog.cpp @@ -24,6 +24,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# include #endif #include "TaskDialog.h" @@ -57,6 +58,20 @@ const std::vector &TaskDialog::getDialogContent(void) const return Content; } +bool TaskDialog::canClose() const +{ + QMessageBox msgBox; + msgBox.setText(tr("A dialog is already open in the task panel")); + msgBox.setInformativeText(QObject::tr("Do you want to close this dialog?")); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Yes); + int ret = msgBox.exec(); + if (ret == QMessageBox::Yes) + return true; + else + return false; +} + //==== calls from the TaskView =============================================================== void TaskDialog::open() diff --git a/src/Gui/TaskView/TaskDialog.h b/src/Gui/TaskView/TaskDialog.h index ec92a9dd2..0a6321d93 100644 --- a/src/Gui/TaskView/TaskDialog.h +++ b/src/Gui/TaskView/TaskDialog.h @@ -60,6 +60,7 @@ public: ButtonPosition buttonPosition() const { return pos; } const std::vector &getDialogContent(void) const; + bool canClose() const; /// tells the framework which buttons whisched for the dialog virtual QDialogButtonBox::StandardButtons getStandardButtons(void) const diff --git a/src/Mod/Part/App/AppPart.cpp b/src/Mod/Part/App/AppPart.cpp index aee9f90b1..a31156dc1 100644 --- a/src/Mod/Part/App/AppPart.cpp +++ b/src/Mod/Part/App/AppPart.cpp @@ -198,6 +198,7 @@ void PartExport initPart() Part::RuledSurface ::init(); Part::Loft ::init(); Part::Sweep ::init(); + Part::Offset ::init(); // Geometry types Part::Geometry ::init(); diff --git a/src/Mod/Part/App/PartFeatures.cpp b/src/Mod/Part/App/PartFeatures.cpp index 45aec90d8..71c732bf7 100644 --- a/src/Mod/Part/App/PartFeatures.cpp +++ b/src/Mod/Part/App/PartFeatures.cpp @@ -30,6 +30,7 @@ # include # include # include +# include #endif @@ -334,3 +335,67 @@ App::DocumentObjectExecReturn *Sweep::execute(void) return new App::DocumentObjectExecReturn(e->GetMessageString()); } } + +// ---------------------------------------------------------------------------- + +const char* Part::Offset::ModeEnums[]= {"Skin","Pipe", "RectoVerso",NULL}; +const char* Part::Offset::JoinEnums[]= {"Arc","Tangent", "Intersection",NULL}; + +PROPERTY_SOURCE(Part::Offset, Part::Feature) + +Offset::Offset() +{ + ADD_PROPERTY_TYPE(Source,(0),"Offset",App::Prop_None,"Source shape"); + ADD_PROPERTY_TYPE(Value,(1.0),"Offset",App::Prop_None,"Offset value"); + ADD_PROPERTY_TYPE(Mode,(long(0)),"Offset",App::Prop_None,"Mode"); + Mode.setEnums(ModeEnums); + ADD_PROPERTY_TYPE(Join,(long(0)),"Offset",App::Prop_None,"Join type"); + Join.setEnums(JoinEnums); + ADD_PROPERTY_TYPE(Intersection,(false),"Offset",App::Prop_None,"Intersection"); + ADD_PROPERTY_TYPE(SelfIntersection,(false),"Offset",App::Prop_None,"Self Intersection"); + ADD_PROPERTY_TYPE(Fill,(false),"Offset",App::Prop_None,"Fill offset"); +} + +short Offset::mustExecute() const +{ + if (Source.isTouched()) + return 1; + if (Value.isTouched()) + return 1; + if (Mode.isTouched()) + return 1; + if (Join.isTouched()) + return 1; + if (Intersection.isTouched()) + return 1; + if (SelfIntersection.isTouched()) + return 1; + if (Fill.isTouched()) + return 1; + return 0; +} + +void Offset::onChanged(const App::Property* prop) +{ + Part::Feature::onChanged(prop); +} + +App::DocumentObjectExecReturn *Offset::execute(void) +{ + App::DocumentObject* source = Source.getValue(); + if (!(source && source->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId()))) + return new App::DocumentObjectExecReturn("No source shape linked."); + double offset = Value.getValue(); + double tol = Precision::Confusion(); + bool inter = Intersection.getValue(); + bool self = SelfIntersection.getValue(); + short mode = (short)Mode.getValue(); + short join = (short)Join.getValue(); + bool fill = Fill.getValue(); + const TopoShape& shape = static_cast(source)->Shape.getShape(); + if (fabs(offset) > 2*tol) + this->Shape.setValue(shape.makeOffsetShape(offset, tol, inter, self, mode, join, fill)); + else + this->Shape.setValue(shape); + return App::DocumentObject::StdReturn; +} diff --git a/src/Mod/Part/App/PartFeatures.h b/src/Mod/Part/App/PartFeatures.h index e891cee59..1a26bd40b 100644 --- a/src/Mod/Part/App/PartFeatures.h +++ b/src/Mod/Part/App/PartFeatures.h @@ -25,6 +25,7 @@ #define PART_FEATURES_H #include +#include #include namespace Part @@ -106,6 +107,39 @@ private: static const char* TransitionEnums[]; }; +class Offset : public Part::Feature +{ + PROPERTY_HEADER(Part::Offset); + +public: + Offset(); + + App::PropertyLink Source; + App::PropertyFloat Value; + App::PropertyEnumeration Mode; + App::PropertyEnumeration Join; + App::PropertyBool Intersection; + App::PropertyBool SelfIntersection; + App::PropertyBool Fill; + + /** @name methods override feature */ + //@{ + /// recalculate the feature + App::DocumentObjectExecReturn *execute(void); + short mustExecute() const; + const char* getViewProviderName(void) const { + return "PartGui::ViewProviderOffset"; + } + //@} + +protected: + void onChanged (const App::Property* prop); + +private: + static const char* ModeEnums[]; + static const char* JoinEnums[]; +}; + } //namespace Part diff --git a/src/Mod/Part/Gui/AppPartGui.cpp b/src/Mod/Part/Gui/AppPartGui.cpp index 48f979675..e030eaae1 100644 --- a/src/Mod/Part/Gui/AppPartGui.cpp +++ b/src/Mod/Part/Gui/AppPartGui.cpp @@ -105,6 +105,7 @@ void PartGuiExport initPartGui() PartGui::ViewProviderRevolution ::init(); PartGui::ViewProviderLoft ::init(); PartGui::ViewProviderSweep ::init(); + PartGui::ViewProviderOffset ::init(); PartGui::ViewProviderCustom ::init(); PartGui::ViewProviderCustomPython ::init(); PartGui::ViewProviderBoolean ::init(); diff --git a/src/Mod/Part/Gui/Command.cpp b/src/Mod/Part/Gui/Command.cpp index d796c31c7..3db0f6c63 100644 --- a/src/Mod/Part/Gui/Command.cpp +++ b/src/Mod/Part/Gui/Command.cpp @@ -62,7 +62,6 @@ #include "TaskShapeBuilder.h" #include "TaskLoft.h" #include "TaskSweep.h" -#include "TaskOffset.h" #include "TaskCheckGeometry.h" @@ -1007,7 +1006,24 @@ CmdPartOffset::CmdPartOffset() void CmdPartOffset::activated(int iMsg) { - Gui::Control().showDialog(new PartGui::TaskOffset()); + App::DocumentObject* shape = getSelection().getObjectsOfType(Part::Feature::getClassTypeId()).front(); + std::string offset = getUniqueObjectName("Offset"); + + openCommand("Make Offset"); + doCommand(Doc,"App.ActiveDocument.addObject(\"Part::Offset\",\"%s\")",offset.c_str()); + doCommand(Doc,"App.ActiveDocument.%s.Source = App.ActiveDocument.%s" ,offset.c_str(), shape->getNameInDocument()); + doCommand(Doc,"App.ActiveDocument.%s.Value = 1.0",offset.c_str()); + updateActive(); + //if (isActiveObjectValid()) + // doCommand(Gui,"Gui.ActiveDocument.hide(\"%s\")",shape->getNameInDocument()); + doCommand(Gui,"Gui.ActiveDocument.setEdit('%s')",offset.c_str()); + + //commitCommand(); + adjustCameraPosition(); + + copyVisual(offset.c_str(), "ShapeColor", shape->getNameInDocument()); + copyVisual(offset.c_str(), "LineColor" , shape->getNameInDocument()); + copyVisual(offset.c_str(), "PointColor", shape->getNameInDocument()); } bool CmdPartOffset::isActive(void) diff --git a/src/Mod/Part/Gui/Resources/Part.qrc b/src/Mod/Part/Gui/Resources/Part.qrc index 8318095da..61315158a 100644 --- a/src/Mod/Part/Gui/Resources/Part.qrc +++ b/src/Mod/Part/Gui/Resources/Part.qrc @@ -19,6 +19,7 @@ icons/Part_Loft.svg icons/Part_Mirror.svg icons/Part_MirrorPNG.png + icons/Part_Offset.svg icons/Part_Revolve.svg icons/Part_RuledSurface.svg icons/Part_Section.svg diff --git a/src/Mod/Part/Gui/Resources/icons/Part_Offset.svg b/src/Mod/Part/Gui/Resources/icons/Part_Offset.svg new file mode 100644 index 000000000..47aeaaa8d --- /dev/null +++ b/src/Mod/Part/Gui/Resources/icons/Part_Offset.svg @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/Mod/Part/Gui/TaskOffset.cpp b/src/Mod/Part/Gui/TaskOffset.cpp index 5324e17ea..3741b792b 100644 --- a/src/Mod/Part/Gui/TaskOffset.cpp +++ b/src/Mod/Part/Gui/TaskOffset.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -43,7 +44,7 @@ #include #include #include -#include +#include using namespace PartGui; @@ -52,7 +53,7 @@ class OffsetWidget::Private { public: Ui_TaskOffset ui; - std::string document; + Part::Offset* offset; Private() { } @@ -63,13 +64,17 @@ public: /* TRANSLATOR PartGui::OffsetWidget */ -OffsetWidget::OffsetWidget(QWidget* parent) +OffsetWidget::OffsetWidget(Part::Offset* offset, QWidget* parent) : d(new Private()) { Gui::Application::Instance->runPythonCode("from FreeCAD import Base"); Gui::Application::Instance->runPythonCode("import Part"); + d->offset = offset; d->ui.setupUi(this); + d->ui.spinOffset->setValue(d->offset->Value.getValue()); + d->ui.spinOffset->setRange(-INT_MAX, INT_MAX); + d->ui.spinOffset->setSingleStep(0.1); } OffsetWidget::~OffsetWidget() @@ -77,13 +82,102 @@ OffsetWidget::~OffsetWidget() delete d; } +Part::Offset* OffsetWidget::getObject() const +{ + return d->offset; +} + +void OffsetWidget::on_spinOffset_valueChanged(double val) +{ + d->offset->Value.setValue((float)val); + if (d->ui.updateView->isChecked()) + d->offset->getDocument()->recomputeFeature(d->offset); +} + +void OffsetWidget::on_modeType_activated(int val) +{ + d->offset->Mode.setValue(val); + if (d->ui.updateView->isChecked()) + d->offset->getDocument()->recomputeFeature(d->offset); +} + +void OffsetWidget::on_joinType_activated(int val) +{ + d->offset->Join.setValue((float)val); + if (d->ui.updateView->isChecked()) + d->offset->getDocument()->recomputeFeature(d->offset); +} + +void OffsetWidget::on_intersection_toggled(bool on) +{ + d->offset->Intersection.setValue(on); + if (d->ui.updateView->isChecked()) + d->offset->getDocument()->recomputeFeature(d->offset); +} + +void OffsetWidget::on_selfIntersection_toggled(bool on) +{ + d->offset->SelfIntersection.setValue(on); + if (d->ui.updateView->isChecked()) + d->offset->getDocument()->recomputeFeature(d->offset); +} + +void OffsetWidget::on_fillOffset_toggled(bool on) +{ + d->offset->Fill.setValue(on); + if (d->ui.updateView->isChecked()) + d->offset->getDocument()->recomputeFeature(d->offset); +} + +void OffsetWidget::on_updateView_toggled(bool on) +{ + if (on) { + d->offset->getDocument()->recomputeFeature(d->offset); + } +} + bool OffsetWidget::accept() { + std::string name = d->offset->getNameInDocument(); + + try { + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Value = %f", + name.c_str(),d->ui.spinOffset->value()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Mode = %i", + name.c_str(),d->ui.modeType->currentIndex()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Join = %i", + name.c_str(),d->ui.joinType->currentIndex()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Intersection = %s", + name.c_str(),d->ui.intersection->isChecked() ? "True" : "False"); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.SelfIntersection = %s", + name.c_str(),d->ui.selfIntersection->isChecked() ? "True" : "False"); + + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.recompute()"); + if (!d->offset->isValid()) + throw Base::Exception(d->offset->getStatusString()); + Gui::Command::doCommand(Gui::Command::Gui,"Gui.ActiveDocument.resetEdit()"); + Gui::Command::commitCommand(); + } + catch (const Base::Exception& e) { + QMessageBox::warning(this, tr("Input error"), QString::fromAscii(e.what())); + return false; + } + return true; } bool OffsetWidget::reject() { + // get the support and Sketch + App::DocumentObject* source = d->offset->Source.getValue(); + if (source){ + Gui::Application::Instance->getViewProvider(source)->show(); + } + + // roll back the done things + Gui::Command::abortCommand(); + Gui::Command::doCommand(Gui::Command::Gui,"Gui.ActiveDocument.resetEdit()"); + return true; } @@ -98,9 +192,9 @@ void OffsetWidget::changeEvent(QEvent *e) /* TRANSLATOR PartGui::TaskOffset */ -TaskOffset::TaskOffset() +TaskOffset::TaskOffset(Part::Offset* offset) { - widget = new OffsetWidget(); + widget = new OffsetWidget(offset); taskbox = new Gui::TaskView::TaskBox( Gui::BitmapFactory().pixmap("Part_Offset"), widget->windowTitle(), true, 0); @@ -112,6 +206,11 @@ TaskOffset::~TaskOffset() { } +Part::Offset* TaskOffset::getObject() const +{ + return widget->getObject(); +} + void TaskOffset::open() { } diff --git a/src/Mod/Part/Gui/TaskOffset.h b/src/Mod/Part/Gui/TaskOffset.h index d1dc8d47b..019530438 100644 --- a/src/Mod/Part/Gui/TaskOffset.h +++ b/src/Mod/Part/Gui/TaskOffset.h @@ -27,6 +27,7 @@ #include #include +namespace Part { class Offset; } namespace PartGui { class OffsetWidget : public QWidget @@ -34,11 +35,21 @@ class OffsetWidget : public QWidget Q_OBJECT public: - OffsetWidget(QWidget* parent = 0); + OffsetWidget(Part::Offset*, QWidget* parent = 0); ~OffsetWidget(); bool accept(); bool reject(); + Part::Offset* getObject() const; + +private Q_SLOTS: + void on_spinOffset_valueChanged(double); + void on_modeType_activated(int); + void on_joinType_activated(int); + void on_intersection_toggled(bool); + void on_selfIntersection_toggled(bool); + void on_fillOffset_toggled(bool); + void on_updateView_toggled(bool); private: void changeEvent(QEvent *e); @@ -53,7 +64,7 @@ class TaskOffset : public Gui::TaskView::TaskDialog Q_OBJECT public: - TaskOffset(); + TaskOffset(Part::Offset*); ~TaskOffset(); public: @@ -61,6 +72,7 @@ public: bool accept(); bool reject(); void clicked(int); + Part::Offset* getObject() const; QDialogButtonBox::StandardButtons getStandardButtons() const { return QDialogButtonBox::Ok|QDialogButtonBox::Cancel; } diff --git a/src/Mod/Part/Gui/TaskOffset.ui b/src/Mod/Part/Gui/TaskOffset.ui index a2a0300c6..3cd639c69 100644 --- a/src/Mod/Part/Gui/TaskOffset.ui +++ b/src/Mod/Part/Gui/TaskOffset.ui @@ -7,7 +7,7 @@ 0 0 256 - 217 + 260 @@ -25,24 +25,14 @@ - - - Tolerance - - - - - - - Mode - - + + Skin @@ -60,15 +50,15 @@ - + Join type - - + + Arc @@ -86,29 +76,52 @@ - - + + Intersection - - + + Self-intersection + + + + Qt::Horizontal + + + + + + + Update view + + + true + + + + + + + Fill offset + + + spinOffset - spinTolerance - comboBox - comboBox_2 - checkBox - checkBox_2 + modeType + joinType + intersection + selfIntersection diff --git a/src/Mod/Part/Gui/ViewProviderMirror.cpp b/src/Mod/Part/Gui/ViewProviderMirror.cpp index b3c6d705a..3e25af0e5 100644 --- a/src/Mod/Part/Gui/ViewProviderMirror.cpp +++ b/src/Mod/Part/Gui/ViewProviderMirror.cpp @@ -46,6 +46,7 @@ #include #include "ViewProviderMirror.h" #include "DlgFilletEdges.h" +#include "TaskOffset.h" using namespace PartGui; @@ -395,3 +396,84 @@ bool ViewProviderSweep::onDelete(const std::vector &) { return true; } + +// --------------------------------------- + +PROPERTY_SOURCE(PartGui::ViewProviderOffset, PartGui::ViewProviderPart) + +ViewProviderOffset::ViewProviderOffset() +{ + sPixmap = "Part_Offset"; +} + +ViewProviderOffset::~ViewProviderOffset() +{ +} + +void ViewProviderOffset::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) +{ + QAction* act; + act = menu->addAction(QObject::tr("Edit offset"), receiver, member); + act->setData(QVariant((int)ViewProvider::Default)); + PartGui::ViewProviderPart::setupContextMenu(menu, receiver, member); +} + +bool ViewProviderOffset::setEdit(int ModNum) +{ + if (ModNum == ViewProvider::Default ) { + Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); + TaskOffset* offsetDlg = qobject_cast(dlg); + if (offsetDlg && offsetDlg->getObject() != this->getObject()) + offsetDlg = 0; // another pad left open its task panel + if (dlg && !offsetDlg) { + if (dlg->canClose()) + Gui::Control().closeDialog(); + else + return false; + } + + // clear the selection (convenience) + Gui::Selection().clearSelection(); + + // start the edit dialog + if (offsetDlg) + Gui::Control().showDialog(offsetDlg); + else + Gui::Control().showDialog(new TaskOffset(static_cast(getObject()))); + + return true; + } + else { + return ViewProviderPart::setEdit(ModNum); + } +} + +void ViewProviderOffset::unsetEdit(int ModNum) +{ + if (ModNum == ViewProvider::Default) { + // when pressing ESC make sure to close the dialog + Gui::Control().closeDialog(); + } + else { + PartGui::ViewProviderPart::unsetEdit(ModNum); + } +} + +std::vector ViewProviderOffset::claimChildren() const +{ + std::vector child; + child.push_back(static_cast(getObject())->Source.getValue()); + return child; +} + +bool ViewProviderOffset::onDelete(const std::vector &) +{ + // get the support and Sketch + Part::Offset* offset = static_cast(getObject()); + App::DocumentObject* source = offset->Source.getValue(); + if (source){ + Gui::Application::Instance->getViewProvider(source)->show(); + } + + return true; +} diff --git a/src/Mod/Part/Gui/ViewProviderMirror.h b/src/Mod/Part/Gui/ViewProviderMirror.h index aae6a79aa..407e06851 100644 --- a/src/Mod/Part/Gui/ViewProviderMirror.h +++ b/src/Mod/Part/Gui/ViewProviderMirror.h @@ -138,6 +138,26 @@ public: bool onDelete(const std::vector &); }; +class ViewProviderOffset : public ViewProviderPart +{ + PROPERTY_HEADER(PartGui::ViewProviderOffset); + +public: + /// constructor + ViewProviderOffset(); + /// destructor + virtual ~ViewProviderOffset(); + + /// grouping handling + std::vector claimChildren(void)const; + void setupContextMenu(QMenu*, QObject*, const char*); + bool onDelete(const std::vector &); + +protected: + virtual bool setEdit(int ModNum); + virtual void unsetEdit(int ModNum); +}; + } // namespace PartGui diff --git a/src/Mod/Part/Gui/Workbench.cpp b/src/Mod/Part/Gui/Workbench.cpp index 079815731..3563c82ed 100644 --- a/src/Mod/Part/Gui/Workbench.cpp +++ b/src/Mod/Part/Gui/Workbench.cpp @@ -89,7 +89,8 @@ Gui::ToolBarItem* Workbench::setupToolBars() const Gui::ToolBarItem* tool = new Gui::ToolBarItem(root); tool->setCommand("Part tools"); *tool << "Part_Extrude" << "Part_Revolve" << "Part_Mirror" << "Part_Fillet" - << "Part_Chamfer" << "Part_RuledSurface" << "Part_Loft" << "Part_Sweep"; + << "Part_Chamfer" << "Part_RuledSurface" << "Part_Loft" << "Part_Sweep" + << "Part_Offset"; Gui::ToolBarItem* boolop = new Gui::ToolBarItem(root); boolop->setCommand("Boolean");