/*************************************************************************** * Copyright (c) 2012-2013 Luke Parry * * * * 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 # include #endif #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 "QGIVertex.h" #include "QGIViewClip.h" #include "QGSPage.h" #include "QGVPage.h" #include "Rez.h" #include "ViewProviderDrawingView.h" #include "ViewProviderPage.h" #include "ZVALUE.h" using namespace TechDrawGui; using namespace TechDraw; #define NODRAG 0 #define DRAGSTARTED 1 #define DRAGGING 2 const float labelCaptionFudge = 0.2f; // temp fiddle for devel QGIView::QGIView() :QGraphicsItemGroup(), viewObj(nullptr), m_locked(false), m_innerView(false), m_dragState(NODRAG) { 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" m_label = new QGCustomLabel(); addToGroup(m_label); m_border = new QGCustomBorder(); addToGroup(m_border); m_caption = new QGICaption(); addToGroup(m_caption); m_lock = new QGCustomImage(); m_lock->setParentItem(m_border); m_lock->load(QString::fromUtf8(":/icons/TechDraw_Lock.svg")); QSize sizeLock = m_lock->imageSize(); m_lockWidth = (double) sizeLock.width(); m_lockHeight = (double) sizeLock.height(); m_lock->hide(); } QGIView::~QGIView() { signalSelectPoint.disconnect_all_slots(); } void QGIView::onSourceChange(TechDraw::DrawView* newParent) { Q_UNUSED(newParent); } void QGIView::isVisible(bool state) { auto feat = getViewObject(); if (feat) { auto vp = QGIView::getViewProvider(feat); if (vp) { Gui::ViewProviderDocumentObject* vpdo = dynamic_cast(vp); if (vpdo) { vpdo->Visibility.setValue(state); } } } } bool QGIView::isVisible() { bool result = false; auto feat = getViewObject(); if (feat) { auto vp = QGIView::getViewProvider(feat); if (vp) { Gui::ViewProviderDocumentObject* vpdo = dynamic_cast(vp); if (vpdo) { result = vpdo->Visibility.getValue(); } } } return result; } //Set selection state for this and it's children //required for items like dimensions & balloons void QGIView::setGroupSelection(bool isSelected) { setSelected(isSelected); } void QGIView::alignTo(QGraphicsItem*item, const QString &alignment) { alignHash.clear(); alignHash.insert(alignment, item); } QVariant QGIView::itemChange(GraphicsItemChange change, const QVariant &value) { QPointF newPos(0.0, 0.0); // Base::Console().Message("QGIV::itemChange(%d)\n", change); if(change == ItemPositionChange && scene()) { newPos = value.toPointF(); //position within parent! if(m_locked){ newPos.setX(pos().x()); newPos.setY(pos().y()); return newPos; } // TODO find a better data structure for this // this is just a pair isn't it? if (getViewObject()->isDerivedFrom(TechDraw::DrawProjGroupItem::getClassTypeId())) { TechDraw::DrawProjGroupItem* dpgi = static_cast(getViewObject()); TechDraw::DrawProjGroup* dpg = dpgi->getPGroup(); if (dpg) { if(alignHash.size() == 1) { //if aligned. QGraphicsItem* item = alignHash.begin().value(); QString alignMode = alignHash.begin().key(); if(alignMode == QString::fromLatin1("Vertical")) { newPos.setX(item->pos().x()); } else if(alignMode == QString::fromLatin1("Horizontal")) { newPos.setY(item->pos().y()); } } } } else { //not a dpgi, not locked, but moved. //feat->setPosition(Rez::appX(newPos.x()), -Rez::appX(newPos.y()); } return newPos; } if (change == ItemSelectedHasChanged && scene()) { if(isSelected()) { m_colCurrent = getSelectColor(); // m_selectState = 2; } else { m_colCurrent = PreferencesGui::normalQColor(); // m_selectState = 0; } drawBorder(); } return QGraphicsItemGroup::itemChange(change, value); } void QGIView::mousePressEvent(QGraphicsSceneMouseEvent * event) { // Base::Console().Message("QGIV::mousePressEvent() - %s\n", getViewName()); signalSelectPoint(this, event->pos()); if (m_dragState == NODRAG) { m_dragState = DRAGSTARTED; } QGraphicsItem::mousePressEvent(event); } void QGIView::mouseMoveEvent(QGraphicsSceneMouseEvent * event) { if (m_dragState == DRAGSTARTED) { m_dragState = DRAGGING; } QGraphicsItem::mouseMoveEvent(event); } void QGIView::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) { //TODO: this should be done in itemChange - item position has changed // Base::Console().Message("QGIV::mouseReleaseEvent() - %s\n", getViewName()); // if(scene() && this == scene()->mouseGrabberItem()) { if (m_dragState == DRAGGING) { if(!m_locked) { if (!isInnerView()) { double tempX = x(), tempY = getY(); getViewObject()->setPosition(Rez::appX(tempX), Rez::appX(tempY)); } else { getViewObject()->setPosition(Rez::appX(x()), Rez::appX(getYInClip(y()))); } } } m_dragState = NODRAG; QGraphicsItem::mouseReleaseEvent(event); } void QGIView::hoverEnterEvent(QGraphicsSceneHoverEvent *event) { // Base::Console().Message("QGIV::hoverEnterEvent()\n"); Q_UNUSED(event); // TODO don't like this but only solution at the minute (MLP) if (isSelected()) { m_colCurrent = getSelectColor(); } else { m_colCurrent = getPreColor(); } drawBorder(); } void QGIView::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) { Q_UNUSED(event); if(isSelected()) { m_colCurrent = getSelectColor(); } else { m_colCurrent = PreferencesGui::normalQColor(); } drawBorder(); } //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; double oldX = pos().x(); double oldY = pos().y(); if (!isInnerView()) { newY = -yPos; } else { newY = getYInClip(yPos); } if ( (TechDraw::DrawUtil::fpCompare(newX, oldX)) && (TechDraw::DrawUtil::fpCompare(newY, oldY)) ) { return; } else { setPos(newX, newY); } } //is this needed anymore??? double QGIView::getYInClip(double y) { return -y; } QGIViewClip* QGIView::getClipGroup() { if (!getViewObject()->isInClip()) { Base::Console().Log( "Logic Error - getClipGroup called for child " "(%s) not in Clip\n", getViewName() ); return nullptr; } QGIViewClip* result = nullptr; auto parentClip( dynamic_cast( parentItem() ) ); if (parentClip) { auto parentView( dynamic_cast( parentClip->parentItem() ) ); if (parentView) { result = parentView; } } return result; } 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(); } 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() { double result = 1.0; TechDraw::DrawView* feat = getViewObject(); if (feat) { result = feat->getScale(); } return result; } 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, QString::fromUtf8("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()) { drawBorder(); show(); } else { hide(); } } void QGIView::drawCaption() { // Base::Console().Message("QGIV::drawCaption()\n"); prepareGeometryChange(); QRectF displayArea = customChildrenBoundingRect(); m_caption->setDefaultTextColor(m_colCurrent); 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); QRectF captionArea = m_caption->boundingRect(); QPointF displayCenter = displayArea.center(); m_caption->setX(displayCenter.x() - captionArea.width()/2.); double labelHeight = (1 - labelCaptionFudge) * m_label->boundingRect().height(); auto vp = static_cast(getViewProvider(getViewObject())); if (getFrameState() || vp->KeepLabel.getValue()) { //place below label if label visible m_caption->setY(displayArea.bottom() + labelHeight); } else { m_caption->setY(displayArea.bottom() + labelCaptionFudge * Preferences::labelFontSizeMM()); } m_caption->show(); } void QGIView::drawBorder() { // Base::Console().Message("QGIV::drawBorder() - %s\n", getViewName()); auto feat = getViewObject(); if (!feat) return; drawCaption(); //always draw caption auto vp = static_cast(getViewProvider(getViewObject())); if (!getFrameState() && !vp->KeepLabel.getValue()) { m_label->hide(); m_border->hide(); m_lock->hide(); return; } m_label->hide(); m_border->hide(); m_lock->hide(); 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::fromUtf8(getViewObject()->Label.getValue()); m_label->setPlainText(labelStr); QRectF labelArea = m_label->boundingRect(); //m_label coords double labelWidth = m_label->boundingRect().width(); double labelHeight = (1 - labelCaptionFudge) * m_label->boundingRect().height(); QBrush b(Qt::NoBrush); m_border->setBrush(b); m_decorPen.setColor(m_colCurrent); m_border->setPen(m_decorPen); QRectF displayArea = customChildrenBoundingRect(); double displayWidth = displayArea.width(); double displayHeight = displayArea.height(); QPointF displayCenter = displayArea.center(); m_label->setX(displayCenter.x() - labelArea.width()/2.); m_label->setY(displayArea.bottom()); double frameWidth = displayWidth; if (labelWidth > displayWidth) { frameWidth = labelWidth; } double frameHeight = labelHeight + displayHeight; QRectF frameArea = QRectF(displayCenter.x() - frameWidth/2., displayArea.top(), frameWidth, frameHeight); double lockX = frameArea.left(); double lockY = frameArea.bottom() - m_lockHeight; if (feat->isLocked() && feat->showLock()) { m_lock->setZValue(ZVALUE::LOCK); m_lock->setPos(lockX, lockY); m_lock->show(); } else { m_lock->hide(); } prepareGeometryChange(); m_border->setRect(frameArea.adjusted(-2, -2, 2,2)); m_border->setPos(0., 0.); m_label->show(); if (getFrameState()) { m_border->show(); } } 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(); int dimItemType = QGraphicsItem::UserType + 106; // TODO: Get magic number from include by name int borderItemType = QGraphicsItem::UserType + 136; // TODO: Magic number warning int labelItemType = QGraphicsItem::UserType + 135; // TODO: Magic number warning int captionItemType = QGraphicsItem::UserType + 180; // TODO: Magic number warning int leaderItemType = QGraphicsItem::UserType + 232; // TODO: Magic number warning int textLeaderItemType = QGraphicsItem::UserType + 233; // TODO: Magic number warning int editablePathItemType = QGraphicsItem::UserType + 301; // TODO: Magic number warning int movableTextItemType = QGraphicsItem::UserType + 300; int weldingSymbolItemType = QGraphicsItem::UserType + 340; QRectF result; for (QList::iterator it = children.begin(); it != children.end(); ++it) { if (!(*it)->isVisible()) { continue; } if ( ((*it)->type() != dimItemType) && ((*it)->type() != leaderItemType) && ((*it)->type() != textLeaderItemType) && ((*it)->type() != editablePathItemType) && ((*it)->type() != movableTextItemType) && ((*it)->type() != borderItemType) && ((*it)->type() != labelItemType) && ((*it)->type() != weldingSymbolItemType) && ((*it)->type() != captionItemType) ) { QRectF childRect = mapFromItem(*it, (*it)->boundingRect()).boundingRect(); result = result.united(childRect); //result = result.united((*it)->boundingRect()); } } return result; } QRectF QGIView::boundingRect() const { return m_border->rect().adjusted(-2., -2., 2., 2.); //allow for border line width //TODO: fiddle brect if border off? } QGIView* QGIView::getQGIVByName(std::string name) { QList qgItems = scene()->items(); QList::iterator it = qgItems.begin(); for (; it != qgItems.end(); it++) { QGIView* qv = dynamic_cast((*it)); if (qv) { const char* qvName = qv->getViewName(); if(name.compare(qvName) == 0) { 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; } QGVPage* QGIView::getQGVPage(TechDraw::DrawView* dView) { ViewProviderPage* vpp = getViewProviderPage(dView); if (!vpp) { return vpp->getQGVPage(); } return nullptr; } QGSPage* QGIView::getQGSPage(TechDraw::DrawView* dView) { ViewProviderPage* vpp = getViewProviderPage(dView); if (vpp) { return vpp->getQGSPage(); } 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 dynamic_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); } } bool QGIView::getFrameState() { // Base::Console().Message("QGIV::getFrameState() - %s\n", getViewName()); bool result = true; TechDraw::DrawView* dv = getViewObject(); if (dv) { TechDraw::DrawPage* page = dv->findParentPage(); if (page) { Gui::Document* activeGui = Gui::Application::Instance->getDocument(page->getDocument()); Gui::ViewProvider* vp = activeGui->getViewProvider(page); ViewProviderPage* vpp = dynamic_cast(vp); if (vpp) { result = vpp->getFrameState(); } } } return result; } 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::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::normalQColor(); } QColor QGIView::getPreColor() { return PreferencesGui::preselectQColor(); } QColor QGIView::getSelectColor() { return PreferencesGui::selectQColor(); } Base::Reference QGIView::getParmGroupCol() { Base::Reference hGrp = App::GetApplication().GetUserParameter() .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/Colors"); return hGrp; } //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); } #include