diff --git a/src/Mod/TechDraw/App/DimensionReferences.cpp b/src/Mod/TechDraw/App/DimensionReferences.cpp index 3cfed7f2f8..0ff93e4819 100644 --- a/src/Mod/TechDraw/App/DimensionReferences.cpp +++ b/src/Mod/TechDraw/App/DimensionReferences.cpp @@ -86,6 +86,12 @@ ReferenceEntry& ReferenceEntry::operator=(const ReferenceEntry& otherRef) } +bool ReferenceEntry::operator==(const ReferenceEntry& otherRef) const +{ + return getObjectName() == otherRef.getObjectName() && getSubName() == otherRef.getSubName(); +} + + TopoDS_Shape ReferenceEntry::getGeometry() const { // Base::Console().Message("RE::getGeometry() - objectName: %s sub: **%s**\n", @@ -259,6 +265,19 @@ std::string ReferenceEntry::geomType() const return DrawUtil::getGeomTypeFromName(getSubName()); } +GeomType ReferenceEntry::geomEdgeType() const +{ + int geoId = TechDraw::DrawUtil::getIndexFromName(getSubName()); + auto dvp = static_cast(getObject()); + BaseGeomPtr geom = dvp->getGeomByIndex(geoId); + + if (geomType() == "Edge" && geom) { + return geom->getGeomType(); + } + + return GeomType::NOTDEF; +} + bool ReferenceEntry::isWholeObject() const { return getSubName().empty(); diff --git a/src/Mod/TechDraw/App/DimensionReferences.h b/src/Mod/TechDraw/App/DimensionReferences.h index 19b1a11681..47943623f6 100644 --- a/src/Mod/TechDraw/App/DimensionReferences.h +++ b/src/Mod/TechDraw/App/DimensionReferences.h @@ -33,6 +33,7 @@ #include #include +#include namespace App { @@ -59,6 +60,7 @@ public: ~ReferenceEntry() = default; ReferenceEntry& operator= (const ReferenceEntry& otherRef); + bool operator== (const ReferenceEntry& otherRef) const; App::DocumentObject* getObject() const; void setObject(App::DocumentObject* docObj) { m_object = docObj; } @@ -71,6 +73,8 @@ public: TopoDS_Shape getGeometry() const; std::string geomType() const; + GeomType geomEdgeType() const; + bool isWholeObject() const; Part::TopoShape asTopoShape() const; diff --git a/src/Mod/TechDraw/Gui/AppTechDrawGui.cpp b/src/Mod/TechDraw/Gui/AppTechDrawGui.cpp index 0b4feec49e..89263a47fa 100644 --- a/src/Mod/TechDraw/Gui/AppTechDrawGui.cpp +++ b/src/Mod/TechDraw/Gui/AppTechDrawGui.cpp @@ -125,11 +125,11 @@ PyMOD_INIT_FUNC(TechDrawGui) // instantiating the commands CreateTechDrawCommands(); - CreateTechDrawCommandsDims(); CreateTechDrawCommandsDecorate(); CreateTechDrawCommandsAnnotate(); CreateTechDrawCommandsExtensionDims(); CreateTechDrawCommandsExtensions(); + CreateTechDrawCommandsDims(); CreateTechDrawCommandsStack(); TechDrawGui::Workbench::init(); diff --git a/src/Mod/TechDraw/Gui/CMakeLists.txt b/src/Mod/TechDraw/Gui/CMakeLists.txt index fbd3c490c6..2951db9ad5 100644 --- a/src/Mod/TechDraw/Gui/CMakeLists.txt +++ b/src/Mod/TechDraw/Gui/CMakeLists.txt @@ -102,6 +102,7 @@ SET(TechDrawGui_SRCS CommandDecorate.cpp CommandAnnotate.cpp CommandExtensionDims.cpp + CommandExtensionDims.h CommandExtensionPack.cpp CommandStack.cpp DimensionValidators.cpp @@ -223,6 +224,8 @@ SET(TechDrawGui_SRCS TaskCosmeticCircle.cpp TaskCosmeticCircle.h TaskCosmeticCircle.ui + TechDrawHandler.cpp + TechDrawHandler.h Widgets/CompassDialWidget.cpp Widgets/CompassDialWidget.h Widgets/CompassWidget.cpp diff --git a/src/Mod/TechDraw/Gui/CommandCreateDims.cpp b/src/Mod/TechDraw/Gui/CommandCreateDims.cpp index 5b32915763..07e4e32630 100644 --- a/src/Mod/TechDraw/Gui/CommandCreateDims.cpp +++ b/src/Mod/TechDraw/Gui/CommandCreateDims.cpp @@ -46,21 +46,30 @@ #include #include #include +#include #include #include #include #include +#include "CommandExtensionDims.h" #include "DimensionValidators.h" #include "DrawGuiUtil.h" +#include "QGIViewDimension.h" +#include "QGVPage.h" +#include "MDIViewPage.h" #include "TaskDimRepair.h" #include "TaskLinkDim.h" +#include "TechDrawHandler.h" +#include "ViewProviderDimension.h" +#include "ViewProviderDrawingView.h" +#include "ViewProviderDrawingView.h" using namespace TechDrawGui; using namespace TechDraw; using namespace std; - +using DimensionType = TechDraw::DrawViewDimension::DimensionType; //=========================================================================== // utility routines @@ -82,16 +91,881 @@ void execExtent(Gui::Command* cmd, int direction); DrawViewDimension* dimensionMaker(TechDraw::DrawViewPart* dvp, std::string dimType, ReferenceVector references2d, ReferenceVector references3d); +DrawViewDimension* dimMaker(TechDraw::DrawViewPart* dvp, std::string dimType, + ReferenceVector references2d, ReferenceVector references3d); void positionDimText(DrawViewDimension* dim); -//NOTE: this is not shown in toolbar and doesn't always work right in the menu. -// should be removed. +void activateHandler(TechDrawHandler* newHandler) +{ + auto* mdi = dynamic_cast(Gui::getMainWindow()->activeWindow()); + if (!mdi) { + return; + } + + ViewProviderPage* vp = mdi->getViewProviderPage(); + if (!vp) { + return; + } + + QGVPage* viewPage = vp->getQGVPage(); + if (!viewPage) { + return; + } + viewPage->activateHandler(newHandler); +} + //=========================================================================== -// TechDraw_NewDimension +// TechDraw_Dimension //=========================================================================== -// this is deprecated. use individual add dimension commands. +class GeomSelectionSizes +{ +public: + GeomSelectionSizes(size_t s_pts, size_t s_lns, size_t s_cir, size_t s_ell, size_t s_spl, size_t s_fcs) : + s_pts(s_pts), s_lns(s_lns), s_cir(s_cir), s_ell(s_ell), s_spl(s_spl), s_fcs(s_fcs) {} + ~GeomSelectionSizes() {} + + bool hasPoints() const { return s_pts > 0; } + bool hasLines() const { return s_lns > 0; } + bool hasCirclesOrArcs() const { return s_cir > 0; } + bool hasEllipseAndCo() const { return s_ell > 0; } + bool hasSplineAndCo() const { return s_spl > 0; } + bool hasFaces() const { return s_fcs > 0; } + + bool has1Face() const { return s_pts == 0 && s_lns == 0 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 1; } + + bool has1Point() const { return s_pts == 1 && s_lns == 0 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has2Points() const { return s_pts == 2 && s_lns == 0 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has1Point1Line() const { return s_pts == 1 && s_lns == 1 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has3Points() const { return s_pts == 3 && s_lns == 0 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has4MorePoints() const { return s_pts >= 4 && s_lns == 0 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has2Points1Line() const { return s_pts == 2 && s_lns == 1 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has3MorePoints1Line() const { return s_pts >= 3 && s_lns == 1 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has1Point1Circle() const { return s_pts == 1 && s_lns == 0 && s_cir == 1 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has1Point1Ellipse() const { return s_pts == 1 && s_lns == 0 && s_cir == 0 && s_ell == 1 && s_spl == 0 && s_fcs == 0; } + + bool has1Line() const { return s_pts == 0 && s_lns == 1 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has2Lines() const { return s_pts == 0 && s_lns == 2 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has3MoreLines() const { return s_pts == 0 && s_lns >= 3 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has1Line1Circle() const { return s_pts == 0 && s_lns == 1 && s_cir == 1 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has1Line2Circles() const { return s_pts == 0 && s_lns == 1 && s_cir == 2 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has1Line1Ellipse() const { return s_pts == 0 && s_lns == 1 && s_cir == 0 && s_ell == 1 && s_spl == 0 && s_fcs == 0; } + + bool has1Circle() const { return s_pts == 0 && s_lns == 0 && s_cir == 1 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has2Circles() const { return s_pts == 0 && s_lns == 0 && s_cir == 2 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has3MoreCircles() const { return s_pts == 0 && s_lns == 0 && s_cir >= 3 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has1Circle1Ellipse() const { return s_pts == 0 && s_lns == 0 && s_cir == 1 && s_ell == 1 && s_spl == 0 && s_fcs == 0; } + + bool has1Ellipse() const { return s_pts == 0 && s_lns == 0 && s_cir == 0 && s_ell == 1 && s_spl == 0 && s_fcs == 0; } + bool has2Ellipses() const { return s_pts == 0 && s_lns == 0 && s_cir == 0 && s_ell == 2 && s_spl == 0 && s_fcs == 0; } + bool has1Point1Spline1MoreEdge() const { return s_pts == 1 && s_spl >= 1 && (s_lns + s_cir + s_ell + s_spl) == 2 && s_fcs == 0; } + + size_t s_pts, s_lns, s_cir, s_ell, s_spl, s_fcs; +}; + +class TDHandlerDimension : public TechDrawHandler, + public Gui::SelectionObserver +{ +public: + explicit TDHandlerDimension(ReferenceVector refs, TechDraw::DrawViewPart* pFeat) + : SelectionObserver(true) + , specialDimension(SpecialDimension::None) + , availableDimension(AvailableDimension::FIRST) + , previousPos(QPoint(0, 0)) + , selPoints({}) + , selLine({}) + , selCircleArc({}) + , selEllipseArc({}) + , selSplineAndCo({}) + , selFaces({}) + , addedRef(ReferenceEntry()) + , removedRef(ReferenceEntry()) + , initialSelection(std::move(refs)) + , partFeat(pFeat) + , dim(nullptr) + , blockRemoveSel(false) + { + } + ~TDHandlerDimension() + { + } + + enum class AvailableDimension { + FIRST, + SECOND, + THIRD, + FOURTH, + FIFTH, + RESET + }; + + enum class SpecialDimension { + LineOr2PointsDistance, + None + }; + + + void activated() + { + Gui::Selection().setSelectionStyle(Gui::SelectionSingleton::SelectionStyle::GreedySelection); + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Insert Dimension")); + handleInitialSelection(); + } + + void deactivated() + { + Gui::Selection().setSelectionStyle(Gui::SelectionSingleton::SelectionStyle::NormalSelection); + Gui::Command::abortCommand(); + } + + void keyPressEvent(QKeyEvent* event) + { + if (event->key() == Qt::Key_M) { + if (availableDimension == AvailableDimension::FIRST) { + availableDimension = AvailableDimension::SECOND; + } + else if (availableDimension == AvailableDimension::SECOND) { + availableDimension = AvailableDimension::THIRD; + } + else if (availableDimension == AvailableDimension::THIRD) { + availableDimension = AvailableDimension::FOURTH; + } + else if (availableDimension == AvailableDimension::FOURTH) { + availableDimension = AvailableDimension::FIFTH; + } + else if (availableDimension == AvailableDimension::FIFTH || availableDimension == AvailableDimension::RESET) { + availableDimension = AvailableDimension::FIRST; + } + makeAppropriateDimension(previousPos); + event->accept(); + } + else if (event->key() == Qt::Key_Z && (QApplication::keyboardModifiers() & Qt::ControlModifier)) { + // User trying to cancel with Ctrl-Z + quit(); + event->accept(); + } + } + void keyReleaseEvent(QKeyEvent* event) + { + if (event->key() == Qt::Key_Z && (QApplication::keyboardModifiers() & Qt::ControlModifier)) { + // User trying to cancel with Ctrl-Z + quit(); + event->accept(); + } + else { + TechDrawHandler::keyReleaseEvent(event); + } + } + + void mouseMoveEvent(QMouseEvent* event) override + { + previousPos = event->pos(); + + //Change distance dimension based on position of mouse. + if (specialDimension == SpecialDimension::LineOr2PointsDistance){ + updateDistanceType(event->pos()); + } + + moveDimension(event->pos()); + } + + QGIDatumLabel* getDimLabel(DrawViewDimension* d) + { + auto* vp = dynamic_cast(Gui::Application::Instance->getViewProvider(d)); + if (!vp) { + return nullptr; + } + auto* qgivDimension(dynamic_cast(vp->getQView())); + if (!qgivDimension) { + return nullptr; + } + return qgivDimension->getDatumLabel(); + } + void moveDimension(QPoint& pos) + { + if (!dim) { return; } + auto label = getDimLabel(dim); + if (!label) { return; } + + label->setPos(getDimPositionToBe(pos)); + } + QPointF getDimPositionToBe(QPoint& pos) + { + auto* vpp = dynamic_cast(Gui::Application::Instance->getViewProvider(partFeat)); + if (!vpp) { return QPointF(); } + + QPointF scenePos = viewPage->mapToScene(pos) - vpp->getQView()->pos(); + return scenePos; + } + void finishDimensionMove() + { + auto label = getDimLabel(dim); + double x = Rez::appX(label->X()), y = Rez::appX(label->Y()); + Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.X = %f", + dim->getNameInDocument(), x); + Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.Y = %f", + dim->getNameInDocument(), -y); + } + + bool mousePressEvent(QMouseEvent* event) override + { + if (event->button() == Qt::RightButton && dim) { + Gui::Selection().clearSelection(); + clearAndRestartCommand(); + event->accept(); + return true; + } + return TechDrawHandler::mousePressEvent(event); + } + + bool mouseReleaseEvent(QMouseEvent* event) override + { + //Base::Console().Warning("mouseReleaseEvent TH\n"); + bool finalize = true; + + if (removedRef.hasGeometry()) { + finalize = false; + //Base::Console().Warning("RmvSelection \n"); + // Remove the reference from the vector + ReferenceVector& selVector = getSelectionVector(removedRef); + selVector.erase(std::remove(selVector.begin(), selVector.end(), removedRef), selVector.end()); + + if (!selectionEmpty()) { + availableDimension = AvailableDimension::FIRST; + makeAppropriateDimension(event->pos()); + } + else { + clearAndRestartCommand(); + } + removedRef = ReferenceEntry(); + } + + if (addedRef.hasGeometry()) { + finalize = false; + //Base::Console().Warning("AddSelection\n"); + //add the geometry to its type vector. Temporarily if not selAllowed + ReferenceVector& selVector = getSelectionVector(addedRef); + selVector.push_back(addedRef); + + availableDimension = AvailableDimension::FIRST; + bool selAllowed = makeAppropriateDimension(event->pos()); + + if (!selAllowed) { + // remove from selection + blockRemoveSel = true; + + Gui::Selection().rmvSelection(addedRef.getObject()->getDocument()->getName(), addedRef.getObject()->getNameInDocument(), addedRef.getSubName().c_str()); + blockRemoveSel = false; + + if (selVector == selFaces) { + // if sel face and not allowed, then a dimension is being created + // and user clicked on a face to drop it. + // Better would be to disable face selectability when needed. + finalize = true; + } + } + addedRef = ReferenceEntry(); + } + + + // Finalize if click on empty space. + if (finalize && dim) { + finalizeCommand(); + } + return true; + } + + void onSelectionChanged(const Gui::SelectionChanges& msg) + { + //Base::Console().Warning("onSelectionChanged %d - --%s--\n", (int)msg.Type, msg.pSubName); + + if (msg.Type == Gui::SelectionChanges::ClrSelection) { + //clearAndRestartCommand(); + return; + } + + if (msg.pSubName[0] == '\0' || msg.Object.getObjectName().empty() + || msg.Object.getDocument() != getPage()->getDocument()) { + if (msg.Type == Gui::SelectionChanges::AddSelection) { + Gui::Selection().rmvSelection(msg.pDocName, msg.pObjectName, msg.pSubName); + } + return; + } + + /*if (msg.Type == Gui::SelectionChanges::SetPreselect) { + Base::Console().Warning("SetPreselect\n"); + std::string geomName = DrawUtil::getGeomTypeFromName(msg.pSubName); + edgeOrPointPreselected = geomName == "Edge" || geomName == "Vertex"; + return; + } + else if (msg.Type == Gui::SelectionChanges::RmvPreselect) { + Base::Console().Warning("RmvPreselect\n"); + edgeOrPointPreselected = false; + return; + }*/ + + App::DocumentObject* obj = msg.Object.getObject(); + if (!obj) { + return; + } + + auto* dvp = dynamic_cast(obj); + if (!dvp) { + return; + } + + if (partFeat && partFeat != dvp) { + // Dimensions can only be within one view. + + if (msg.Type == Gui::SelectionChanges::AddSelection) { + Gui::Selection().rmvSelection(msg.pDocName, msg.pObjectName, msg.pSubName); + } + return; + } + else { + partFeat = dvp; + } + + if (msg.Type == Gui::SelectionChanges::AddSelection) { + addedRef = ReferenceEntry(dvp, msg.pSubName); + } + else if (msg.Type == Gui::SelectionChanges::RmvSelection) { + if (!blockRemoveSel) { + removedRef = ReferenceEntry(dvp, msg.pSubName); + } + } + } + +private: + QString getCrosshairCursorSVGName() const override + { + return QString::fromLatin1("TechDraw_Dimension_Pointer"); + } + +protected: + SpecialDimension specialDimension; + AvailableDimension availableDimension; + + QPoint previousPos; + + ReferenceVector selPoints; + ReferenceVector selLine; + ReferenceVector selCircleArc; + ReferenceVector selEllipseArc; + ReferenceVector selSplineAndCo; + ReferenceVector selFaces; + + ReferenceEntry addedRef; + ReferenceEntry removedRef; + + ReferenceVector initialSelection; + + TechDraw::DrawViewPart* partFeat; + + DrawViewDimension* dim; + + bool blockRemoveSel; + + void clearRefVectors() + { + selPoints.clear(); + selLine.clear(); + selCircleArc.clear(); + selEllipseArc.clear(); + selSplineAndCo.clear(); + selFaces.clear(); + } + + void handleInitialSelection() + { + if (initialSelection.size() == 0) { + return; + } + + availableDimension = AvailableDimension::FIRST; + + // Add the selected elements to their corresponding selection vectors + for (auto& ref : initialSelection) { + ReferenceVector& selVector = getSelectionVector(ref); + selVector.push_back(ref); + } + + // See if the selection is valid + bool selAllowed = makeAppropriateDimension(QPoint()); + + if (!selAllowed) { + clearRefVectors(); + } + } + + void finalizeCommand() + { + //Base::Console().Warning("finalizeCommand \n"); + + finishDimensionMove(); + + // Ask for the value of datum dimensions + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/TechDraw"); + + Gui::Command::commitCommand(); + + // Touch the parent feature so the dimension in tree view appears as a child + partFeat->touch(true); + + // This code enables the continuous creation mode. + bool continuousMode = hGrp->GetBool("ContinuousCreationMode", true); + if (continuousMode) { + Gui::Selection().clearSelection(); + clearAndRestartCommand(); + } + else { + viewPage->deactivateHandler(); // no code after this line, Handler get deleted in QGVPage + } + } + + ReferenceVector& getSelectionVector(ReferenceEntry& ref) + { + static ReferenceVector emptyVector; + + std::string subName = ref.getSubName(); + auto* dvp = static_cast(ref.getObject()); + + std::string geomName = DrawUtil::getGeomTypeFromName(subName); + if (geomName == "Face") { + return selFaces; + } + else if (geomName == "Edge") { + + int GeoId(TechDraw::DrawUtil::getIndexFromName(subName)); + TechDraw::BaseGeomPtr geom = dvp->getGeomByIndex(GeoId); + if (!geom) { + return emptyVector; + } + + if (geom->getGeomType() == TechDraw::GENERIC) { + TechDraw::GenericPtr gen1 = std::static_pointer_cast(geom); + if (gen1->points.size() < 2) { + return emptyVector; + } + return selLine; + //Base::Vector3d line = gen1->points.at(1) - gen1->points.at(0); + } + else if (geom->getGeomType() == TechDraw::CIRCLE || geom->getGeomType() == TechDraw::ARCOFCIRCLE) { + return selCircleArc; + } + else if (geom->getGeomType() == TechDraw::ELLIPSE || geom->getGeomType() == TechDraw::ARCOFELLIPSE) { + return selEllipseArc; + } + else if (geom->getGeomType() == TechDraw::BSPLINE) { + //TechDraw::BSplinePtr spline = std::static_pointer_cast(geom); + //if (spline->isCircle()) { + // return isBSplineCircle; + //} + //else { + //} + return selSplineAndCo; + } + } + else if (geomName == "Vertex") { + return selPoints; + } + + return emptyVector; + } + + /* + bool notSelectedYet(const ReferenceEntry& elem) + { + auto contains = [&](const ReferenceVector& vec, const ReferenceEntry& elem) { + for (const auto& x : vec) + { + if (x == elem){ + return true; + } + } + return false; + }; + + return !contains(selPoints, elem) + && !contains(selLine, elem) + && !contains(selCircleArc, elem) + && !contains(selEllipseArc, elem) + && !contains(selFaces, elem); + }*/ + + bool selectionEmpty() + { + return selPoints.empty() && selLine.empty() && selCircleArc.empty() && selEllipseArc.empty() && selFaces.empty(); + } + + bool makeAppropriateDimension(QPoint& pos) { + bool selAllowed = false; + //Base::Console().Warning("makeAppropriateDimension %d %d %d %d %d %d\n", selPoints.size(), selLine.size(), selCircleArc.size(), selEllipseArc.size(), selSplineAndCo.size(), selFaces.size()); + + GeomSelectionSizes selection(selPoints.size(), selLine.size(), selCircleArc.size(), selEllipseArc.size(), selSplineAndCo.size(), selFaces.size()); + if (selection.hasFaces()) { + makeCts_Faces(selAllowed, pos); + } + else if (selection.hasPoints()) { + if (selection.has1Point()) { selAllowed = true; } + else if (selection.has2Points()) { makeCts_2Point(selAllowed, pos); } + else if (selection.has3Points()) { makeCts_3Point(selAllowed, pos); } + else if (selection.has1Point1Line()) { makeCts_1Point1Line(selAllowed, pos); } + else if (selection.has1Point1Circle()) { makeCts_1Point1Circle(selAllowed, pos); } + else if (selection.has1Point1Ellipse()) { makeCts_1Point1Ellipse(selAllowed, pos); } + } + else if (selection.hasLines()) { + if (selection.has1Line()) { makeCts_1Line(selAllowed, pos); } + else if (selection.has2Lines()) { makeCts_2Line(selAllowed, pos); } + else if (selection.has1Line1Circle()) { makeCts_1Line1Circle(selAllowed, pos); } + else if (selection.has1Line1Ellipse()) { makeCts_1Line1Ellipse(selAllowed, pos); } + } + else if (selection.hasCirclesOrArcs()) { + if (selection.has1Circle()) { makeCts_1Circle(selAllowed, pos); } + else if (selection.has2Circles()) { makeCts_2Circle(selAllowed, pos); } + } + else if (selection.hasEllipseAndCo()) { + if (selection.has1Ellipse()) { makeCts_1Ellipse(selAllowed, pos); } + if (selection.has2Ellipses()) { makeCts_2Ellipses(selAllowed, pos); } + } + return selAllowed; + } + + // TODO + void makeCts_Faces(bool& selAllowed, QPoint& pos) + { + //area + if (availableDimension == AvailableDimension::FIRST) { + /*restartCommand(QT_TRANSLATE_NOOP("Command", "Add Area dimension")); + createAreaDimension(pos); + selAllowed = true; + availableDimension = AvailableDimension::RESET;*/ + } + } + + void makeCts_2Point(bool& selAllowed, QPoint& pos) + { + //distance + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance dimension")); + createDistanceDimension("Distance", selPoints[0], selPoints[1], pos); + specialDimension = SpecialDimension::LineOr2PointsDistance; + selAllowed = true; + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_3Point(bool& selAllowed, QPoint& pos) + { + //angle + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add 3-points angle dimension")); + create3pAngleDimension(selPoints[0], selPoints[1], selPoints[2], pos); + selAllowed = true; + } + else if (availableDimension == AvailableDimension::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add 3-points angle dimension")); + create3pAngleDimension(selPoints[1], selPoints[2], selPoints[0], pos); + } + else if (availableDimension == AvailableDimension::THIRD) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add 3-points angle dimension")); + create3pAngleDimension(selPoints[2], selPoints[0], selPoints[1], pos); + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_1Point1Line(bool& selAllowed, QPoint& pos) + { + //distance + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add point to line Distance dimension")); + createDistanceDimension("Distance", selPoints[0], selLine[0], pos); + selAllowed = true; + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_1Point1Circle(bool& selAllowed, QPoint& pos) + { + //Distance + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add length dimension")); + createDistanceDimension("Distance", selPoints[0], selCircleArc[0], pos); + selAllowed = true; + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_1Point1Ellipse(bool& selAllowed, QPoint& pos) + { + //Distance + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add length dimension")); + createDistanceDimension("Distance", selPoints[0], selEllipseArc[0], pos); + selAllowed = true; + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_1Line(bool& selAllowed, QPoint& pos) + { + //distance + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add length dimension")); + createDistanceDimension("Distance", selLine[0], ReferenceEntry(), pos); + specialDimension = SpecialDimension::LineOr2PointsDistance; + selAllowed = true; + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_2Line(bool& selAllowed, QPoint& pos) + { + //angle (if parallel: Distance (see in createAngleDimension)). + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Angle dimension")); + createAngleDimension(selLine[0], selLine[1], pos); + selAllowed = true; + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_1Line1Circle(bool& selAllowed, QPoint& pos) + { + //distance + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add circle to line Distance dimension")); + createDistanceDimension("Distance", selCircleArc[0], selLine[0], pos); + selAllowed = true; + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_1Line1Ellipse(bool& selAllowed, QPoint& pos) + { + //distance + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add ellipse to line Distance dimension")); + createDistanceDimension("Distance", selEllipseArc[0], selLine[0], pos); + selAllowed = true; + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_1Circle(bool& selAllowed, QPoint& pos) + { + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Radius dimension")); + createRadiusDiameterDimension(selCircleArc[0], pos, true); + selAllowed = true; + } + if (availableDimension == AvailableDimension::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Radius dimension")); + createRadiusDiameterDimension(selCircleArc[0], pos, false); + if (selCircleArc[0].geomEdgeType() != TechDraw::ARCOFCIRCLE) { + availableDimension = AvailableDimension::RESET; + } + } + if (availableDimension == AvailableDimension::THIRD) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Arc Length dimension")); + createArcLengthDimension(selCircleArc[0], pos); + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_2Circle(bool& selAllowed, QPoint& pos) + { + //Distance + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add circle to circle Distance dimension")); + createDistanceDimension("Distance", selCircleArc[0], selCircleArc[0], pos); + selAllowed = true; + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_1Ellipse(bool& selAllowed, QPoint& pos) + { + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Radius dimension")); + createRadiusDiameterDimension(selEllipseArc[0], pos, true); + selAllowed = true; + } + if (availableDimension == AvailableDimension::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Radius dimension")); + createRadiusDiameterDimension(selEllipseArc[0], pos, false); + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_2Ellipses(bool& selAllowed, QPoint& pos) + { + //Distance + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add ellipse to ellipse Distance dimension")); + createDistanceDimension("Distance", selEllipseArc[0], selEllipseArc[0], pos); + selAllowed = true; + availableDimension = AvailableDimension::RESET; + } + } + + //TODO + void createAreaDimension(QPoint& pos) + { + /*// see CmdTechDrawExtensionAreaAnnotation::activated + Base::Vector3d center; + double totalArea = 0.0; + for (auto& ref : selFaces) { + TechDraw::FacePtr face = partFeat->getFace(ref.getSubName()); + if (!face) { + continue; + } + + GProp_GProps faceProps; + BRepGProp::SurfaceProperties(face->toOccFace(), faceProps); + + double faceArea = faceProps.Mass(); + totalArea += faceArea; + center += faceArea * DrawUtil::toVector3d(faceProps.CentreOfMass()); + } + if (totalArea > 0.0) { + center /= totalArea; + } + + //function (and file) to create in CommandExtensionPack.h + auto* areaBalloon = createAreaBalloon(totalArea, center);*/ + } + + void createRadiusDiameterDimension(ReferenceEntry ref, QPoint& pos, bool firstCstr) { + bool isCircleGeom = true; + + int GeoId(TechDraw::DrawUtil::getIndexFromName(ref.getSubName())); + TechDraw::BaseGeomPtr geom = partFeat->getGeomByIndex(GeoId); + isCircleGeom = geom->getGeomType() == TechDraw::CIRCLE || TechDraw::ELLIPSE; + + // Use same preference as in sketcher? + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/TechDraw/dimensioning"); + bool dimensioningDiameter = hGrp->GetBool("DimensioningDiameter", true); + bool dimensioningRadius = hGrp->GetBool("DimensioningRadius", true); + + if ((firstCstr && dimensioningRadius && !dimensioningDiameter) || + (!firstCstr && !dimensioningRadius && dimensioningDiameter) || + (firstCstr && dimensioningRadius && dimensioningDiameter && !isCircleGeom) || + (!firstCstr && dimensioningRadius && dimensioningDiameter && isCircleGeom)) { + dim = dimMaker(partFeat, "Radius", { ref }, {}); + } + else { + dim = dimMaker(partFeat, "Diameter", { ref }, {}); + } + + moveDimension(pos); + } + + void createAngleDimension(ReferenceEntry ref1, ReferenceEntry ref2, QPoint& pos) { + if (TechDraw::isValidMultiEdge({ ref1, ref2 }) != isAngle) { + //isValidMultiEdge check if lines are parallel. + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance dimension")); + createDistanceDimension("Distance", ref1, ref2, pos); + return; + } + + dim = dimMaker(partFeat, "Angle", {ref1, ref2}, {}); + + moveDimension(pos); + } + + void create3pAngleDimension(ReferenceEntry ref1, ReferenceEntry ref2, ReferenceEntry ref3, QPoint& pos) + { + dim = dimMaker(partFeat, "Angle3Pt", {ref1, ref2, ref3}, {}); + + moveDimension(pos); + } + + void createArcLengthDimension(ReferenceEntry ref, QPoint& pos) + { + dim = makeArcLengthDimension(ref); + + moveDimension(pos); + } + + void createDistanceDimension(std::string type, ReferenceEntry ref1, ReferenceEntry ref2, QPoint& pos) { + ReferenceVector refs = { ref1 }; + if (ref2.hasGeometry()) { // if valid + refs.push_back(ref2); + } + + dim = dimMaker(partFeat, type, refs, {}); + + moveDimension(pos); + } + + void updateDistanceType(QPoint& pos) + { + if (!dim) { + return; + } + + auto type = static_cast(dim->Type.getValue()); + + TechDraw::pointPair pp = dim->getLinearPoints(); + Base::Vector3d pnt1 = Rez::guiX(pp.first()); + Base::Vector3d pnt2 = Rez::guiX(pp.second()); + + QPointF fpos = getDimPositionToBe(pos); + + double minX, minY, maxX, maxY; + minX = min(pnt1.x, pnt2.x); + maxX = max(pnt1.x, pnt2.x); + minY = min(pnt1.y, pnt2.y); + maxY = max(pnt1.y, pnt2.y); + + std::string newType = "Distance"; + if (fpos.x() > minX && fpos.x() < maxX + && (fpos.y() < minY || fpos.y() > maxY) && type != DimensionType::DistanceX) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceX dimension")); + newType = "DistanceX"; + } + else if (fpos.y() > minY && fpos.y() < maxY + && (fpos.x() < minX || fpos.x() > maxX) && type != DimensionType::DistanceY) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceY dimension")); + newType = "DistanceY"; + } + else if ((((fpos.y() < minY || fpos.y() > maxY) && (fpos.x() < minX || fpos.x() > maxX)) + || (fpos.y() > minY && fpos.y() < maxY && fpos.x() > minX && fpos.x() < maxX)) && type != DimensionType::Distance) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance dimension")); + } + else { + return; + } + specialDimension = SpecialDimension::LineOr2PointsDistance; + + if (selLine.size() == 1) { + createDistanceDimension(newType, selLine[0], ReferenceEntry(), pos); + } + else { + createDistanceDimension(newType, selPoints[0], selPoints[1], pos); + } + } + + void restartCommand(const char* cstrName) { + specialDimension = SpecialDimension::None; + Gui::Command::abortCommand(); + Gui::Command::openCommand(cstrName); + + dim = nullptr; + } + + void clearAndRestartCommand() { + Gui::Command::abortCommand(); + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Dimension")); + specialDimension = SpecialDimension::None; + previousPos = QPoint(0,0); + clearRefVectors(); + partFeat = nullptr; + dim = nullptr; + } +}; DEF_STD_CMD_A(CmdTechDrawDimension) @@ -101,7 +975,9 @@ CmdTechDrawDimension::CmdTechDrawDimension() sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Insert Dimension"); - sToolTipText = sMenuText; + sToolTipText = QT_TR_NOOP("Dimension contextually based on your selection.\n" + "Depending on your selection you might have several dimensions available. You can cycle through them using the M key.\n" + "Left clicking on empty space will validate the current Dimensiont. Right clicking or pressing Esc will cancel."); sWhatsThis = "TechDraw_Dimension"; sStatusTip = sToolTipText; sPixmap = "TechDraw_Dimension"; @@ -110,6 +986,13 @@ CmdTechDrawDimension::CmdTechDrawDimension() void CmdTechDrawDimension::activated(int iMsg) { Q_UNUSED(iMsg); + + ReferenceVector references2d; + ReferenceVector references3d; + TechDraw::DrawViewPart* partFeat = + TechDraw::getReferencesFromSelection(references2d, references3d); + + activateHandler(new TDHandlerDimension(references2d, partFeat)); } bool CmdTechDrawDimension::isActive() @@ -119,6 +1002,41 @@ bool CmdTechDrawDimension::isActive() return (havePage && haveView); } + +// Comp for dimension tools ============================================= + +class CmdTechDrawCompDimensionTools : public Gui::GroupCommand +{ +public: + CmdTechDrawCompDimensionTools() + : GroupCommand("TechDraw_CompDimensionTools") + { + sAppModule = "TechDraw"; + sGroup = "TechDraw"; + sMenuText = QT_TR_NOOP("Dimension"); + sToolTipText = QT_TR_NOOP("Dimension tools."); + sWhatsThis = "TechDraw_CompDimensionTools"; + sStatusTip = sToolTipText; + eType = ForEdit; + + setCheckable(false); + setRememberLast(false); + + addCommand("TechDraw_Dimension"); + addCommand(); //separator + addCommand("TechDraw_LengthDimension"); + addCommand("TechDraw_HorizontalDimension"); + addCommand("TechDraw_VerticalDimension"); + addCommand("TechDraw_RadiusDimension"); + addCommand("TechDraw_DiameterDimension"); + addCommand("TechDraw_AngleDimension"); + addCommand("TechDraw_3PtAngleDimension"); + addCommand("TechDraw_ExtensionCreateLengthArc"); + } + + const char* className() const override { return "CmdTechDrawCompDimensionTools"; } +}; + //=========================================================================== // TechDraw_RadiusDimension //=========================================================================== @@ -1345,6 +2263,7 @@ void CreateTechDrawCommandsDims() rcCmdMgr.addCommand(new CmdTechDrawLinkDimension()); rcCmdMgr.addCommand(new CmdTechDrawLandmarkDimension()); rcCmdMgr.addCommand(new CmdTechDrawDimensionRepair()); + rcCmdMgr.addCommand(new CmdTechDrawCompDimensionTools()); } //------------------------------------------------------------------------------ @@ -1352,11 +2271,27 @@ void CreateTechDrawCommandsDims() //Common code to build a dimension feature DrawViewDimension* dimensionMaker(TechDraw::DrawViewPart* dvp, std::string dimType, ReferenceVector references2d, ReferenceVector references3d) +{ + TechDraw::DrawViewDimension* dim = dimMaker(dvp, dimType, references2d, references3d); + + Gui::Command::commitCommand(); + + // Touch the parent feature so the dimension in tree view appears as a child + dvp->touch(true); + + // Select only the newly created dimension + Gui::Selection().clearSelection(); + Gui::Selection().addSelection(dvp->getDocument()->getName(), dim->getNameInDocument()); + + return dim; +} + +DrawViewDimension* dimMaker(TechDraw::DrawViewPart* dvp, std::string dimType, + ReferenceVector references2d, ReferenceVector references3d) { TechDraw::DrawPage* page = dvp->findParentPage(); std::string PageName = page->getNameInDocument(); - TechDraw::DrawViewDimension* dim = nullptr; std::string dimName = dvp->getDocument()->getUniqueObjectName("Dimension"); Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Dimension")); @@ -1374,7 +2309,7 @@ DrawViewDimension* dimensionMaker(TechDraw::DrawViewPart* dvp, std::string dimTy dimName.c_str(), "Projected"); - dim = + auto* dim = dynamic_cast(dvp->getDocument()->getObject(dimName.c_str())); if (!dim) { throw Base::TypeError("CmdTechDrawNewDiameterDimension - dim not found\n"); @@ -1389,16 +2324,9 @@ DrawViewDimension* dimensionMaker(TechDraw::DrawViewPart* dvp, std::string dimTy PageName.c_str(), dimName.c_str()); - Gui::Command::commitCommand(); - - // Touch the parent feature so the dimension in tree view appears as a child - dvp->touch(true); - - // Select only the newly created dimension - Gui::Selection().clearSelection(); - Gui::Selection().addSelection(dvp->getDocument()->getName(), dim->getNameInDocument()); dim->recomputeFeature(); + return dim; } diff --git a/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp b/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp index 60f07baa9c..bf55681a0d 100644 --- a/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp +++ b/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp @@ -52,6 +52,7 @@ #include "DrawGuiUtil.h" #include "TaskCustomizeFormat.h" #include "TaskSelectLineAttributes.h" +#include "CommandExtensionDims.h" using namespace TechDrawGui; @@ -78,7 +79,7 @@ namespace TechDrawGui { std::vector_getDimensions(std::vector selection, std::string needDimType); std::vector _getVertexInfo(TechDraw::DrawViewPart* objFeat, std::vector subNames); - TechDraw::DrawViewDimension* _createLinDimension(Gui::Command* cmd, + TechDraw::DrawViewDimension* _createLinDimension( TechDraw::DrawViewPart* objFeat, std::string startVertex, std::string endVertex, @@ -1280,7 +1281,7 @@ void execCreateHorizChainDimension(Gui::Command* cmd) { float yMaster = 0.0; for (long unsigned int n = 0; n < allVertexes.size() - 1; n++) { TechDraw::DrawViewDimension* dim; - dim = _createLinDimension(cmd, objFeat, allVertexes[n].name, allVertexes[n + 1].name, "DistanceX"); + dim = _createLinDimension(objFeat, allVertexes[n].name, allVertexes[n + 1].name, "DistanceX"); TechDraw::pointPair pp = dim->getLinearPoints(); Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; dim->X.setValue(mid.x); @@ -1347,7 +1348,7 @@ void execCreateVertChainDimension(Gui::Command* cmd) { double fontSize = Preferences::dimFontSizeMM(); for (long unsigned int n = 0; n < allVertexes.size() - 1; n++) { TechDraw::DrawViewDimension* dim; - dim = _createLinDimension(cmd, objFeat, allVertexes[n].name, allVertexes[n + 1].name, "DistanceY"); + dim = _createLinDimension(objFeat, allVertexes[n].name, allVertexes[n + 1].name, "DistanceY"); TechDraw::pointPair pp = dim->getLinearPoints(); Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; if (n == 0) @@ -1443,7 +1444,7 @@ void execCreateObliqueChainDimension(Gui::Command* cmd) { double fontSize = Preferences::dimFontSizeMM(); for (long unsigned int n = 0; n < allVertexes.size() - 1; n++) { TechDraw::DrawViewDimension* dim; - dim = _createLinDimension(cmd, objFeat, carrierVertexes[n].name, carrierVertexes[n + 1].name, "Distance"); + dim = _createLinDimension(objFeat, carrierVertexes[n].name, carrierVertexes[n + 1].name, "Distance"); TechDraw::pointPair pp = dim->getLinearPoints(); Base::Vector3d mid = (pp.first() + pp.second()) / 2.0 + delta; dim->X.setValue(mid.x); @@ -1630,7 +1631,7 @@ void execCreateHorizCoordDimension(Gui::Command* cmd) { dimDistance = -dimDistance; for (long unsigned int n = 0; n < allVertexes.size() - 1; n++) { TechDraw::DrawViewDimension* dim; - dim = _createLinDimension(cmd, objFeat, allVertexes[0].name, allVertexes[n + 1].name, "DistanceX"); + dim = _createLinDimension(objFeat, allVertexes[0].name, allVertexes[n + 1].name, "DistanceX"); TechDraw::pointPair pp = dim->getLinearPoints(); Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; dim->X.setValue(mid.x); @@ -1703,7 +1704,7 @@ void execCreateVertCoordDimension(Gui::Command* cmd) { double fontSize = Preferences::dimFontSizeMM(); for (long unsigned int n = 0; n < allVertexes.size() - 1; n++) { TechDraw::DrawViewDimension* dim; - dim = _createLinDimension(cmd, objFeat, allVertexes[0].name, allVertexes[n + 1].name, "DistanceY"); + dim = _createLinDimension(objFeat, allVertexes[0].name, allVertexes[n + 1].name, "DistanceY"); TechDraw::pointPair pp = dim->getLinearPoints(); Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; dim->X.setValue(xMaster + dimDistance * n); @@ -1804,7 +1805,7 @@ void execCreateObliqueCoordDimension(Gui::Command* cmd) { double fontSize = Preferences::dimFontSizeMM(); for (long unsigned int n = 0; n < allVertexes.size() - 1; n++) { TechDraw::DrawViewDimension* dim; - dim = _createLinDimension(cmd, objFeat, carrierVertexes[0].name, carrierVertexes[n + 1].name, "Distance"); + dim = _createLinDimension(objFeat, carrierVertexes[0].name, carrierVertexes[n + 1].name, "Distance"); TechDraw::pointPair pp = dim->getLinearPoints(); Base::Vector3d mid = (pp.first() + pp.second()) / 2.0 + delta * (n + 1); dim->X.setValue(mid.x); @@ -1991,7 +1992,7 @@ void execCreateHorizChamferDimension(Gui::Command* cmd) { if (!allVertexes.empty() && allVertexes.size() > 1) { const auto Pi180 = 180.0 / M_PI; TechDraw::DrawViewDimension* dim; - dim = _createLinDimension(cmd, objFeat, allVertexes[0].name, allVertexes[1].name, "DistanceX"); + dim = _createLinDimension(objFeat, allVertexes[0].name, allVertexes[1].name, "DistanceX"); float yMax = std::max(abs(allVertexes[0].point.y), abs(allVertexes[1].point.y)) + 7.0; if (std::signbit(allVertexes[0].point.y)) yMax = -yMax; @@ -2060,7 +2061,7 @@ void execCreateVertChamferDimension(Gui::Command* cmd) { if (!allVertexes.empty() && allVertexes.size() > 1) { const auto Pi180 = 180.0 / M_PI; TechDraw::DrawViewDimension* dim; - dim = _createLinDimension(cmd, objFeat, allVertexes[0].name, allVertexes[1].name, "DistanceY"); + dim = _createLinDimension(objFeat, allVertexes[0].name, allVertexes[1].name, "DistanceY"); float xMax = std::max(abs(allVertexes[0].point.x), abs(allVertexes[1].point.x)) + 7.0; if (std::signbit(allVertexes[0].point.x)) xMax = -xMax; @@ -2240,47 +2241,17 @@ void CmdTechDrawExtensionCreateLengthArc::activated(int iMsg) { } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Arc Length Dim")); - const std::vector subNames = selection[0].getSubNames(); - int geoId = TechDraw::DrawUtil::getIndexFromName(subNames[0]); - TechDraw::BaseGeomPtr geom = objFeat->getGeomByIndex(geoId); - std::string geoType = TechDraw::DrawUtil::getGeomTypeFromName(subNames[0]); - if (geoType == "Edge" && geom->getGeomType() == TechDraw::ARCOFCIRCLE) { - TechDraw::AOCPtr arcTag = std::static_pointer_cast(geom); - float radius = arcTag->radius; - Base::Vector3d centerPt = arcTag->center; - centerPt.y = -centerPt.y; - Base::Vector3d startPt = arcTag->startPnt; - startPt.y = -startPt.y; - Base::Vector3d endPt = arcTag->endPnt; - endPt.y = -endPt.y; - std::stringstream startName, endName, formatSpec; - double scale = objFeat->getScale(); - Base::Vector3d cvPoint = CosmeticVertex::makeCanonicalPoint(objFeat, startPt); - std::string startVertTag = objFeat->addCosmeticVertex(cvPoint); - int startVertNumber = objFeat->add1CVToGV(startVertTag); - startName << "Vertex" << startVertNumber; - cvPoint = CosmeticVertex::makeCanonicalPoint(objFeat, endPt); - std::string endVertTag = objFeat->addCosmeticVertex(cvPoint); - int endVertNumber = objFeat->add1CVToGV(endVertTag); - endName << "Vertex" << endVertNumber; - TechDraw::DrawViewDimension* dim; - dim = _createLinDimension(this, objFeat, startName.str(), endName.str(), "Distance"); - TechDraw::pointPair pp = dim->getLinearPoints(); - Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; - dim->X.setValue(mid.x); - dim->Y.setValue(-mid.y); - Base::Vector3d radVec1 = startPt - centerPt; - Base::Vector3d radVec2 = endPt - centerPt; - float alpha = acos((radVec1 * radVec2) / (radVec1.Length() * radVec2.Length())); - float arcLength = alpha * radius / scale; - dim->Arbitrary.setValue(true); - formatSpec << "◠ " << arcLength; - dim->FormatSpec.setValue(formatSpec.str()); - objFeat->refreshCEGeoms(); - objFeat->requestPaint(); - getSelection().clearSelection(); + + ReferenceEntry ref(objFeat, selection[0].getSubNames()[0]); + + TechDraw::DrawViewDimension* dim = makeArcLengthDimension(ref); + + if (dim) { + Gui::Command::commitCommand(); + } + else { + Gui::Command::abortCommand(); } - Gui::Command::commitCommand(); } bool CmdTechDrawExtensionCreateLengthArc::isActive() @@ -2329,6 +2300,54 @@ bool CmdTechDrawExtensionCustomizeFormat::isActive() return (havePage && haveView); } +DrawViewDimension* TechDrawGui::makeArcLengthDimension(const ReferenceEntry& ref) +{ + DrawViewDimension* dim = nullptr; + auto* dvp = static_cast(ref.getObject()); + + int geoId = DrawUtil::getIndexFromName(ref.getSubName()); + BaseGeomPtr geom = dvp->getGeomByIndex(geoId); + + if (ref.geomEdgeType() == TechDraw::ARCOFCIRCLE) { + TechDraw::AOCPtr arcTag = std::static_pointer_cast(geom); + float radius = arcTag->radius; + Base::Vector3d centerPt = arcTag->center; + centerPt.y = -centerPt.y; + Base::Vector3d startPt = arcTag->startPnt; + startPt.y = -startPt.y; + Base::Vector3d endPt = arcTag->endPnt; + endPt.y = -endPt.y; + + std::stringstream startName, endName, formatSpec; + double scale = dvp->getScale(); + Base::Vector3d cvPoint = CosmeticVertex::makeCanonicalPoint(dvp, startPt); + std::string startVertTag = dvp->addCosmeticVertex(cvPoint); + int startVertNumber = dvp->add1CVToGV(startVertTag); + startName << "Vertex" << startVertNumber; + cvPoint = CosmeticVertex::makeCanonicalPoint(dvp, endPt); + std::string endVertTag = dvp->addCosmeticVertex(cvPoint); + int endVertNumber = dvp->add1CVToGV(endVertTag); + endName << "Vertex" << endVertNumber; + + dim = _createLinDimension(dvp, startName.str(), endName.str(), "Distance"); + TechDraw::pointPair pp = dim->getLinearPoints(); + Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; + dim->X.setValue(mid.x); + dim->Y.setValue(-mid.y); + Base::Vector3d radVec1 = startPt - centerPt; + Base::Vector3d radVec2 = endPt - centerPt; + float alpha = acos((radVec1 * radVec2) / (radVec1.Length() * radVec2.Length())); + float arcLength = alpha * radius / scale; + dim->Arbitrary.setValue(true); + formatSpec << "◠ " << arcLength; + dim->FormatSpec.setValue(formatSpec.str()); + dvp->refreshCEGeoms(); + dvp->requestPaint(); + } + + return dim; +} + namespace TechDrawGui { //=========================================================================== // internal helper routines @@ -2387,7 +2406,8 @@ namespace TechDrawGui { } return true; } - TechDraw::DrawViewDimension* _createLinDimension(Gui::Command* cmd, + + TechDraw::DrawViewDimension* _createLinDimension( TechDraw::DrawViewPart* objFeat, std::string startVertex, std::string endVertex, @@ -2396,21 +2416,21 @@ namespace TechDrawGui { { TechDraw::DrawPage* page = objFeat->findParentPage(); std::string PageName = page->getNameInDocument(); - TechDraw::DrawViewDimension* dim = nullptr; - std::string FeatName = cmd->getUniqueObjectName("Dimension"); + std::string FeatName = objFeat->getDocument()->getUniqueObjectName("Dimension"); std::vector objs; std::vector subs; objs.push_back(objFeat); objs.push_back(objFeat); subs.push_back(startVertex); subs.push_back(endVertex); - cmd->doCommand(cmd->Doc, "App.activeDocument().addObject('TechDraw::DrawViewDimension', '%s')", FeatName.c_str()); - cmd->doCommand(cmd->Doc, "App.activeDocument().%s.Type = '%s'", FeatName.c_str(), dimType.c_str()); - dim = dynamic_cast(cmd->getDocument()->getObject(FeatName.c_str())); - if (!dim) + Gui::Command::doCommand(Gui::Command::Doc, "App.activeDocument().addObject('TechDraw::DrawViewDimension', '%s')", FeatName.c_str()); + Gui::Command::doCommand(Gui::Command::Doc, "App.activeDocument().%s.Type = '%s'", FeatName.c_str(), dimType.c_str()); + auto dim = dynamic_cast(objFeat->getDocument()->getObject(FeatName.c_str())); + if (!dim){ throw Base::TypeError("CmdTechDrawExtensionCreateLinDimension - dim not found\n"); + } dim->References2D.setValues(objs, subs); - cmd->doCommand(cmd->Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", PageName.c_str(), FeatName.c_str()); + Gui::Command::doCommand(Gui::Command::Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", PageName.c_str(), FeatName.c_str()); // Touch the parent feature so the dimension in tree view appears as a child objFeat->touch(); diff --git a/src/Mod/TechDraw/Gui/CommandExtensionDims.h b/src/Mod/TechDraw/Gui/CommandExtensionDims.h new file mode 100644 index 0000000000..e94fcbc6ce --- /dev/null +++ b/src/Mod/TechDraw/Gui/CommandExtensionDims.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (c) 2021 edi * + * * + * 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 * + * * + ***************************************************************************/ + +#ifndef TECHDRAWGUI_CommandExtensionDims_H +#define TECHDRAWGUI_CommandExtensionDims_H + +namespace TechDraw +{ + class ReferenceEntry; +} + +namespace TechDrawGui { + TechDraw::DrawViewDimension* makeArcLengthDimension(const TechDraw::ReferenceEntry& ref); +} + +#endif // TECHDRAWGUI_CommandExtensionDims_H diff --git a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensions.ui b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensions.ui index eb4870f3f4..a1819701b6 100644 --- a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensions.ui +++ b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensions.ui @@ -524,9 +524,9 @@ Multiplier of 'Font Size' - Controls the size of gap between dimension point and start of extension line for ISO dimensions. -Value * linewidth is the gap. -Normally, no gap is used. If using a gap, the recommended value 8. + Controls the size of gap between dimension point and start of extension line for ISO dimensions. + Value * linewidth is the gap. + Normally, no gap is used. If using a gap, the recommended value 8. Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -545,8 +545,8 @@ Normally, no gap is used. If using a gap, the recommended value 8. - Controls the size of gap between dimension point and start of extension line for ASME dimensions. Value * linewidth is the gap. -Normally, no gap is used. If a gap is used, the recommended value is 6. + Controls the size of gap between dimension point and start of extension line for ASME dimensions. Value * linewidth is the gap. + Normally, no gap is used. If a gap is used, the recommended value is 6. Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -618,6 +618,56 @@ Normally, no gap is used. If a gap is used, the recommended value is 6. + + + + + 0 + 0 + + + + Tools + + + + + + Dimensioning tools: + + + + + + + Select the type of dimensioning tools for your toolbar: +'Single tool': A single tool for all dimensioning in the toolbar: Distance, Distance X / Y, Angle, Radius. (Others in dropdown) +'Separated tools': Individual tools for each dimensioning tool. +'Both': You will have both the 'Dimension' tool and the separated tools. +This setting is only for the toolbar. Whichever you choose, all tools are always available in the menu and through shortcuts. + + + + + + + Dimension tool diameter/radius mode: + + + + + + + While using the Dimension tool you may choose how to handle circles and arcs: +'Auto': The tool will apply radius to arcs and diameter to circles. +'Diameter': The tool will apply diameter to all. +'Radius': The tool will apply radius to all. + + + + + + diff --git a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensionsImp.cpp b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensionsImp.cpp index 0b4b276e3e..c7ef7746ce 100644 --- a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensionsImp.cpp +++ b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensionsImp.cpp @@ -25,6 +25,7 @@ #include "PreCompiled.h" #include +#include #include "DlgPrefsTechDrawDimensionsImp.h" #include "ui_DlgPrefsTechDrawDimensions.h" @@ -67,6 +68,61 @@ void DlgPrefsTechDrawDimensionsImp::saveSettings() ui->pdsbGapISO->onSave(); ui->pdsbGapASME->onSave(); ui->pdsbLineSpacingFactorISO->onSave(); + + enum + { + DimensionSingleTool, + DimensionSeparateTools, + DimensionBoth + }; + + // Dimensioning constraints mode + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/TechDraw/dimensioning"); + bool singleTool = true; + bool SeparatedTools = false; + int index = ui->dimensioningMode->currentIndex(); + switch (index) { + case DimensionSeparateTools: + singleTool = false; + SeparatedTools = true; + break; + case DimensionBoth: + singleTool = true; + SeparatedTools = true; + break; + } + hGrp->SetBool("SingleDimensioningTool", singleTool); + hGrp->SetBool("SeparatedDimensioningTools", SeparatedTools); + + ui->radiusDiameterMode->setEnabled(index != 1); + + enum + { + DimensionAutoRadiusDiam, + DimensionDiameter, + DimensionRadius + }; + + bool Diameter = true; + bool Radius = true; + index = ui->radiusDiameterMode->currentIndex(); + switch (index) { + case DimensionDiameter: + Diameter = true; + Radius = false; + break; + case DimensionRadius: + Diameter = false; + Radius = true; + break; + } + hGrp->SetBool("DimensioningDiameter", Diameter); + hGrp->SetBool("DimensioningRadius", Radius); + + if (property("dimensioningMode").toInt() != ui->dimensioningMode->currentIndex()) { + requireRestart(); + } } void DlgPrefsTechDrawDimensionsImp::loadSettings() @@ -101,6 +157,42 @@ void DlgPrefsTechDrawDimensionsImp::loadSettings() ui->pdsbGapASME->onRestore(); ui->pdsbLineSpacingFactorISO->onRestore(); + + // Dimensioning constraints mode + ui->dimensioningMode->clear(); + ui->dimensioningMode->addItem(tr("Single tool")); + ui->dimensioningMode->addItem(tr("Separated tools")); + ui->dimensioningMode->addItem(tr("Both")); + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/TechDraw/dimensioning"); + bool singleTool = hGrp->GetBool("SingleDimensioningTool", true); + bool SeparatedTools = hGrp->GetBool("SeparatedDimensioningTools", true); + int index = SeparatedTools ? (singleTool ? 2 : 1) : 0; + ui->dimensioningMode->setCurrentIndex(index); + setProperty("dimensioningMode", index); + connect(ui->dimensioningMode, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &DlgPrefsTechDrawDimensionsImp::dimensioningModeChanged); + + ui->radiusDiameterMode->setEnabled(index != 1); + + // Dimensioning constraints mode + ui->radiusDiameterMode->clear(); + ui->radiusDiameterMode->addItem(tr("Auto")); + ui->radiusDiameterMode->addItem(tr("Diameter")); + ui->radiusDiameterMode->addItem(tr("Radius")); + + bool Diameter = hGrp->GetBool("DimensioningDiameter", true); + bool Radius = hGrp->GetBool("DimensioningRadius", true); + index = Diameter ? (Radius ? 0 : 1) : 2; + ui->radiusDiameterMode->setCurrentIndex(index); +} + +void DlgPrefsTechDrawDimensionsImp::dimensioningModeChanged(int index) +{ + ui->radiusDiameterMode->setEnabled(index != 1); } /** diff --git a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensionsImp.h b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensionsImp.h index 44b66bf16e..c06dcb0d6c 100644 --- a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensionsImp.h +++ b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensionsImp.h @@ -45,6 +45,7 @@ protected: void saveSettings() override; void loadSettings() override; void changeEvent(QEvent *e) override; + void dimensioningModeChanged(int index); int prefArrowStyle() const; diff --git a/src/Mod/TechDraw/Gui/MDIViewPage.cpp b/src/Mod/TechDraw/Gui/MDIViewPage.cpp index 3af07c9e06..edbc80b1d5 100644 --- a/src/Mod/TechDraw/Gui/MDIViewPage.cpp +++ b/src/Mod/TechDraw/Gui/MDIViewPage.cpp @@ -755,13 +755,27 @@ void MDIViewPage::sceneSelectionChanged() std::vector treeSel = Gui::Selection().getSelectionEx(); QList sceneSel = m_qgSceneSelected; - //check if really need to change selection - bool sameSel = compareSelections(treeSel, sceneSel); - if (sameSel) { - return; + + bool saveBlock = blockSelection(true);// block selectionChanged signal from Tree/Observer + blockSceneSelection(true); + + if (sceneSel.empty()) { + if (!treeSel.empty()) { + Gui::Selection().clearSelection(); + } + } + else { + for (auto& sel : treeSel) { + removeSelFromTreeSel(sceneSel, sel); + } + + for (auto* scene : sceneSel) { + addSceneToTreeSel(scene, treeSel); + } } - setTreeToSceneSelect(); + blockSceneSelection(false); + blockSelection(saveBlock); } //Note: Qt says: "no guarantee of selection order"!!! @@ -771,149 +785,48 @@ void MDIViewPage::setTreeToSceneSelect() bool saveBlock = blockSelection(true);// block selectionChanged signal from Tree/Observer blockSceneSelection(true); Gui::Selection().clearSelection(); - QList sceneSel = m_qgSceneSelected; - for (QList::iterator it = sceneSel.begin(); it != sceneSel.end(); ++it) { - QGIView* itemView = dynamic_cast(*it); + + for (auto* scene : m_qgSceneSelected) { + auto* itemView = dynamic_cast(scene); if (!itemView) { - QGIEdge* edge = dynamic_cast(*it); - if (edge) { - QGraphicsItem* parent = edge->parentItem(); - if (!parent) { - continue; - } - - QGIView* viewItem = dynamic_cast(parent); - if (!viewItem) { - continue; - } - - TechDraw::DrawView* viewObj = viewItem->getViewObject(); - - std::stringstream ss; - ss << "Edge" << edge->getProjIndex(); - //bool accepted = - static_cast(Gui::Selection().addSelection(viewObj->getDocument()->getName(), - viewObj->getNameInDocument(), - ss.str().c_str())); - showStatusMsg(viewObj->getDocument()->getName(), viewObj->getNameInDocument(), - ss.str().c_str()); + auto* parent = dynamic_cast(scene->parentItem()); + if (!parent) { + return; + } + TechDraw::DrawView* viewObj = parent->getViewObject(); + if (!viewObj) { continue; } + const char* doc_name = viewObj->getDocument()->getName(); + const char* obj_name = viewObj->getNameInDocument(); - QGIVertex* vert = dynamic_cast(*it); - if (vert) { - QGraphicsItem* parent = vert->parentItem(); - if (!parent) { - continue; - } + auto* edge = dynamic_cast(scene); + auto* vert = dynamic_cast(scene); + auto* face = dynamic_cast(scene); + if (edge || vert || face) { + const char* ssn = getSceneSubName(scene).c_str(); - QGIView* viewItem = dynamic_cast(parent); - if (!viewItem) { - continue; - } - - TechDraw::DrawView* viewObj = viewItem->getViewObject(); - - std::stringstream ss; - ss << "Vertex" << vert->getProjIndex(); - //bool accepted = - static_cast(Gui::Selection().addSelection(viewObj->getDocument()->getName(), - viewObj->getNameInDocument(), - ss.str().c_str())); - showStatusMsg(viewObj->getDocument()->getName(), viewObj->getNameInDocument(), - ss.str().c_str()); - continue; + Gui::Selection().addSelection(doc_name, obj_name, ssn); + showStatusMsg(doc_name, obj_name, ssn); + return; } - - QGIFace* face = dynamic_cast(*it); - if (face) { - QGraphicsItem* parent = face->parentItem(); - if (!parent) { - continue; - } - - QGIView* viewItem = dynamic_cast(parent); - if (!viewItem) { - continue; - } - - TechDraw::DrawView* viewObj = viewItem->getViewObject(); - - std::stringstream ss; - ss << "Face" << face->getProjIndex(); - //bool accepted = - static_cast(Gui::Selection().addSelection(viewObj->getDocument()->getName(), - viewObj->getNameInDocument(), - ss.str().c_str())); - showStatusMsg(viewObj->getDocument()->getName(), viewObj->getNameInDocument(), - ss.str().c_str()); - continue; - } - - QGIDatumLabel* dimLabel = dynamic_cast(*it); - if (dimLabel) { - QGraphicsItem* dimParent = dimLabel->QGraphicsItem::parentItem(); - if (!dimParent) { - continue; - } - - QGIView* dimItem = dynamic_cast(dimParent); - - if (!dimItem) { - continue; - } - - TechDraw::DrawView* dimObj = dimItem->getViewObject(); - if (!dimObj) { - continue; - } - const char* name = dimObj->getNameInDocument(); - if (!name) {//can happen during undo/redo if Dim is selected??? + else if (dynamic_cast(scene) || dynamic_cast(scene)) { + if (!obj_name) {//can happen during undo/redo if Dim is selected??? //Base::Console().Log("INFO - MDIVP::sceneSelectionChanged - dimObj name is null!\n"); continue; } - //bool accepted = - static_cast(Gui::Selection().addSelection(dimObj->getDocument()->getName(), - dimObj->getNameInDocument())); - } - - QGMText* mText = dynamic_cast(*it); - if (mText) { - QGraphicsItem* textParent = mText->QGraphicsItem::parentItem(); - if (!textParent) { - continue; - } - - QGIView* parent = dynamic_cast(textParent); - - if (!parent) { - continue; - } - - TechDraw::DrawView* parentFeat = parent->getViewObject(); - if (!parentFeat) { - continue; - } - const char* name = parentFeat->getNameInDocument(); - if (!name) {//can happen during undo/redo if Dim is selected??? - continue; - } - - //bool accepted = - static_cast(Gui::Selection().addSelection( - parentFeat->getDocument()->getName(), parentFeat->getNameInDocument())); + Gui::Selection().addSelection(doc_name, obj_name); } } else { - TechDraw::DrawView* viewObj = itemView->getViewObject(); if (viewObj && !viewObj->isRemoving()) { - std::string doc_name = viewObj->getDocument()->getName(); - std::string obj_name = viewObj->getNameInDocument(); + const char* doc_name = viewObj->getDocument()->getName(); + const char* obj_name = viewObj->getNameInDocument(); - Gui::Selection().addSelection(doc_name.c_str(), obj_name.c_str()); - showStatusMsg(doc_name.c_str(), obj_name.c_str(), ""); + Gui::Selection().addSelection(doc_name, obj_name); + showStatusMsg(doc_name, obj_name, ""); } } } @@ -922,6 +835,138 @@ void MDIViewPage::setTreeToSceneSelect() blockSelection(saveBlock); } +std::string MDIViewPage::getSceneSubName(QGraphicsItem* scene) +{ + auto* edge = dynamic_cast(scene); + auto* vert = dynamic_cast(scene); + auto* face = dynamic_cast(scene); + if (edge || vert || face) { + auto* viewItem = dynamic_cast(scene->parentItem()); + if (viewItem) { + TechDraw::DrawView* viewObj = viewItem->getViewObject(); + + std::stringstream ss; + if (edge) { ss << "Edge" << edge->getProjIndex(); } + else if (vert) { ss << "Vertex" << vert->getProjIndex(); } + else { ss << "Face" << face->getProjIndex(); } + + return ss.str(); + } + } + return ""; +} + +// adds scene to core selection if it's not in already. +void MDIViewPage::addSceneToTreeSel(QGraphicsItem* sn, std::vector treeSel) +{ + auto* itemView = dynamic_cast(sn); + if (!itemView) { + auto* parent = dynamic_cast(sn->parentItem()); + if (!parent) { + return; + } + TechDraw::DrawView* viewObj = parent->getViewObject(); + if (!viewObj) { + return; + } + + const char* doc_name = viewObj->getDocument()->getName(); + const char* obj_name = viewObj->getNameInDocument(); + std::string sub_name; + + if (dynamic_cast(sn) || dynamic_cast(sn) || dynamic_cast(sn)) { + sub_name = getSceneSubName(sn); + } + + else if (dynamic_cast(sn) || dynamic_cast(sn)) { + if (!obj_name) {//can happen during undo/redo if Dim is selected??? + return; + } + } + else { // are there other cases? + return; + } + + if (!Gui::Selection().isSelected(viewObj, sub_name.c_str())) { + Gui::Selection().addSelection(doc_name, obj_name, sub_name.c_str()); + showStatusMsg(doc_name, obj_name, sub_name.c_str()); + } + } + else { + TechDraw::DrawView* viewObj = itemView->getViewObject(); + if (viewObj && !viewObj->isRemoving()) { + const char* doc_name = viewObj->getDocument()->getName(); + const char* obj_name = viewObj->getNameInDocument(); + + if (!Gui::Selection().isSelected(viewObj)) { + Gui::Selection().addSelection(doc_name, obj_name); + showStatusMsg(doc_name, obj_name, ""); + } + } + } +} + +// remove core selection if scene is not selected anymore +void MDIViewPage::removeSelFromTreeSel(QList sceneSel, Gui::SelectionObject& sel) +{ + std::string selDocName = sel.getDocName(); + App::DocumentObject* selObj = sel.getObject(); + + for (auto& sub : sel.getSubNames()) { + bool found = false; + for (auto& sn : sceneSel) { + auto* itemView = dynamic_cast(sn); + if (!itemView) { + auto* parent = dynamic_cast(sn->parentItem()); + if (!parent) { + continue; + } + TechDraw::DrawView* viewObj = parent->getViewObject(); + if (!viewObj) { + continue; + } + + const char* doc_name = viewObj->getDocument()->getName(); + const char* obj_name = viewObj->getNameInDocument(); + std::string sub_name; + + if (dynamic_cast(sn) || dynamic_cast(sn) || dynamic_cast(sn)) { + sub_name = getSceneSubName(sn); + } + + else if (dynamic_cast(sn) || dynamic_cast(sn)) { + if (!obj_name) {//can happen during undo/redo if Dim is selected??? + continue; + } + } + else { // are there other cases? + continue; + } + + if (selDocName == doc_name && selObj == viewObj && sub == sub_name) { + found = true; + break; + } + } + else { + TechDraw::DrawView* viewObj = itemView->getViewObject(); + if (viewObj && !viewObj->isRemoving()) { + const char* doc_name = viewObj->getDocument()->getName(); + const char* obj_name = viewObj->getNameInDocument(); + + if (selDocName == doc_name && selObj == viewObj) { + found = true; + break; + } + } + } + } + if (!found) { + Gui::Selection().rmvSelection(sel.getDocName(), sel.getObject()->getNameInDocument(), sub.c_str()); + } + } +} + bool MDIViewPage::compareSelections(std::vector treeSel, QList sceneSel) { diff --git a/src/Mod/TechDraw/Gui/MDIViewPage.h b/src/Mod/TechDraw/Gui/MDIViewPage.h index 474247305f..6f58a19eba 100644 --- a/src/Mod/TechDraw/Gui/MDIViewPage.h +++ b/src/Mod/TechDraw/Gui/MDIViewPage.h @@ -127,6 +127,9 @@ protected: void onDeleteObject(const App::DocumentObject& obj); bool compareSelections(std::vector treeSel, QList sceneSel); + void addSceneToTreeSel(QGraphicsItem* scene, std::vector treeSel); + void removeSelFromTreeSel(QList sceneSel, Gui::SelectionObject& sel); + std::string getSceneSubName(QGraphicsItem* scene); void setTreeToSceneSelect(); void sceneSelectionManager(); diff --git a/src/Mod/TechDraw/Gui/PreferencesGui.cpp b/src/Mod/TechDraw/Gui/PreferencesGui.cpp index c4cfbcd26a..24059b51d0 100644 --- a/src/Mod/TechDraw/Gui/PreferencesGui.cpp +++ b/src/Mod/TechDraw/Gui/PreferencesGui.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -214,7 +215,8 @@ bool PreferencesGui::showGrid() bool PreferencesGui::multiSelection() { - return Preferences::getPreferenceGroup("General")->GetBool("multiSelection", false); + bool greedy = Gui::Selection().getSelectionStyle() == Gui::SelectionSingleton::SelectionStyle::GreedySelection; + return greedy || Preferences::getPreferenceGroup("General")->GetBool("multiSelection", false); } App::Color PreferencesGui::pageColor() diff --git a/src/Mod/TechDraw/Gui/QGVPage.cpp b/src/Mod/TechDraw/Gui/QGVPage.cpp index 3869bea238..de73224363 100644 --- a/src/Mod/TechDraw/Gui/QGVPage.cpp +++ b/src/Mod/TechDraw/Gui/QGVPage.cpp @@ -63,6 +63,7 @@ #include "QGVNavStyleTouchpad.h" #include "QGVPage.h" #include "Rez.h" +#include "TechDrawHandler.h" #include "ViewProviderPage.h" @@ -167,7 +168,7 @@ public: QGVPage::QGVPage(ViewProviderPage* vpPage, QGSPage* scenePage, QWidget* parent) : QGraphicsView(parent), m_renderer(Native), drawBkg(true), m_vpPage(nullptr), m_scene(scenePage), balloonPlacing(false), m_showGrid(false), - m_navStyle(nullptr), d(new Private(this)) + m_navStyle(nullptr), d(new Private(this)), toolHandler(nullptr) { assert(vpPage); m_vpPage = vpPage; @@ -281,6 +282,31 @@ void QGVPage::setNavigationStyle(std::string navParm) } } + +void QGVPage::activateHandler(TechDrawHandler* newHandler) +{ + if (toolHandler) { + toolHandler->deactivate(); + } + + toolHandler = std::unique_ptr(newHandler); + toolHandler->activate(this); + + // make sure receiver has focus so immediately pressing Escape will be handled by + // ViewProviderSketch::keyPressed() and dismiss the active handler, and not the entire + // sketcher editor + //Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); + //mdi->setFocus(); +} + +void QGVPage::deactivateHandler() +{ + if (toolHandler) { + toolHandler->deactivate(); + toolHandler = nullptr; + } +} + void QGVPage::startBalloonPlacing(DrawView* parent) { // Base::Console().Message("QGVP::startBalloonPlacing(%s)\n", parent->getNameInDocument()); @@ -421,7 +447,12 @@ void QGVPage::wheelEvent(QWheelEvent* event) void QGVPage::keyPressEvent(QKeyEvent* event) { - m_navStyle->handleKeyPressEvent(event); + if (toolHandler) { + toolHandler->keyPressEvent(event); + } + else { + m_navStyle->handleKeyPressEvent(event); + } if (!event->isAccepted()) { QGraphicsView::keyPressEvent(event); } @@ -429,7 +460,12 @@ void QGVPage::keyPressEvent(QKeyEvent* event) void QGVPage::keyReleaseEvent(QKeyEvent* event) { - m_navStyle->handleKeyReleaseEvent(event); + if (toolHandler) { + toolHandler->keyReleaseEvent(event); + } + else { + m_navStyle->handleKeyReleaseEvent(event); + } if (!event->isAccepted()) { QGraphicsView::keyReleaseEvent(event); } @@ -465,6 +501,11 @@ void QGVPage::enterEvent(QEvent* event) void QGVPage::enterEvent(QEnterEvent* event) #endif { + if (toolHandler) { + // if the user interacted with another widget than the mdi, the cursor got unset. + // So we reapply it. + toolHandler->updateCursor(); + } QGraphicsView::enterEvent(event); m_navStyle->handleEnterEvent(event); QGraphicsView::enterEvent(event); @@ -478,21 +519,37 @@ void QGVPage::leaveEvent(QEvent* event) void QGVPage::mousePressEvent(QMouseEvent* event) { - m_navStyle->handleMousePressEvent(event); + if (toolHandler && (event->button() != Qt::MiddleButton)) { + toolHandler->mousePressEvent(event); + } + else { + m_navStyle->handleMousePressEvent(event); + } QGraphicsView::mousePressEvent(event); } void QGVPage::mouseMoveEvent(QMouseEvent* event) { - m_navStyle->handleMouseMoveEvent(event); + if (toolHandler) { + toolHandler->mouseMoveEvent(event); + } + else { + m_navStyle->handleMouseMoveEvent(event); + } QGraphicsView::mouseMoveEvent(event); } void QGVPage::mouseReleaseEvent(QMouseEvent* event) { - m_navStyle->handleMouseReleaseEvent(event); - QGraphicsView::mouseReleaseEvent(event); - resetCursor(); + if (toolHandler && (event->button() != Qt::MiddleButton)) { + QGraphicsView::mouseReleaseEvent(event); + toolHandler->mouseReleaseEvent(event); + } + else { + m_navStyle->handleMouseReleaseEvent(event); + QGraphicsView::mouseReleaseEvent(event); + resetCursor(); + } } TechDraw::DrawPage* QGVPage::getDrawPage() { return m_vpPage->getDrawPage(); } @@ -553,8 +610,7 @@ void QGVPage::activateCursor(QCursor cursor) void QGVPage::resetCursor() { - this->setCursor(Qt::ArrowCursor); - viewport()->setCursor(Qt::ArrowCursor); + activateCursor(Qt::ArrowCursor); } void QGVPage::setPanCursor() { activateCursor(panCursor); } diff --git a/src/Mod/TechDraw/Gui/QGVPage.h b/src/Mod/TechDraw/Gui/QGVPage.h index 3b40888650..f123a9d8f1 100644 --- a/src/Mod/TechDraw/Gui/QGVPage.h +++ b/src/Mod/TechDraw/Gui/QGVPage.h @@ -71,6 +71,7 @@ class QGILeaderLine; class QGIRichAnno; class QGITile; class QGVNavStyle; +class TechDrawHandler; class TechDrawGuiExport QGVPage: public QGraphicsView { @@ -101,6 +102,9 @@ public: void showGrid(bool state) { m_showGrid = state; } void updateViewport() { viewport()->repaint(); } + void activateHandler(TechDrawHandler* newHandler); + void deactivateHandler(); + bool isBalloonPlacing() const { return balloonPlacing; } void setBalloonPlacing(bool isPlacing) { balloonPlacing = isPlacing; } @@ -196,6 +200,8 @@ private: MDIViewPage* m_parentMDI; QContextMenuEvent* m_saveContextEvent; + + std::unique_ptr toolHandler; }; }// namespace TechDrawGui diff --git a/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc b/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc index 82812f4187..19cbeef03b 100644 --- a/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc +++ b/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc @@ -90,6 +90,7 @@ icons/TechDraw_CameraOrientation.svg icons/TechDraw_DiameterDimension.svg icons/TechDraw_Dimension.svg + icons/TechDraw_Dimension_Pointer.svg icons/TechDraw_DimensionRepair.svg icons/TechDraw_ExtensionAreaAnnotation.svg icons/TechDraw_ExtensionArcLengthAnnotation.svg diff --git a/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Dimension.svg b/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Dimension.svg index c3b098638a..4a59128659 100644 --- a/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Dimension.svg +++ b/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Dimension.svg @@ -1,26 +1,24 @@ - - + inkscape:export-ydpi="45" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> - - - - - - - - @@ -131,26 +105,6 @@ offset="1" id="stop3897" /> - - + inkscape:collect="always" + xlink:href="#linearGradient3026" + id="linearGradient927" + x1="16.969255" + y1="39.85923" + x2="58.005249" + y2="39.85923" + gradientUnits="userSpaceOnUse" /> + inkscape:snap-nodes="false" + objecttolerance="10.0" + gridtolerance="10.0" + guidetolerance="10.0" + inkscape:pagecheckerboard="0"> image/svg+xml - [WandererFan] @@ -266,70 +222,81 @@ id="layer1" inkscape:label="Layer 1" inkscape:groupmode="layer"> - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Dimension_Pointer.svg b/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Dimension_Pointer.svg new file mode 100644 index 0000000000..a489693e73 --- /dev/null +++ b/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Dimension_Pointer.svg @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/TechDraw/Gui/TechDrawHandler.cpp b/src/Mod/TechDraw/Gui/TechDrawHandler.cpp new file mode 100644 index 0000000000..cec5457435 --- /dev/null +++ b/src/Mod/TechDraw/Gui/TechDrawHandler.cpp @@ -0,0 +1,107 @@ +/*************************************************************************** + * Copyright (c) 2024 Pierre-Louis Boyer * + * * + * 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 +#endif // #ifndef _PreComp_ + +#include +#include +#include +#include +#include +#include +#include + +#include "MDIViewPage.h" +#include "QGVPage.h" +#include "TechDrawHandler.h" + + +using namespace TechDrawGui; + +/**************************** TechDrawHandler *******************************************/ + +TechDrawHandler::TechDrawHandler() : Gui::ToolHandler(), viewPage(nullptr) +{} + +TechDrawHandler::~TechDrawHandler() +{} + +void TechDrawHandler::activate(QGVPage* vp) +{ + viewPage = vp; + + if (!Gui::ToolHandler::activate()) { + viewPage->deactivateHandler(); + } +} + +void TechDrawHandler::keyReleaseEvent(QKeyEvent* event) +{ + // the default behaviour is to quit - specific handler categories may + // override this behaviour, for example to implement a continuous mode + if (event->key() == Qt::Key_Escape) { + quit(); + event->accept(); + } +} + +bool TechDrawHandler::mousePressEvent(QMouseEvent* event) +{ + // the default behaviour is to quit - specific handler categories may + // override this behaviour, for example to implement a continuous mode + if (event->button() == Qt::RightButton) { + quit(); + event->accept(); + } + return true; +} + +void TechDrawHandler::quit() +{ + viewPage->deactivateHandler(); +} + +QWidget* TechDrawHandler::getCursorWidget() +{ + return viewPage; +} + +void TechDrawHandler::setWidgetCursor(QCursor cursor) +{ + if (viewPage) { + viewPage->setCursor(cursor); + viewPage->viewport()->setCursor(cursor); + } +} + +TechDraw::DrawPage* TechDrawHandler::getPage() +{ + return viewPage->getDrawPage(); +} \ No newline at end of file diff --git a/src/Mod/TechDraw/Gui/TechDrawHandler.h b/src/Mod/TechDraw/Gui/TechDrawHandler.h new file mode 100644 index 0000000000..d1c911541b --- /dev/null +++ b/src/Mod/TechDraw/Gui/TechDrawHandler.h @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (c) 2024 Pierre-Louis Boyer * + * * + * 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 * + * * + ***************************************************************************/ + +#ifndef TechDrawGUI_TechDrawHandler_H +#define TechDrawGUI_TechDrawHandler_H + +#include + +#include + +namespace TechDrawGui +{ +class QGVPage; + +class TechDrawGuiExport TechDrawHandler : public Gui::ToolHandler +{ +public: + + TechDrawHandler(); + virtual ~TechDrawHandler(); + + void activate(QGVPage* vPage); + + void quit() override; + + virtual void mouseMoveEvent(QMouseEvent* event) = 0; + virtual bool mousePressEvent(QMouseEvent* event); + virtual bool mouseReleaseEvent(QMouseEvent* event) = 0; + + virtual void keyPressEvent(QKeyEvent* event) = 0; + virtual void keyReleaseEvent(QKeyEvent* event); + + TechDraw::DrawPage* getPage(); + + +protected: + QWidget* getCursorWidget() override; + void setWidgetCursor(QCursor cursor) override; + + QGVPage* viewPage; +}; + + +} // namespace TechDrawGui + + +#endif // TechDrawGUI_TechDrawHandler_H diff --git a/src/Mod/TechDraw/Gui/Workbench.cpp b/src/Mod/TechDraw/Gui/Workbench.cpp index b0651ffaba..aad8d1ae3e 100644 --- a/src/Mod/TechDraw/Gui/Workbench.cpp +++ b/src/Mod/TechDraw/Gui/Workbench.cpp @@ -28,6 +28,7 @@ #endif #include "Workbench.h" +#include #include #include @@ -82,6 +83,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const // dimensions Gui::MenuItem* dimensions = new Gui::MenuItem; dimensions->setCommand("Dimensions"); + *dimensions << "TechDraw_Dimension"; *dimensions << "TechDraw_LengthDimension"; *dimensions << "TechDraw_HorizontalDimension"; *dimensions << "TechDraw_VerticalDimension"; @@ -305,13 +307,28 @@ Gui::ToolBarItem* Workbench::setupToolBars() const Gui::ToolBarItem* dims = new Gui::ToolBarItem(root); dims->setCommand("TechDraw Dimensions"); - *dims << "TechDraw_LengthDimension"; - *dims << "TechDraw_HorizontalDimension"; - *dims << "TechDraw_VerticalDimension"; - *dims << "TechDraw_RadiusDimension"; - *dims << "TechDraw_DiameterDimension"; - *dims << "TechDraw_AngleDimension"; - *dims << "TechDraw_3PtAngleDimension"; + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/TechDraw/dimensioning"); + bool separatedTools = hGrp->GetBool("SeparatedDimensioningTools", true); + if (hGrp->GetBool("SingleDimensioningTool", true)) { + if (separatedTools) { + *dims << "TechDraw_Dimension"; + } + else { + *dims << "TechDraw_CompDimensionTools"; + } + } + if (separatedTools) { + *dims << "TechDraw_LengthDimension"; + *dims << "TechDraw_HorizontalDimension"; + *dims << "TechDraw_VerticalDimension"; + *dims << "TechDraw_RadiusDimension"; + *dims << "TechDraw_DiameterDimension"; + *dims << "TechDraw_AngleDimension"; + *dims << "TechDraw_3PtAngleDimension"; + } + *dims << "TechDraw_ExtentGroup"; // TechDraw_LinkDimension is DEPRECATED. Use TechDraw_DimensionRepair instead. // *dims << "TechDraw_LinkDimension"; @@ -347,7 +364,9 @@ Gui::ToolBarItem* Workbench::setupToolBars() const *extdimensions << "TechDraw_ExtensionCreateChainDimensionGroup"; *extdimensions << "TechDraw_ExtensionCreateCoordDimensionGroup"; *extdimensions << "TechDraw_ExtensionChamferDimensionGroup"; - *extdimensions << "TechDraw_ExtensionCreateLengthArc"; + if (separatedTools) { + *extdimensions << "TechDraw_ExtensionCreateLengthArc"; + } *extdimensions << "TechDraw_ExtensionInsertPrefixGroup"; *extdimensions << "TechDraw_ExtensionIncreaseDecreaseGroup"; @@ -406,6 +425,7 @@ Gui::ToolBarItem* Workbench::setupCommandBars() const Gui::ToolBarItem* dims = new Gui::ToolBarItem(root); dims->setCommand("TechDraw Dimensions"); + *dims << "TechDraw_Dimension"; *dims << "TechDraw_LengthDimension"; *dims << "TechDraw_HorizontalDimension"; *dims << "TechDraw_VerticalDimension";