/*************************************************************************** * Copyright (c) 2012-2013 Luke Parry * * Copyright (c) 2024 Benjamin Bræstrup Sayoc * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ # include # include # include # include # include # include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "QGIView.h" #include "MDIViewPage.h" #include "PreferencesGui.h" #include "QGCustomBorder.h" #include "QGCustomClip.h" #include "QGCustomImage.h" #include "QGCustomLabel.h" #include "QGICaption.h" #include "QGIEdge.h" #include "QGIVertex.h" #include "QGIViewClip.h" #include "QGIUserTypes.h" #include "QGSPage.h" #include "QGVPage.h" #include "Rez.h" #include "ViewProviderDrawingView.h" #include "ViewProviderPage.h" #include "ZVALUE.h" #include "DrawGuiUtil.h" using namespace TechDrawGui; using namespace TechDraw; using DU = DrawUtil; const float labelCaptionFudge = 0.2f; // temp fiddle for devel QGIView::QGIView() :QGraphicsItemGroup(), m_isHovered(false), viewObj(nullptr), m_innerView(false), m_multiselectActivated(false), snapping(false), m_label(new QGCustomLabel()), m_border(new QGCustomBorder()), m_caption(new QGICaption()), m_lock(new QGCustomImage()) { setCacheMode(QGraphicsItem::NoCache); setHandlesChildEvents(false); setAcceptHoverEvents(true); setFlag(QGraphicsItem::ItemIsSelectable, true); setFlag(QGraphicsItem::ItemIsMovable, true); setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true); setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); m_colNormal = prefNormalColor(); m_colCurrent = m_colNormal; m_pen.setColor(m_colCurrent); m_decorPen.setStyle(Qt::DashLine); m_decorPen.setWidth(0); // 0 => 1px "cosmetic pen" addToGroup(m_label); addToGroup(m_border); addToGroup(m_caption); m_lock->setParentItem(m_border); m_lock->load(QStringLiteral(":/icons/TechDraw_Lock.svg")); QSize sizeLock = m_lock->imageSize(); m_lockWidth = (double) sizeLock.width(); m_lockHeight = (double) sizeLock.height(); m_lock->hide(); m_border->hide(); m_label->hide(); } void QGIView::isVisible(bool state) { auto feat = getViewObject(); if (!feat) return; auto vp = QGIView::getViewProvider(feat); if (!vp) return; Gui::ViewProviderDocumentObject* vpdo = dynamic_cast(vp); if (!vpdo) return; vpdo->Visibility.setValue(state); } bool QGIView::isVisible() { auto feat = getViewObject(); if (!feat) return false; auto vp = QGIView::getViewProvider(feat); if (!vp) return false; Gui::ViewProviderDocumentObject* vpdo = dynamic_cast(vp); if (!vpdo) return false; return vpdo->Visibility.getValue(); } //Gets selection state for this view and/or eventually its children bool QGIView::getGroupSelection() { return isSelected(); } //Set selection state for this and its children //required for items like dimensions & balloons void QGIView::setGroupSelection(bool isSelected) { setSelected(isSelected); } // Set selection state of the feature (empty subName) or its sub items void QGIView::setGroupSelection(bool isSelected, const std::vector &subNames) { Q_UNUSED(subNames); setGroupSelection(isSelected); } void QGIView::alignTo(QGraphicsItem*item, const QString &alignment) { alignHash.clear(); alignHash.insert(alignment, item); } QVariant QGIView::itemChange(GraphicsItemChange change, const QVariant &value) { // Base::Console().message("QGIV::itemChange(%d)\n", change); if(change == ItemPositionChange && scene()) { QPointF newPos = value.toPointF(); //position within parent! TechDraw::DrawView* viewObj = getViewObject(); auto* dpgi = dynamic_cast(viewObj); if (dpgi && dpgi->getPGroup()) { // restrict movements of secondary views. if(alignHash.size() == 1) { //if aligned. QGraphicsItem* item = alignHash.begin().value(); QString alignMode = alignHash.begin().key(); if(alignMode == QStringLiteral("Vertical")) { newPos.setX(item->pos().x()); } else if(alignMode == QStringLiteral("Horizontal")) { newPos.setY(item->pos().y()); } } } else { // For general views we check if we need to snap to a position if (!(QApplication::keyboardModifiers() & Qt::AltModifier)) { snapPosition(newPos); } } return newPos; } if (change == ItemSelectedHasChanged && scene()) { bool thisViewIsSelected = value.toBool(); bool anyChildSelected = false; if (!thisViewIsSelected) { // Only check children if this view is becoming unselected anyChildSelected = std::ranges::any_of(childItems(), [](QGraphicsItem* child) { return child->isSelected(); }); } if(thisViewIsSelected || anyChildSelected || isSelected()) { m_colCurrent = getSelectColor(); m_border->show(); m_label->show(); m_lock->setVisible(getViewObject()->isLocked() && getViewObject()->showLock()); } else { dragFinished(); if (!m_isHovered) { m_colCurrent = PreferencesGui::getAccessibleQColor(PreferencesGui::normalQColor()); m_border->hide(); m_label->hide(); m_lock->hide(); } else { m_colCurrent = getPreColor(); } } drawBorder(); update(); } return QGraphicsItemGroup::itemChange(change, value); } //! The default behaviour here only applies to views whose (X, Y) describes a position on the page. //! Others, like QGILeaderLine whose (X, Y) describes a position within a view's boundary and is not //! draggable, should override this method. void QGIView::dragFinished() { if (!viewObj) { return; } double currX = viewObj->X.getValue(); double currY = viewObj->Y.getValue(); double candidateX = Rez::appX(pos().x()); double candidateY = Rez::appX(-pos().y()); bool setX = false; bool setY = false; const double tolerance = 0.001; // mm if (!DrawUtil::fpCompare(currX, candidateX, tolerance)) { setX = true; } if (!DrawUtil::fpCompare(currY, candidateY, tolerance)) { setY = true; } if (!setX && !setY) { return; } bool ownTransaction = (viewObj->getDocument()->getTransactionID(true) == 0); if (ownTransaction) { Gui::Command::openCommand("Drag view"); } // tell the feature that we have moved Gui::ViewProvider *vp = getViewProvider(viewObj); if (vp && !vp->isRestoring()) { snapping = true; // avoid triggering updateView by the VP updateData if (setX) { Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.X = %f", viewObj->getNameInDocument(), candidateX); } if (setY) { Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.Y = %f", viewObj->getNameInDocument(), candidateY); } snapping = false; } if (ownTransaction) { Gui::Command::commitCommand(); } } //! align this view with others. newPosition is in this view's parent's coord //! system. if this view is not in a ProjectionGroup, then this is the scene //! position, otherwise it is the position within the ProjectionGroup. void QGIView::snapPosition(QPointF& newPosition) { if (!Preferences::SnapViews()) { return; } auto feature = getViewObject(); if (!feature) { return; } auto dvp = freecad_cast(feature); if (dvp && !dvp->hasGeometry()) { // too early. wait for updates to finish. return; } auto vpPage = getViewProviderPage(feature); QGSPage* scenePage = vpPage->getQGSPage(); if (!scenePage) { return; } auto* sectionView = dynamic_cast(feature); if (sectionView) { snapSectionView(sectionView, newPosition); return; } // For general views we check if the view is close to aligned vertically or horizontally to another view. // if we are not a section view, then we could be in a projection group and // need to get the correct scene position. auto newScenePos = newPosition; if (parentItem()) { newScenePos = parentItem()->mapToScene(newPosition); } // First get a list of the views of the page. qreal snapPercent = Preferences::SnapLimitFactor(); std::vector views = scenePage->getViews(); for (auto* view : views) { if (view == this) { continue; } auto viewFeature = view->getViewObject(); auto viewDvp = freecad_cast(viewFeature); auto viewScenePos = view->scenePos(); if (viewDvp && DrawView::isProjGroupItem(viewDvp)) { viewScenePos = DU::toQPointF(projItemPagePos(viewDvp)); viewScenePos = DU::invertY(Rez::guiX(viewScenePos)); } auto xwindow = view->boundingRect().width() * snapPercent; auto ywindow = view->boundingRect().height() * snapPercent; auto xerror = fabs(newScenePos.x() - viewScenePos.x()); auto yerror = fabs(newScenePos.y() - viewScenePos.y()); // if the smaller of vertical and horizontal errors is within the acceptable // window, snap to position. if (xerror <= yerror && xerror <= xwindow) { newScenePos.setX(viewScenePos.x()); if (parentItem()) { newScenePos = parentItem()->mapFromScene(newScenePos); } newPosition = newScenePos; return; } if (yerror < xerror && yerror <= ywindow) { newScenePos.setY(viewScenePos.y()); if (parentItem()) { newScenePos = parentItem()->mapFromScene(newScenePos); } newPosition = newScenePos; return; } } } //! snap this section view to its base view. The section should be positioned on //! line from the base view along the section normal direction, ie the same direction //! as the arrows on the section line. // Note: positions are in Qt inverted Y coordinates. They need to be converted before // doing math on them, then converted back on return. // Note: section views are never inside a ProjectionGroup, so their position is // always in scene coordinates. void QGIView::snapSectionView(const TechDraw::DrawViewSection* sectionView, QPointF& newPosition) { auto* baseView = sectionView->getBaseDVP(); if (!baseView) { return; } auto* vpdv = freecad_cast(getViewProvider(baseView)); if (!vpdv) { return; } auto* qgiv(dynamic_cast(vpdv->getQView())); if (!qgiv) { return; } Base::Vector3d arrowDirection = sectionView->SectionNormal.getValue() * -1; auto arrowDirectionOnBase = baseView->projectPoint(arrowDirection, false); if (arrowDirectionOnBase.Length() < Precision::Confusion()) { return; } arrowDirectionOnBase.Normalize(); double baseSize = Rez::guiX(baseView->getSizeAlongVector(arrowDirectionOnBase)); double snapDist = baseSize * getScale() * Preferences::SnapLimitFactor(); // find the scene position of the SO on the base view auto baseX = baseView->X.getValue(); auto baseY = baseView->Y.getValue(); Base::Vector3d baseScenePos{baseX, baseY, 0}; // paper space position if (DrawView::isProjGroupItem(baseView)) { baseScenePos = projItemPagePos(baseView); } auto sectionOrg3d = sectionView->SectionOrigin.getValue(); auto shapeCenter3d = baseView->getCurrentCentroid(); auto baseShapeCenter = baseView->projectPoint(shapeCenter3d, false); auto baseSectionOrg = baseView->projectPoint(sectionOrg3d, false); auto baseSOOffset = (baseSectionOrg - baseShapeCenter) * baseView->getScale(); auto baseSOScenePos = baseScenePos + baseSOOffset; // find the SO offset from origin on the rotated & scaled sectionView auto sectionCutCenter = sectionView->projectPoint(sectionView->getCutCentroid(), false); auto sectionSectionOrg = sectionView->projectPoint(sectionOrg3d, false); auto sectionSOOffset = (sectionSectionOrg - sectionCutCenter) * sectionView->getScale(); auto sectionRotationDeg = sectionView->Rotation.getValue(); sectionSOOffset.RotateZ(Base::toRadians(sectionRotationDeg)); // from here on, we work with scene units (1/10 mm) sectionSOOffset = Rez::guiX(sectionSOOffset); baseSOScenePos = Rez::guiX(baseSOScenePos); // check our alignment auto newSOPosition = DU::invertY(DU::toVector3d(newPosition)) + sectionSOOffset; Base::Vector3d actualAlignmentVector = newSOPosition - baseSOScenePos; actualAlignmentVector.Normalize(); // if we are not on the correct side of the section line, we should not try to snap auto dot = arrowDirectionOnBase.Dot(actualAlignmentVector); if (dot <= 0) { return; } auto pointOnArrowLine = newSOPosition.Perpendicular(baseSOScenePos, arrowDirectionOnBase); auto errorVector = pointOnArrowLine - newSOPosition; if (errorVector.Length() < snapDist) { // get the position point corresponding to our SO alignment auto netPosition = pointOnArrowLine - sectionSOOffset; netPosition = DU::invertY(netPosition); newPosition = DU::toQPointF(netPosition); } return; } Base::Vector3d QGIView::projItemPagePos(DrawViewPart* item) { if (!DrawView::isProjGroupItem(item)) { return Base::Vector3d(0, 0, 0); } auto dpgi = static_cast(item); auto group = dpgi->getPGroup(); auto itemX = dpgi->X.getValue(); auto itemY = dpgi->Y.getValue(); Base::Vector3d itemOffsetPos{itemX, itemY, 0}; // relative to group auto groupX = group->X.getValue(); auto groupY = group->Y.getValue(); Base::Vector3d groupPagePos{groupX, groupY, 0}; // relative to page return groupPagePos + itemOffsetPos; } void QGIView::mousePressEvent(QGraphicsSceneMouseEvent * event) { // this is never called for balloons (and dimensions?) because the label objects do not // inherit from QGIView, but directly from QGraphicsItem. - wf Qt::KeyboardModifiers originalModifiers = event->modifiers(); if (event->button()&Qt::LeftButton) { m_multiselectActivated = false; } if (event->button() == Qt::LeftButton && PreferencesGui::multiSelection()) { std::vector selection = Gui::Selection().getSelectionEx(); if (!DrawGuiUtil::getSubsForSelectedObject(selection, getViewObject()).empty()) { // we have already selected geometry for this view m_multiselectActivated = true; event->setModifiers(originalModifiers | Qt::ControlModifier); } } QGraphicsItemGroup::mousePressEvent(event); event->setModifiers(originalModifiers); } void QGIView::mouseMoveEvent(QGraphicsSceneMouseEvent * event) { QGraphicsItemGroup::mouseMoveEvent(event); } void QGIView::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) { Qt::KeyboardModifiers originalModifiers = event->modifiers(); if ((event->button()&Qt::LeftButton) && m_multiselectActivated) { if (PreferencesGui::multiSelection()) { event->setModifiers(originalModifiers | Qt::ControlModifier); } m_multiselectActivated = false; } dragFinished(); QGraphicsItemGroup::mouseReleaseEvent(event); event->setModifiers(originalModifiers); } void QGIView::hoverEnterEvent(QGraphicsSceneHoverEvent *event) { QGraphicsItemGroup::hoverEnterEvent(event); m_isHovered = true; if (isSelected()) { m_colCurrent = getSelectColor(); } else { m_colCurrent = getPreColor(); } m_border->show(); m_label->show(); m_lock->setVisible(getViewObject()->isLocked() && getViewObject()->showLock()); drawBorder(); update(); } void QGIView::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) { QGraphicsItemGroup::hoverLeaveEvent(event); m_isHovered = false; if (isSelected()) { m_colCurrent = getSelectColor(); m_border->show(); m_label->show(); m_lock->setVisible(getViewObject()->isLocked() && getViewObject()->showLock()); } else { m_colCurrent = PreferencesGui::getAccessibleQColor(PreferencesGui::normalQColor()); m_border->hide(); m_label->hide(); m_lock->hide(); } drawBorder(); update(); } //sets position in /Gui(graphics), not /App void QGIView::setPosition(qreal xPos, qreal yPos) { // Base::Console().message("QGIV::setPosition(%.3f, %.3f) (gui)\n", x, y); double newX = xPos; double newY = -yPos; double oldX = pos().x(); double oldY = pos().y(); if (TechDraw::DrawUtil::fpCompare(newX, oldX) && TechDraw::DrawUtil::fpCompare(newY, oldY)) { return; } else { setPos(newX, newY); } } QGIViewClip* QGIView::getClipGroup() { if (!getViewObject()->isInClip()) { return nullptr; } auto parentClip( dynamic_cast( parentItem() ) ); if (!parentClip) return nullptr; auto parentView( dynamic_cast( parentClip->parentItem() ) ); return parentView; } void QGIView::updateView(bool forceUpdate) { // Base::Console().message("QGIV::updateView() - %s\n", getViewObject()->getNameInDocument()); //allow/prevent dragging if (getViewObject()->isLocked()) { setFlag(QGraphicsItem::ItemIsMovable, false); } else { setFlag(QGraphicsItem::ItemIsMovable, true); } if (getViewObject() && forceUpdate) { setPosition(Rez::guiX(getViewObject()->X.getValue()), Rez::guiX(getViewObject()->Y.getValue())); } double appRotation = getViewObject()->Rotation.getValue(); double guiRotation = rotation(); if (!TechDraw::DrawUtil::fpCompare(appRotation, guiRotation)) { rotateView(); } drawBorder(); QGIView::draw(); } //QGIVP derived classes do not need a rotate view method as rotation is handled on App side. void QGIView::rotateView() { //NOTE: QPainterPaths have to be rotated individually. This transform handles Rotation for everything else. //Scale is handled in GeometryObject for DVP & descendents //Objects not descended from DVP must setScale for themselves //note that setTransform(, ,rotation, ,) is not the same as setRotation!!! double rot = getViewObject()->Rotation.getValue(); QPointF centre = boundingRect().center(); setTransform(QTransform().translate(centre.x(), centre.y()).rotate(-rot).translate(-centre.x(), -centre.y())); } double QGIView::getScale() { TechDraw::DrawView* feat = getViewObject(); if (!feat) { return 1.0; } return feat->getScale(); } const char * QGIView::getViewName() const { return viewName.c_str(); } const std::string QGIView::getViewNameAsString() const { return viewName; } TechDraw::DrawView * QGIView::getViewObject() const { return viewObj; } void QGIView::setViewFeature(TechDraw::DrawView *obj) { if (!obj) return; viewObj = obj; viewName = obj->getNameInDocument(); //mark the actual QGraphicsItem so we can check what's in the scene later setData(0, QStringLiteral("QGIV")); setData(1, QString::fromUtf8(obj->getNameInDocument())); } void QGIView::toggleCache(bool state) { // temp for devl. chaching was hiding problems WF //setCacheMode((state)? NoCache : NoCache); Q_UNUSED(state); setCacheMode(NoCache); } void QGIView::draw() { // Base::Console().message("QGIV::draw()\n"); double xFeat, yFeat; if (getViewObject()) { xFeat = Rez::guiX(getViewObject()->X.getValue()); yFeat = Rez::guiX(getViewObject()->Y.getValue()); if (!getViewObject()->LockPosition.getValue()) { setPosition(xFeat, yFeat); } } if (isVisible()) { show(); } else { hide(); } } void QGIView::prepareCaption() { m_caption->setDefaultTextColor(prefNormalColor()); m_font.setFamily(Preferences::labelFontQString()); int fontSize = exactFontSize(Preferences::labelFont(), Preferences::labelFontSizeMM()); m_font.setPixelSize(fontSize); m_caption->setFont(m_font); QString captionStr = QString::fromUtf8(getViewObject()->Caption.getValue()); m_caption->setPlainText(captionStr); } void QGIView::layoutDecorations(const QRectF& contentArea, const QRectF& captionRect, const QRectF& labelRect, QRectF& outFrameRect, QPointF& outCaptionPos, QPointF& outLabelPos, QPointF& outLockPos) const { const qreal padding = 100.0; QRectF paddedContentArea = contentArea.adjusted(-padding, -padding, padding, padding); double frameWidth = qMax(paddedContentArea.width(), labelRect.width()); double frameHeight = paddedContentArea.height() + (captionRect.height() / 2) + labelRect.height(); outFrameRect = QRectF(paddedContentArea.center().x() - (frameWidth / 2), paddedContentArea.top(), frameWidth, frameHeight); outLabelPos = QPointF(outFrameRect.center().x() - (labelRect.width() / 2), outFrameRect.bottom() - labelRect.height()); double view_width = getViewObject()->getRect().x(); outCaptionPos = QPointF(view_width - (captionRect.width() / 2), outLabelPos.y() - captionRect.height()); outLockPos = QPointF(outFrameRect.left(), outFrameRect.bottom() - m_lockHeight); } void QGIView::drawBorder() { // Base::Console().message("QGIV::drawBorder() - %s\n", getViewName()); auto feat = getViewObject(); if (!feat) return; prepareCaption(); m_label->setDefaultTextColor(m_colCurrent); m_font.setFamily(Preferences::labelFontQString()); int fontSize = exactFontSize(Preferences::labelFont(), Preferences::labelFontSizeMM()); m_font.setPixelSize(fontSize); m_label->setFont(m_font); QString labelStr = QString::fromStdString(getViewObject()->Label.getValue()); m_label->setPlainText(labelStr); QRectF contentArea = customChildrenBoundingRect(); QRectF captionRect = m_caption->boundingRect(); QRectF labelRect = m_label->boundingRect(); QRectF finalFrameRect; QPointF finalCaptionPos, finalLabelPos, finalLockPos; layoutDecorations(contentArea, captionRect, labelRect, finalFrameRect, finalCaptionPos, finalLabelPos, finalLockPos); m_caption->setPos(finalCaptionPos); m_label->setPos(finalLabelPos); m_lock->setPos(finalLockPos); m_lock->setZValue(ZVALUE::LOCK); m_border->setBrush(Qt::NoBrush); m_decorPen.setColor(m_colCurrent); m_border->setPen(m_decorPen); m_border->setPos(0., 0.); // Adjust the final border to make space for the label m_border->setRect(finalFrameRect.adjusted(-2, -2, 2, -labelRect.height() + 2)); prepareGeometryChange(); } void QGIView::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { QStyleOptionGraphicsItem myOption(*option); myOption.state &= ~QStyle::State_Selected; // painter->setPen(Qt::red); // painter->drawRect(boundingRect()); //good for debugging QGraphicsItemGroup::paint(painter, &myOption, widget); } QRectF QGIView::customChildrenBoundingRect() const { QList children = childItems(); // exceptions not to be included in determining the frame rectangle QRectF result; for (auto& child : children) { if (!child->isVisible()) { continue; } if ( child->type() != UserType::QGIRichAnno && child->type() != UserType::QGEPath && child->type() != UserType::QGMText && child->type() != UserType::QGCustomBorder && child->type() != UserType::QGCustomLabel && child->type() != UserType::QGICaption && child->type() != UserType::QGIVertex && child->type() != UserType::QGICMark) { QRectF childRect = mapFromItem(child, child->boundingRect()).boundingRect(); result = result.united(childRect); } } return result; } QRectF QGIView::boundingRect() const { QRectF totalRect = customChildrenBoundingRect(); totalRect = totalRect.united(m_border->rect()); totalRect = totalRect.united(m_label->boundingRect().translated(m_label->pos())); totalRect = totalRect.united(m_caption->boundingRect().translated(m_caption->pos())); if (m_lock && m_lock->isVisible()) { totalRect = totalRect.united(m_border->mapRectToParent(m_lock->boundingRect().translated(m_lock->pos()))); } if (totalRect.isEmpty()) { return QRectF(0, 0, 1, 1); } return totalRect; } QGIView* QGIView::getQGIVByName(std::string name) const { QList qgItems = scene()->items(); QList::iterator it = qgItems.begin(); for (; it != qgItems.end(); it++) { QGIView* qv = dynamic_cast((*it)); if (qv) { std::string qvName = qv->getViewNameAsString(); if (name == qvName) { return (qv); } } } return nullptr; } /* static */ Gui::ViewProvider* QGIView::getViewProvider(App::DocumentObject* obj) { if (obj) { Gui::Document* guiDoc = Gui::Application::Instance->getDocument(obj->getDocument()); return guiDoc->getViewProvider(obj); } return nullptr; } MDIViewPage* QGIView::getMDIViewPage() const { if (!getViewObject()) { return nullptr; } ViewProviderPage* vpp = getViewProviderPage(getViewObject()); if (vpp) { return vpp->getMDIViewPage(); } return nullptr; } ViewProviderPage* QGIView::getViewProviderPage(TechDraw::DrawView* dView) { if (!dView) { return nullptr; } TechDraw::DrawPage* page = dView->findParentPage(); if (!page) { return nullptr; } Gui::Document* activeGui = Gui::Application::Instance->getDocument(page->getDocument()); if (!activeGui) { return nullptr; } return freecad_cast(activeGui->getViewProvider(page)); } //remove a child of this from scene while keeping scene indexes valid void QGIView::removeChild(QGIView* child) { if (child && (child->parentItem() == this) ) { prepareGeometryChange(); scene()->removeItem(child); } } void QGIView::hideFrame() { m_border->hide(); m_label->hide(); } void QGIView::addArbitraryItem(QGraphicsItem* qgi) { if (qgi) { // m_randomItems.push_back(qgi); addToGroup(qgi); qgi->show(); } } void QGIView::switchParentItem(QGIView *targetParent) { auto currentParent = dynamic_cast(this->parentItem()); if (currentParent != targetParent) { if (targetParent) { targetParent->addToGroup(this); targetParent->updateView(); if (currentParent) { currentParent->updateView(); } } else { while (currentParent) { currentParent->removeFromGroup(this); currentParent->updateView(); currentParent = dynamic_cast(this->parentItem()); } } } } void QGIView::setStack(int z) { m_zOrder = z; setZValue(z); draw(); } void QGIView::setStackFromVP() { TechDraw::DrawView* feature = getViewObject(); ViewProviderDrawingView* vpdv = static_cast (getViewProvider(feature)); int z = vpdv->getZ(); setStack(z); } QColor QGIView::prefNormalColor() { return PreferencesGui::getAccessibleQColor(PreferencesGui::normalQColor()); } QColor QGIView::getPreColor() { return PreferencesGui::getAccessibleQColor(PreferencesGui::preselectQColor()); } QColor QGIView::getSelectColor() { return PreferencesGui::getAccessibleQColor(PreferencesGui::selectQColor()); } Base::Reference QGIView::getParmGroupCol() { return App::GetApplication().GetUserParameter() .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/Colors"); } //convert input font size in mm to scene units //note that when used to set font size this will result in //text that is smaller than sizeInMillimetres. If exactly //correct sized text is required, use exactFontSize. int QGIView::calculateFontPixelSize(double sizeInMillimetres) { // Calculate font size in pixels by using resolution conversion // and round to nearest integer return (int) (Rez::guiX(sizeInMillimetres) + 0.5); } int QGIView::calculateFontPixelWidth(const QFont &font) { // Return the width of digit 0, most likely the most wide digit return Gui::QtTools::horizontalAdvance(QFontMetrics(font), QChar::fromLatin1('0')); } const double QGIView::DefaultFontSizeInMM = 5.0; void QGIView::dumpRect(const char* text, QRectF rect) { Base::Console().message("DUMP - %s - rect: (%.3f, %.3f) x (%.3f, %.3f)\n", text, rect.left(), rect.top(), rect.right(), rect.bottom()); } //determine the required font size to generate text with upper case //letter height = nominalSize int QGIView::exactFontSize(std::string fontFamily, double nominalSize) { double sceneSize = Rez::guiX(nominalSize); //desired height in scene units QFont font; font.setFamily(QString::fromUtf8(fontFamily.c_str())); font.setPixelSize(sceneSize); QFontMetricsF fm(font); double capHeight = fm.capHeight(); double ratio = sceneSize / capHeight; return (int) sceneSize * ratio; } void QGIView::makeMark(double xPos, double yPos, QColor color) { QGIVertex* vItem = new QGIVertex(-1); vItem->setParentItem(this); vItem->setPos(xPos, yPos); vItem->setWidth(2.0); vItem->setRadius(20.0); vItem->setNormalColor(color); vItem->setFillColor(color); vItem->setPrettyNormal(); vItem->setZValue(ZVALUE::VERTEX); } void QGIView::makeMark(Base::Vector3d pos, QColor color) { makeMark(pos.x, pos.y, color); } void QGIView::makeMark(QPointF pos, QColor color) { makeMark(pos.x(), pos.y(), color); } //! Retrieves objects of type T with given indexes template std::vector QGIView::getObjects(std::vector indexes) { QList children = childItems(); std::vector result; for (QGraphicsItem*& child : children) { // Convert QGIVertex* (as T) to QGIVertex if (child->type() != std::remove_pointer::type::Type) { continue; } // Get index of child item T object = static_cast(child); int target = object->getProjIndex(); // If child item's index in indexes, then add to results if (std::ranges::find(indexes, target) != indexes.end()) { result.push_back(object); } } return result; } template std::vector QGIView::getObjects(std::vector); template std::vector QGIView::getObjects(std::vector); #include