diff --git a/src/Mod/TechDraw/App/AppTechDraw.cpp b/src/Mod/TechDraw/App/AppTechDraw.cpp index b79ebc04d9..a8461a119f 100644 --- a/src/Mod/TechDraw/App/AppTechDraw.cpp +++ b/src/Mod/TechDraw/App/AppTechDraw.cpp @@ -39,6 +39,7 @@ #include "DrawViewMulti.h" #include "DrawViewImage.h" #include "DrawViewDetail.h" +#include "DrawViewBalloon.h" namespace TechDraw { extern PyObject* initModule(); @@ -78,6 +79,7 @@ PyMOD_INIT_FUNC(TechDraw) TechDraw::DrawProjGroup ::init(); TechDraw::DrawProjGroupItem ::init(); TechDraw::DrawViewDetail ::init(); + TechDraw::DrawViewBalloon ::init(); TechDraw::DrawTemplate ::init(); diff --git a/src/Mod/TechDraw/App/CMakeLists.txt b/src/Mod/TechDraw/App/CMakeLists.txt index 5b279ccfe2..a26fb81e7a 100644 --- a/src/Mod/TechDraw/App/CMakeLists.txt +++ b/src/Mod/TechDraw/App/CMakeLists.txt @@ -74,6 +74,8 @@ SET(Draw_SRCS DrawViewCollection.h DrawViewDimension.cpp DrawViewDimension.h + DrawViewBalloon.cpp + DrawViewBalloon.h DrawViewSection.cpp DrawViewSection.h DrawHatch.cpp diff --git a/src/Mod/TechDraw/App/DrawPage.cpp b/src/Mod/TechDraw/App/DrawPage.cpp index cadbbf0482..6ac6e64242 100644 --- a/src/Mod/TechDraw/App/DrawPage.cpp +++ b/src/Mod/TechDraw/App/DrawPage.cpp @@ -49,6 +49,7 @@ #include "DrawViewCollection.h" #include "DrawViewPart.h" #include "DrawViewDimension.h" +#include "DrawViewBalloon.h" #include // generated from DrawPagePy.xml @@ -81,9 +82,9 @@ DrawPage::DrawPage(void) ADD_PROPERTY_TYPE(KeepUpdated, (autoUpdate), group, (App::PropertyType)(App::Prop_None), "Keep page in sync with model"); ADD_PROPERTY_TYPE(Template, (0), group, (App::PropertyType)(App::Prop_None), "Attached Template"); - Template.setScope(App::LinkScope::Global); + Template.setScope(App::LinkScope::Global); ADD_PROPERTY_TYPE(Views, (0), group, (App::PropertyType)(App::Prop_None), "Attached Views"); - Views.setScope(App::LinkScope::Global); + Views.setScope(App::LinkScope::Global); // Projection Properties ProjectionType.setEnums(ProjectionTypeEnums); @@ -255,8 +256,9 @@ int DrawPage::addView(App::DocumentObject *docObj) return -1; DrawView* view = static_cast(docObj); - //position all new views in center of Page (exceptDVDimension) - if (!docObj->isDerivedFrom(TechDraw::DrawViewDimension::getClassTypeId())) { + //position all new views in center of Page (exceptDVDimension) + if (!docObj->isDerivedFrom(TechDraw::DrawViewDimension::getClassTypeId()) && + !docObj->isDerivedFrom(TechDraw::DrawViewBalloon::getClassTypeId())) { view->X.setValue(getPageWidth()/2.0); view->Y.setValue(getPageHeight()/2.0); } diff --git a/src/Mod/TechDraw/App/DrawViewBalloon.cpp b/src/Mod/TechDraw/App/DrawViewBalloon.cpp new file mode 100644 index 0000000000..105416535c --- /dev/null +++ b/src/Mod/TechDraw/App/DrawViewBalloon.cpp @@ -0,0 +1,175 @@ +/*************************************************************************** + * Copyright (c) 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 +# include +# include +#include +#include +#include +#include +#include +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Geometry.h" +#include "DrawViewPart.h" +#include "DrawViewBalloon.h" +#include "DrawUtil.h" +#include "LineGroup.h" + + +//#include // generated from DrawViewDimensionPy.xml + +using namespace TechDraw; + +//=========================================================================== +// DrawViewDimension +//=========================================================================== + +PROPERTY_SOURCE(TechDraw::DrawViewBalloon, TechDraw::DrawView) + +const char* DrawViewBalloon::endTypeEnums[]= {"Arrow", + "Dot", + NULL}; + +const char* DrawViewBalloon::balloonTypeEnums[]= {"Circular", + "None", + "Triangle", + "Inspection", + "Hexagon", + "Square", + "Rectangle", + NULL}; + +DrawViewBalloon::DrawViewBalloon(void) +{ + ADD_PROPERTY_TYPE(Text , (""),"",App::Prop_None,"The text to be displayed"); + ADD_PROPERTY_TYPE(sourceView,(0),"",(App::PropertyType)(App::Prop_None),"Source view for balloon"); + ADD_PROPERTY_TYPE(OriginX,(0),"",(App::PropertyType)(App::Prop_None),"Balloon origin x"); + ADD_PROPERTY_TYPE(OriginY,(0),"",(App::PropertyType)(App::Prop_None),"Balloon origin y"); + ADD_PROPERTY_TYPE(OriginIsSet, (false), "",(App::PropertyType)(App::Prop_None),"Balloon origin is set"); + + EndType.setEnums(endTypeEnums); + ADD_PROPERTY(EndType,((long)0)); + + Symbol.setEnums(balloonTypeEnums); + ADD_PROPERTY(Symbol,((long)0)); + + ADD_PROPERTY_TYPE(SymbolScale,(1),"",(App::PropertyType)(App::Prop_None),"Balloon symbol scale"); + + ADD_PROPERTY_TYPE(TextWrapLen,(-1),"",(App::PropertyType)(App::Prop_None),"Balloon symbol scale"); + + OriginX.setStatus(App::Property::Hidden,false); + OriginY.setStatus(App::Property::Hidden,false); + OriginIsSet.setStatus(App::Property::Hidden,false); + OriginIsSet.setStatus(App::Property::ReadOnly,true); + + sourceView.setScope(App::LinkScope::Global); + sourceView.setStatus(App::Property::Hidden,true); + Rotation.setStatus(App::Property::Hidden,true); + ScaleType.setStatus(App::Property::Hidden,true); + Scale.setStatus(App::Property::Hidden,true); + Caption.setStatus(App::Property::Hidden,true); + X.setStatus(App::Property::Hidden,true); + Y.setStatus(App::Property::Hidden,true); +} + +DrawViewBalloon::~DrawViewBalloon() +{ + +} + +void DrawViewBalloon::onChanged(const App::Property* prop) +{ + DrawView::onChanged(prop); +} + +void DrawViewBalloon::onDocumentRestored() +{ + +} + + +short DrawViewBalloon::mustExecute() const +{ + bool result = 0; + if (!isRestoring()) { + result = Text.isTouched(); + } + + if (result) { + return result; + } + + auto dvp = getViewPart(); + if (dvp != nullptr) { + result = dvp->isTouched(); + } + if (result) { + return result; + } + + return DrawView::mustExecute(); +} + +DrawViewPart* DrawViewBalloon::getViewPart() const +{ + App::DocumentObject* obj = sourceView.getValue(); + DrawViewPart* result = dynamic_cast(obj); + return result; +} + +App::DocumentObjectExecReturn *DrawViewBalloon::execute(void) +{ + requestPaint(); + return App::DocumentObject::execute(); +} +/* +PyObject *DrawViewBalloon::getPyObject(void) +{ + if (PythonObject.is(Py::_None())) { + // ref counter is set to 1 + PythonObject = Py::Object(new DrawViewBalloonPy(this),true); + } + return Py::new_reference_to(PythonObject); +} +*/ diff --git a/src/Mod/TechDraw/App/DrawViewBalloon.h b/src/Mod/TechDraw/App/DrawViewBalloon.h new file mode 100644 index 0000000000..cce389461f --- /dev/null +++ b/src/Mod/TechDraw/App/DrawViewBalloon.h @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright (c) 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 * + * * + ***************************************************************************/ + +#ifndef _TechDraw_DrawViewBalloon_h_ +#define _TechDraw_DrawViewBalloon_h_ +#include + +# include +# include +# include + +#include "DrawView.h" + +class TopoDS_Shape; + +namespace Measure { +class Measurement; +} +namespace TechDraw { + +class DrawViewPart; + +class TechDrawExport DrawViewBalloon : public TechDraw::DrawView +{ + PROPERTY_HEADER(TechDraw::DrawViewBalloon); + +public: + /// Constructor + DrawViewBalloon(); + virtual ~DrawViewBalloon(); + + App::PropertyLink sourceView; + App::PropertyString Text; + App::PropertyEnumeration EndType; + App::PropertyEnumeration Symbol; + App::PropertyFloat SymbolScale; + App::PropertyFloat OriginX; + App::PropertyFloat OriginY; + App::PropertyBool OriginIsSet; + App::PropertyFloat TextWrapLen; + + short mustExecute() const; + + DrawViewPart* getViewPart() const; + + //virtual PyObject *getPyObject(void); + + virtual App::DocumentObjectExecReturn *execute(void); + //@} + + /// returns the type name of the ViewProvider + virtual const char* getViewProviderName(void) const { + return "TechDrawGui::ViewProviderBalloon"; + } + +protected: + void onChanged(const App::Property* prop); + virtual void onDocumentRestored(); + +private: + static const char* endTypeEnums[]; + static const char* balloonTypeEnums[]; +}; + +} //namespace TechDraw +#endif diff --git a/src/Mod/TechDraw/App/DrawViewPart.cpp b/src/Mod/TechDraw/App/DrawViewPart.cpp index 4df61eecd7..75cff63844 100644 --- a/src/Mod/TechDraw/App/DrawViewPart.cpp +++ b/src/Mod/TechDraw/App/DrawViewPart.cpp @@ -97,6 +97,7 @@ #include "DrawHatch.h" #include "DrawGeomHatch.h" #include "DrawViewDimension.h" +#include "DrawViewBalloon.h" #include "DrawViewDetail.h" #include "DrawPage.h" #include "EdgeWalker.h" @@ -559,6 +560,20 @@ std::vector DrawViewPart::getDimensions() const return result; } +std::vector DrawViewPart::getBalloons() const +{ + std::vector result; + std::vector children = getInList(); + std::sort(children.begin(),children.end(),std::less()); + std::vector::iterator newEnd = std::unique(children.begin(),children.end()); + for (std::vector::iterator it = children.begin(); it != newEnd; ++it) { + if ((*it)->getTypeId().isDerivedFrom(DrawViewBalloon::getClassTypeId())) { + TechDraw::DrawViewBalloon* balloon = dynamic_cast(*it); + result.push_back(balloon); + } + } + return result; +} const std::vector & DrawViewPart::getVertexGeometry() const { @@ -837,6 +852,22 @@ void DrawViewPart::unsetupObject() } } + // Remove Balloons which reference this DVP + // must use page->removeObject first + page = findParentPage(); + if (page != nullptr) { + std::vector balloons = getBalloons(); + std::vector::iterator it3 = balloons.begin(); + for (; it3 != balloons.end(); it3++) { + page->removeView(*it3); + const char* name = (*it3)->getNameInDocument(); + if (name) { + Base::Interpreter().runStringArg("App.getDocument(\"%s\").removeObject(\"%s\")", + docName.c_str(), name); + } + } + } + } //! is this an Isometric projection? diff --git a/src/Mod/TechDraw/App/DrawViewPart.h b/src/Mod/TechDraw/App/DrawViewPart.h index 11887d79de..265da3e50e 100644 --- a/src/Mod/TechDraw/App/DrawViewPart.h +++ b/src/Mod/TechDraw/App/DrawViewPart.h @@ -67,6 +67,7 @@ class DrawViewDimension; class DrawProjectSplit; class DrawViewSection; class DrawViewDetail; +class DrawViewBalloon; } namespace TechDraw @@ -103,6 +104,7 @@ public: std::vector getHatches(void) const; std::vector getGeomHatches(void) const; std::vector getDimensions() const; + std::vector getBalloons() const; //TODO: are there use-cases for Python access to TechDrawGeometry??? diff --git a/src/Mod/TechDraw/CMakeLists.txt b/src/Mod/TechDraw/CMakeLists.txt index c036221fc6..1d28eda752 100644 --- a/src/Mod/TechDraw/CMakeLists.txt +++ b/src/Mod/TechDraw/CMakeLists.txt @@ -96,6 +96,7 @@ SET(TDTest_SRCS TDTest/DVDimensionTest.py TDTest/DVPartTest.py TDTest/DVSectionTest.py + TDTest/DVBalloonTest.py ) SET(TDTestFile_SRCS diff --git a/src/Mod/TechDraw/Gui/AppTechDrawGui.cpp b/src/Mod/TechDraw/Gui/AppTechDrawGui.cpp index f18bd3e27a..d577db6bae 100644 --- a/src/Mod/TechDraw/Gui/AppTechDrawGui.cpp +++ b/src/Mod/TechDraw/Gui/AppTechDrawGui.cpp @@ -43,6 +43,7 @@ #include "ViewProviderPage.h" #include "ViewProviderDrawingView.h" #include "ViewProviderDimension.h" +#include "ViewProviderBalloon.h" #include "ViewProviderProjGroup.h" #include "ViewProviderProjGroupItem.h" #include "ViewProviderTemplate.h" @@ -113,6 +114,7 @@ PyMOD_INIT_FUNC(TechDrawGui) TechDrawGui::ViewProviderTemplate::init(); TechDrawGui::ViewProviderDimension::init(); + TechDrawGui::ViewProviderBalloon::init(); TechDrawGui::ViewProviderViewPart::init(); TechDrawGui::ViewProviderProjGroupItem::init(); TechDrawGui::ViewProviderProjGroup::init(); diff --git a/src/Mod/TechDraw/Gui/CMakeLists.txt b/src/Mod/TechDraw/Gui/CMakeLists.txt index d6d54b3807..169667a13d 100644 --- a/src/Mod/TechDraw/Gui/CMakeLists.txt +++ b/src/Mod/TechDraw/Gui/CMakeLists.txt @@ -30,6 +30,7 @@ set(TechDrawGui_MOC_HDRS QGISVGTemplate.h QGIDrawingTemplate.h QGIViewDimension.h + QGIViewBalloon.h TaskProjGroup.h DlgPrefsTechDrawImp.h DlgPrefsTechDraw2Imp.h @@ -148,6 +149,8 @@ SET(TechDrawGuiView_SRCS QGIViewCollection.h QGIViewDimension.cpp QGIViewDimension.h + QGIViewBalloon.cpp + QGIViewBalloon.h QGIProjGroup.cpp QGIProjGroup.h QGIViewPart.cpp @@ -193,6 +196,8 @@ SET(TechDrawGuiViewProvider_SRCS ViewProviderTemplate.h ViewProviderDimension.cpp ViewProviderDimension.h + ViewProviderBalloon.cpp + ViewProviderBalloon.h ViewProviderViewPart.cpp ViewProviderViewPart.h ViewProviderProjGroup.cpp diff --git a/src/Mod/TechDraw/Gui/Command.cpp b/src/Mod/TechDraw/Gui/Command.cpp index bf74d0da50..77264fc351 100644 --- a/src/Mod/TechDraw/Gui/Command.cpp +++ b/src/Mod/TechDraw/Gui/Command.cpp @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -680,6 +681,109 @@ bool CmdTechDrawAnnotation::isActive(void) return DrawGuiUtil::needPage(this); } +//=========================================================================== +// TechDraw_NewBalloon +//=========================================================================== + +//! common checks of Selection for Dimension commands +//non-empty selection, no more than maxObjs selected and at least 1 DrawingPage exists +bool _checkSelectionBalloon(Gui::Command* cmd, unsigned maxObjs) { + std::vector selection = cmd->getSelection().getSelectionEx(); + if (selection.size() == 0) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect selection"), + QObject::tr("Select an object first")); + return false; + } + + const std::vector SubNames = selection[0].getSubNames(); + if (SubNames.size() > maxObjs){ + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect selection"), + QObject::tr("Too many objects selected")); + return false; + } + + std::vector pages = cmd->getDocument()->getObjectsOfType(TechDraw::DrawPage::getClassTypeId()); + if (pages.empty()){ + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect selection"), + QObject::tr("Create a page first.")); + return false; + } + return true; +} + +bool _checkDrawViewPartBalloon(Gui::Command* cmd) { + std::vector selection = cmd->getSelection().getSelectionEx(); + auto objFeat( dynamic_cast(selection[0].getObject()) ); + if( !objFeat ) { + QMessageBox::warning( Gui::getMainWindow(), + QObject::tr("Incorrect selection"), + QObject::tr("No View of a Part in selection.") ); + return false; + } + return true; +} + +DEF_STD_CMD_A(CmdTechDrawNewBalloon); + +CmdTechDrawNewBalloon::CmdTechDrawNewBalloon() + : Command("TechDraw_NewBalloon") +{ + sAppModule = "TechDraw"; + sGroup = QT_TR_NOOP("TechDraw"); + sMenuText = QT_TR_NOOP("Insert a new balloon"); + sToolTipText = QT_TR_NOOP("Insert a new balloon"); + sWhatsThis = "TechDraw_Balloon"; + sStatusTip = sToolTipText; + sPixmap = "TechDraw_Balloon"; +} + +void CmdTechDrawNewBalloon::activated(int iMsg) +{ + Q_UNUSED(iMsg); + bool result = _checkSelectionBalloon(this,1); + if (!result) + return; + result = _checkDrawViewPartBalloon(this); + if (!result) + return; + + std::string FeatName = getUniqueObjectName("Balloon"); + + std::vector selection = getSelection().getSelectionEx(); + auto objFeat( dynamic_cast(selection[0].getObject()) ); + if( objFeat == nullptr ) { + return; + } + TechDraw::DrawPage* page = objFeat->findParentPage(); + std::string PageName = page->getNameInDocument(); + TechDraw::DrawViewBalloon *balloon = 0; + + openCommand("Create Balloon"); + doCommand(Doc,"App.activeDocument().addObject('TechDraw::DrawViewBalloon','%s')",FeatName.c_str()); + doCommand(Doc,"App.activeDocument().%s.addView(App.activeDocument().%s)",PageName.c_str(),FeatName.c_str()); + + balloon = dynamic_cast(getDocument()->getObject(FeatName.c_str())); + if (!balloon) { + throw Base::TypeError("CmdTechDrawNewBalloon - balloon not found\n"); + } + + balloon->sourceView.setValue(objFeat); + + commitCommand(); + balloon->recomputeFeature(); + + //Horrible hack to force Tree update + double x = objFeat->X.getValue(); + objFeat->X.setValue(x); + +} + +bool CmdTechDrawNewBalloon::isActive(void) +{ + bool havePage = DrawGuiUtil::needPage(this); + bool haveView = DrawGuiUtil::needView(this); + return (havePage && haveView); +} //=========================================================================== // TechDraw_Clip @@ -1228,4 +1332,5 @@ void CreateTechDrawCommands(void) rcCmdMgr.addCommand(new CmdTechDrawDraftView()); rcCmdMgr.addCommand(new CmdTechDrawArchView()); rcCmdMgr.addCommand(new CmdTechDrawSpreadsheet()); + rcCmdMgr.addCommand(new CmdTechDrawNewBalloon()); } diff --git a/src/Mod/TechDraw/Gui/MDIViewPage.cpp b/src/Mod/TechDraw/Gui/MDIViewPage.cpp index 0710e3bcf6..5c5769efd6 100644 --- a/src/Mod/TechDraw/Gui/MDIViewPage.cpp +++ b/src/Mod/TechDraw/Gui/MDIViewPage.cpp @@ -75,6 +75,7 @@ #include #include #include +#include #include #include #include @@ -86,6 +87,7 @@ #include "QGIView.h" #include "QGIViewPart.h" #include "QGIViewDimension.h" +#include "QGIViewBalloon.h" #include "QGIViewClip.h" #include "QGIVertex.h" #include "QGIEdge.h" @@ -161,6 +163,7 @@ MDIViewPage::MDIViewPage(ViewProviderPage *pageVp, Gui::Document* doc, QWidget* //when restoring, it is possible for a Dimension to be loaded before the ViewPart it applies to //therefore we need to make sure parentage of the graphics representation is set properly. bit of a kludge. setDimensionGroups(); + setBalloonGroups(); App::DocumentObject *obj = m_vpPage->getDrawPage()->Template.getValue(); auto pageTemplate( dynamic_cast(obj) ); @@ -205,6 +208,23 @@ void MDIViewPage::setDimensionGroups(void) } } +void MDIViewPage::setBalloonGroups(void) +{ + const std::vector &allItems = m_view->getViews(); + std::vector::const_iterator itInspect; + int balloonItemType = QGraphicsItem::UserType + 140; + + for (itInspect = allItems.begin(); itInspect != allItems.end(); itInspect++) { + if (((*itInspect)->type() == balloonItemType) && (!(*itInspect)->group())) { + QGIView* parent = m_view->findParent((*itInspect)); + if (parent) { + QGIViewBalloon* balloon = dynamic_cast((*itInspect)); + m_view->addBalloonToParent(balloon,parent); + } + } + } +} + void MDIViewPage::setDocumentObject(const std::string& name) { m_objectName = name; @@ -294,6 +314,9 @@ bool MDIViewPage::attachView(App::DocumentObject *obj) } else if (typeId.isDerivedFrom(TechDraw::DrawViewDimension::getClassTypeId()) ) { qview = m_view->addViewDimension( static_cast(obj) ); + } else if (typeId.isDerivedFrom(TechDraw::DrawViewBalloon::getClassTypeId()) ) { + qview = m_view->addViewBalloon( static_cast(obj) ); + } else if (typeId.isDerivedFrom(TechDraw::DrawViewAnnotation::getClassTypeId()) ) { qview = m_view->addDrawViewAnnotation( static_cast(obj) ); diff --git a/src/Mod/TechDraw/Gui/MDIViewPage.h b/src/Mod/TechDraw/Gui/MDIViewPage.h index a5544236b3..dbfd6cab98 100644 --- a/src/Mod/TechDraw/Gui/MDIViewPage.h +++ b/src/Mod/TechDraw/Gui/MDIViewPage.h @@ -126,6 +126,7 @@ protected: void closeEvent(QCloseEvent*); QPrinter::PaperSize getPaperSize(int w, int h) const; void setDimensionGroups(void); + void setBalloonGroups(void); void showStatusMsg(const char* s1, const char* s2, const char* s3) const; void onDeleteObject(const App::DocumentObject& obj); diff --git a/src/Mod/TechDraw/Gui/QGIUserTypes.h b/src/Mod/TechDraw/Gui/QGIUserTypes.h index d8a9798bd4..a3eca433ba 100644 --- a/src/Mod/TechDraw/Gui/QGIUserTypes.h +++ b/src/Mod/TechDraw/Gui/QGIUserTypes.h @@ -9,6 +9,7 @@ QGraphicsItemEdge: 103 QGraphicsItemFace: 104 QGraphicsItemVertex: 105 QGraphicsItemViewDimension : 106 +QGraphicsItemViewBalloon : 140 QGraphicsItemDatumLabel : 107 QGraphicsItemViewSection : 108 QGraphicsItemArrow: 109 diff --git a/src/Mod/TechDraw/Gui/QGIView.cpp b/src/Mod/TechDraw/Gui/QGIView.cpp index ddae543deb..1ea077cd92 100644 --- a/src/Mod/TechDraw/Gui/QGIView.cpp +++ b/src/Mod/TechDraw/Gui/QGIView.cpp @@ -110,6 +110,10 @@ QGIView::QGIView() isVisible(true); } +QGIView::~QGIView() +{ + signalSelectPoint.disconnect_all_slots(); +} void QGIView::alignTo(QGraphicsItem*item, const QString &alignment) { @@ -179,6 +183,8 @@ QVariant QGIView::itemChange(GraphicsItemChange change, const QVariant &value) void QGIView::mousePressEvent(QGraphicsSceneMouseEvent * event) { + signalSelectPoint(this, event->pos()); + if(m_locked) { event->ignore(); } else { diff --git a/src/Mod/TechDraw/Gui/QGIView.h b/src/Mod/TechDraw/Gui/QGIView.h index 6ea64cf08f..a8ae0a44b4 100644 --- a/src/Mod/TechDraw/Gui/QGIView.h +++ b/src/Mod/TechDraw/Gui/QGIView.h @@ -52,7 +52,7 @@ class TechDrawGuiExport QGIView : public QGraphicsItemGroup { public: QGIView(); - virtual ~QGIView() = default; + virtual ~QGIView(); enum {Type = QGraphicsItem::UserType + 101}; int type() const override { return Type;} @@ -102,6 +102,7 @@ public: MDIViewPage* getMDIViewPage(void) const; // Mouse handling virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override; + boost::signals2::signal signalSelectPoint; protected: QGIView* getQGIVByName(std::string name); diff --git a/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp b/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp new file mode 100644 index 0000000000..aaf4c88199 --- /dev/null +++ b/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp @@ -0,0 +1,571 @@ +/*************************************************************************** + * Copyright (c) 2013 Luke Parry * + * Copyright (c) 2019 Franck Jullien * + * * + * 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 + # include + # include + + # include + # include + # include + # include + # include + + # include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "Rez.h" +#include "ZVALUE.h" +#include "QGIArrow.h" +#include "QGIDimLines.h" +#include "QGIViewBalloon.h" +#include "ViewProviderBalloon.h" +#include "DrawGuiUtil.h" +#include "QGIViewPart.h" +#include "QGIViewDimension.h" +#include "QGVPage.h" +#include "MDIViewPage.h" + +#define PI 3.14159 + +//TODO: hide the Qt coord system (+y down). + +using namespace TechDraw; +using namespace TechDrawGui; + +//************************************************************** +QGIViewBalloon::QGIViewBalloon() : + hasHover(false), + m_lineWidth(0.0) +{ + setHandlesChildEvents(false); + setFlag(QGraphicsItem::ItemIsMovable, false); + setCacheMode(QGraphicsItem::NoCache); + + balloonLabel = new QGIDatumLabel(); + addToGroup(balloonLabel); + balloonLabel->setColor(getNormalColor()); + balloonLabel->setPrettyNormal(); + + balloonLines = new QGIDimLines(); + addToGroup(balloonLines); + + balloonShape = new QGIDimLines(); + addToGroup(balloonShape); + + arrow = new QGIArrow(); + addToGroup(arrow); + + balloonLabel->setZValue(ZVALUE::LABEL); + arrow->setZValue(ZVALUE::DIMENSION); + balloonLines->setZValue(ZVALUE::DIMENSION); + balloonLines->setStyle(Qt::SolidLine); + + balloonShape->setZValue(ZVALUE::DIMENSION); + balloonShape->setStyle(Qt::SolidLine); + + oldLabelCenter = new QPointF; + oldLabelCenter->setX(0.0); + oldLabelCenter->setY(0.0); + + balloonLabel->setPosFromCenter(0, 0); + + // connecting the needed slots and signals + QObject::connect( + balloonLabel, SIGNAL(dragging(bool)), + this , SLOT (balloonLabelDragged(bool))); + + QObject::connect( + balloonLabel, SIGNAL(dragFinished()), + this , SLOT (balloonLabelDragFinished())); + + QObject::connect( + balloonLabel, SIGNAL(selected(bool)), + this , SLOT (select(bool))); + + QObject::connect( + balloonLabel, SIGNAL(hover(bool)), + this , SLOT (hover(bool))); + + toggleBorder(false); + setZValue(ZVALUE::DIMENSION); //note: this won't paint dimensions over another View if it stacks + //above this Dimension's parent view. need Layers? + +} + +void QGIViewBalloon::disconnect(void) +{ + auto bnd = boost::bind(&QGIViewBalloon::parentViewMousePressed, this, _1, _2); + if (m_parent) { + m_parent->signalSelectPoint.disconnect(bnd); + } +} + +void QGIViewBalloon::connect(QGIView *parent) +{ + auto bnd = boost::bind(&QGIViewBalloon::parentViewMousePressed, this, _1, _2); + parent->signalSelectPoint.connect(bnd); + m_parent = parent; +} + +void QGIViewBalloon::parentViewMousePressed(QGIView *view, QPointF pos) +{ + Q_UNUSED(view); + //Base::Console().Message("%s::parentViewMousePressed from %s\n", this->getViewName(), view->getViewName()); + + auto vp = static_cast(getViewProvider(getViewObject())); + if ( vp == nullptr ) { + return; + } + + auto balloon( dynamic_cast(getViewObject()) ); + if( balloon == nullptr ) + return; + + auto bnd = boost::bind(&QGIViewBalloon::parentViewMousePressed, this, _1, _2); + + if (balloon->OriginIsSet.getValue() == false) { + balloon->OriginX.setValue(pos.x()); + balloon->OriginY.setValue(pos.y()); + balloon->OriginIsSet.setValue(true); + + m_parent->signalSelectPoint.disconnect(bnd); + + MDIViewPage* mdi = getMDIViewPage(); + QGVPage* page; + if (mdi != nullptr) { + page = mdi->getQGVPage(); + } + + QString labelText = QString::fromUtf8(std::to_string(page->balloonIndex).c_str()); + balloon->Text.setValue(std::to_string(page->balloonIndex++).c_str()); + + QFont font = balloonLabel->getFont(); + font.setPointSizeF(Rez::guiX(vp->Fontsize.getValue())); + font.setFamily(QString::fromUtf8(vp->Font.getValue())); + balloonLabel->setFont(font); + prepareGeometryChange(); + + // Default label position + balloonLabel->setPosFromCenter(pos.x() + 200, pos.y() -200); + balloonLabel->setDimString(labelText, Rez::guiX(balloon->TextWrapLen.getValue())); + } + + draw(); +} + + +void QGIViewBalloon::setViewPartFeature(TechDraw::DrawViewBalloon *balloon) +{ + if(balloon == 0) + return; + + setViewFeature(static_cast(balloon)); + + float x = Rez::guiX(balloon->X.getValue()); + float y = Rez::guiX(-balloon->Y.getValue()); + + balloonLabel->setColor(getNormalColor()); + balloonLabel->setPosFromCenter(x, y); + + QString labelText = QString::fromUtf8(balloon->Text.getStrValue().data()); + balloonLabel->setDimString(labelText, Rez::guiX(balloon->TextWrapLen.getValue())); + + updateBalloon(); + + draw(); +} + +void QGIViewBalloon::select(bool state) +{ + setSelected(state); + draw(); +} + +void QGIViewBalloon::hover(bool state) +{ + hasHover = state; + draw(); +} + +void QGIViewBalloon::updateView(bool update) +{ + Q_UNUSED(update); + auto balloon( dynamic_cast(getViewObject()) ); + if( balloon == nullptr ) + return; + + auto vp = static_cast(getViewProvider(getViewObject())); + if ( vp == nullptr ) { + return; + } + + if (update) { + QString labelText = QString::fromUtf8(balloon->Text.getStrValue().data()); + balloonLabel->setDimString(labelText, Rez::guiX(balloon->TextWrapLen.getValue())); + balloonLabel->setColor(getNormalColor()); + } + + updateBalloon(); + draw(); +} + +void QGIViewBalloon::updateBalloon(bool obtuse) +{ + (void) obtuse; + const auto balloon( dynamic_cast(getViewObject()) ); + if( balloon == nullptr ) { + return; + } + auto vp = static_cast(getViewProvider(getViewObject())); + if ( vp == nullptr ) { + return; + } + + if (balloon->OriginIsSet.getValue() == false) + return; + + QFont font = balloonLabel->getFont(); + font.setPointSizeF(Rez::guiX(vp->Fontsize.getValue())); + font.setFamily(QString::fromUtf8(vp->Font.getValue())); + balloonLabel->setFont(font); +} + +void QGIViewBalloon::balloonLabelDragged(bool ctrl) +{ + draw_modifier(ctrl); +} + +void QGIViewBalloon::balloonLabelDragFinished() +{ + auto dim( dynamic_cast(getViewObject()) ); + + if( dim == nullptr ) { + return; + } + + double x = Rez::appX(balloonLabel->X()), + y = Rez::appX(balloonLabel->Y()); + Gui::Command::openCommand("Drag Balloon"); + 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); + Gui::Command::commitCommand(); +} + +void QGIViewBalloon::draw() +{ + draw_modifier(false); +} + +void QGIViewBalloon::draw_modifier(bool modifier) +{ + if (!isVisible()) { //should this be controlled by parent ViewPart? + return; + } + + TechDraw::DrawViewBalloon *balloon = dynamic_cast(getViewObject()); + if((!balloon) || //nothing to draw, don't try + (!balloon->isDerivedFrom(TechDraw::DrawViewBalloon::getClassTypeId()))) { + balloonLabel->hide(); + hide(); + return; + } + + if (balloon->OriginIsSet.getValue() == false) { + return; + } + + balloonLabel->show(); + show(); + + const TechDraw::DrawViewPart *refObj = balloon->getViewPart(); + if(!refObj->hasGeometry()) { //nothing to draw yet (restoring) + balloonLabel->hide(); + hide(); + return; + } + + auto vp = static_cast(getViewProvider(getViewObject())); + if ( vp == nullptr ) { + return; + } + + m_colNormal = getNormalColor(); + balloonLabel->setColor(m_colNormal); + + m_lineWidth = Rez::guiX(vp->LineWidth.getValue()); + + double textWidth = balloonLabel->getDimText()->boundingRect().width(); + double textHeight = balloonLabel->getDimText()->boundingRect().height(); + QRectF mappedRect = mapRectFromItem(balloonLabel, balloonLabel->boundingRect()); + Base::Vector3d lblCenter = Base::Vector3d(mappedRect.center().x(), mappedRect.center().y(), 0.0); + + if (balloon->isLocked()) { + lblCenter.x = (oldLabelCenter->x()); + lblCenter.y = (oldLabelCenter->y()); + balloonLabel->setFlag(QGraphicsItem::ItemIsMovable, false); + } else + balloonLabel->setFlag(QGraphicsItem::ItemIsMovable, true); + + Base::Vector3d dLineStart; + Base::Vector3d kinkPoint; + double kinkLength = Rez::guiX(5.0); + + float orginX = balloon->OriginX.getValue(); + float orginY = balloon->OriginY.getValue(); + + const char *balloonType = balloon->Symbol.getValueAsString(); + + float scale = balloon->SymbolScale.getValue(); + double offset; + QPainterPath balloonPath; + + if (strcmp(balloonType, "Circular") == 0) { + double balloonRadius = sqrt(pow((textHeight / 2.0), 2) + pow((textWidth / 2.0), 2)); + balloonRadius = balloonRadius * scale; + balloonPath.moveTo(lblCenter.x, lblCenter.y); + balloonPath.addEllipse(lblCenter.x - balloonRadius,lblCenter.y - balloonRadius, balloonRadius * 2, balloonRadius * 2); + offset = balloonRadius; + } else if (strcmp(balloonType, "None") == 0) { + balloonPath = QPainterPath(); + offset = (textWidth / 2.0) + Rez::guiX(2.0); + } else if (strcmp(balloonType, "Rectangle") == 0) { + //Add some room + textWidth = (textWidth * scale) + Rez::guiX(2.0); + textHeight = (textHeight * scale) + Rez::guiX(1.0); + balloonPath.addRect(lblCenter.x -(textWidth / 2.0), lblCenter.y - (textHeight / 2.0), textWidth, textHeight); + offset = (textWidth / 2.0); + } else if (strcmp(balloonType, "Triangle") == 0) { + double radius = sqrt(pow((textHeight / 2.0), 2) + pow((textWidth / 2.0), 2)); + radius = radius * scale; + radius += Rez::guiX(3.0); + offset = (tan(30 * PI / 180) * radius); + QPolygonF triangle; + double startAngle = -PI / 2; + double angle = startAngle; + for (int i = 0; i < 4; i++) { + triangle += QPointF(lblCenter.x + (radius * cos(angle)), lblCenter.y + (radius * sin(angle))); + angle += (2 * PI / 3); + } + balloonPath.moveTo(lblCenter.x + (radius * cos(startAngle)), lblCenter.y + (radius * sin(startAngle))); + balloonPath.addPolygon(triangle); + } else if (strcmp(balloonType, "Inspection") == 0) { + //Add some room + textWidth = (textWidth * scale) + Rez::guiX(2.0); + textHeight = (textHeight * scale) + Rez::guiX(1.0); + QPointF textBoxCorner(lblCenter.x - (textWidth / 2.0), lblCenter.y - (textHeight / 2.0)); + balloonPath.moveTo(textBoxCorner); + balloonPath.lineTo(textBoxCorner.x() + textWidth, textBoxCorner.y()); + balloonPath.arcTo(textBoxCorner.x() + textWidth - (textHeight / 2.0), textBoxCorner.y(), textHeight, textHeight, 90, -180); + balloonPath.lineTo(textBoxCorner.x(), textBoxCorner.y() + textHeight); + balloonPath.arcTo(textBoxCorner.x() - (textHeight / 2), textBoxCorner.y(), textHeight, textHeight, -90, -180); + offset = (textWidth / 2.0) + (textHeight / 2.0); + } else if (strcmp(balloonType, "Square") == 0) { + //Add some room + textWidth = (textWidth * scale) + Rez::guiX(2.0); + textHeight = (textHeight * scale) + Rez::guiX(1.0); + double max = std::max(textWidth, textHeight); + balloonPath.addRect(lblCenter.x -(max / 2.0), lblCenter.y - (max / 2.0), max, max); + offset = (max / 2.0); + } else if (strcmp(balloonType, "Hexagon") == 0) { + double radius = sqrt(pow((textHeight / 2.0), 2) + pow((textWidth / 2.0), 2)); + radius = radius * scale; + radius += Rez::guiX(1.0); + offset = radius; + QPolygonF triangle; + double startAngle = -2 * PI / 3; + double angle = startAngle; + for (int i = 0; i < 7; i++) { + triangle += QPointF(lblCenter.x + (radius * cos(angle)), lblCenter.y + (radius * sin(angle))); + angle += (2 * PI / 6); + } + balloonPath.moveTo(lblCenter.x + (radius * cos(startAngle)), lblCenter.y + (radius * sin(startAngle))); + balloonPath.addPolygon(triangle); + } + + offset = (lblCenter.x < orginX) ? offset : -offset; + dLineStart.y = lblCenter.y; + dLineStart.x = lblCenter.x + offset; + kinkLength = (lblCenter.x < orginX) ? kinkLength : -kinkLength; + kinkPoint.y = dLineStart.y; + kinkPoint.x = dLineStart.x + kinkLength; + + QPainterPath dLinePath; + dLinePath.moveTo(dLineStart.x, dLineStart.y); + dLinePath.lineTo(kinkPoint.x, kinkPoint.y); + + if (modifier) { + balloon->OriginX.setValue(orginX + lblCenter.x - oldLabelCenter->x()); + balloon->OriginY.setValue(orginY + lblCenter.y - oldLabelCenter->y()); + } + + orginX = balloon->OriginX.getValue(); + orginY = balloon->OriginY.getValue(); + + dLinePath.lineTo(orginX, orginY); + + oldLabelCenter->setX(lblCenter.x); + oldLabelCenter->setY(lblCenter.y); + + balloonLines->setPath(dLinePath); + balloonShape->setPath(balloonPath); + + const char *endType = balloon->EndType.getValueAsString(); + + if (strcmp(endType, "Arrow") == 0) { + arrow->setStyle(QGIArrow::getPrefArrowStyle()); + } else if (strcmp(endType, "Dot") == 0) { + arrow->setStyle(3); + } + + arrow->setSize(QGIArrow::getPrefArrowSize()); + arrow->draw(); + + Base::Vector3d orign(orginX, orginY, 0.0); + Base::Vector3d dirballoonLinesLine = (orign - kinkPoint).Normalize(); + float arAngle = atan2(dirballoonLinesLine.y, dirballoonLinesLine.x) * 180 / M_PI; + + arrow->setPos(orginX, orginY); + arrow->setRotation(arAngle); + arrow->show(); + + // redraw the Dimension and the parent View + if (hasHover && !isSelected()) { + arrow->setPrettyPre(); + balloonLines->setPrettyPre(); + balloonShape->setPrettyPre(); + } else if (isSelected()) { + arrow->setPrettySel(); + balloonLines->setPrettySel(); + balloonShape->setPrettySel(); + } else { + arrow->setPrettyNormal(); + balloonLines->setPrettyNormal(); + balloonShape->setPrettyNormal(); + } + + update(); + if (parentItem()) { + //TODO: parent redraw still required with new frame/label?? + parentItem()->update(); + } else { + Base::Console().Log("INFO - QGIVD::draw - no parent to update\n"); + } + +} + +void QGIViewBalloon::drawBorder(void) +{ +//Dimensions have no border! +// Base::Console().Message("TRACE - QGIViewDimension::drawBorder - doing nothing!\n"); +} + +QVariant QGIViewBalloon::itemChange(GraphicsItemChange change, const QVariant &value) +{ + if (change == ItemSelectedHasChanged && scene()) { + if(isSelected()) { + balloonLabel->setSelected(true); + } else { + balloonLabel->setSelected(false); + } + draw(); + } + return QGIView::itemChange(change, value); +} + +void QGIViewBalloon::paint ( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget) { + QStyleOptionGraphicsItem myOption(*option); + myOption.state &= ~QStyle::State_Selected; + + QPaintDevice* hw = painter->device(); + QSvgGenerator* svg = dynamic_cast(hw); + setPens(); + //double balloonLinesSaveWidth = arrow->getWidth(); + if (svg) { + setSvgPens(); + } else { + setPens(); + } + QGIView::paint (painter, &myOption, widget); + setPens(); +} + +void QGIViewBalloon::setSvgPens(void) +{ + double svgLineFactor = 3.0; //magic number. should be a setting somewhere. + balloonLines->setWidth(m_lineWidth/svgLineFactor); + balloonShape->setWidth(m_lineWidth/svgLineFactor); + arrow->setWidth(arrow->getWidth()/svgLineFactor); +} + +void QGIViewBalloon::setPens(void) +{ + balloonLines->setWidth(m_lineWidth); + balloonShape->setWidth(m_lineWidth); + arrow->setWidth(m_lineWidth); +} + +QColor QGIViewBalloon::getNormalColor() +{ + Base::Reference hGrp = App::GetApplication().GetUserParameter() + .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/Dimensions"); + App::Color fcColor; + fcColor.setPackedValue(hGrp->GetUnsigned("Color", 0x00000000)); + m_colNormal = fcColor.asValue(); + + auto balloon( dynamic_cast(getViewObject()) ); + if( balloon == nullptr ) + return m_colNormal; + + auto vp = static_cast(getViewProvider(getViewObject())); + if ( vp == nullptr ) { + return m_colNormal; + } + + m_colNormal = vp->Color.getValue().asValue(); + return m_colNormal; +} + +#include diff --git a/src/Mod/TechDraw/Gui/QGIViewBalloon.h b/src/Mod/TechDraw/Gui/QGIViewBalloon.h new file mode 100644 index 0000000000..28880e2d2c --- /dev/null +++ b/src/Mod/TechDraw/Gui/QGIViewBalloon.h @@ -0,0 +1,112 @@ +/*************************************************************************** + * Copyright (c) 2013 Luke Parry * + * Copyright (c) 2019 Franck Jullien * + * * + * 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 DRAWINGGUI_QGRAPHICSITEMVIEWBALLOON_H +#define DRAWINGGUI_QGRAPHICSITEMVIEWBALLOON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "QGIView.h" +#include "QGIViewPart.h" +#include "QGCustomText.h" +#include "QGIViewDimension.h" + +namespace TechDraw { +class DrawViewBalloon; +} + +namespace TechDrawGeometry { +class BaseGeom; +class AOC; +} + +namespace TechDrawGui +{ +class QGIArrow; +class QGIDimLines; +class QGIViewBalloon; + +//******************************************************************* + +class TechDrawGuiExport QGIViewBalloon : public QObject, public QGIView +{ + Q_OBJECT + +public: + enum {Type = QGraphicsItem::UserType + 140}; + + explicit QGIViewBalloon(); + virtual ~QGIViewBalloon() = default; + + void setViewPartFeature(TechDraw::DrawViewBalloon *obj); + int type() const override { return Type;} + + virtual void drawBorder() override; + virtual void updateView(bool update = false) override; + virtual void paint( QPainter * painter, + const QStyleOptionGraphicsItem * option, + QWidget * widget = 0 ) override; + virtual QColor getNormalColor(void) override; + QString getLabelText(void); + void connect(QGIView *parent); + void disconnect(void); + void draw_modifier(bool modifier); + +public Q_SLOTS: + void balloonLabelDragged(bool ctrl); + void balloonLabelDragFinished(void); + void select(bool state); + void hover(bool state); + void updateBalloon(bool obtuse = false); + +protected: + void draw() override; + virtual QVariant itemChange( GraphicsItemChange change, + const QVariant &value ) override; + virtual void setSvgPens(void); + virtual void setPens(void); + QString getPrecision(void); + +protected: + bool hasHover; + QGIDatumLabel* balloonLabel; + QGIDimLines* balloonLines; + QGIDimLines* balloonShape; + QGIArrow* arrow; + double m_lineWidth; + bool m_obtuse; + void parentViewMousePressed(QGIView *view, QPointF pos); + QPointF *oldLabelCenter; + QGIView *m_parent; + +}; + +} // namespace MDIViewPageGui + +#endif // DRAWINGGUI_QGRAPHICSITEMVIEWBALLOON_H diff --git a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp index 45d6dd41c9..9997be188f 100644 --- a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp @@ -210,6 +210,13 @@ void QGIDatumLabel::setDimString(QString t) m_dimText->setPlainText(t); } +void QGIDatumLabel::setDimString(QString t, qreal maxWidth) +{ + prepareGeometryChange(); + m_dimText->setPlainText(t); + m_dimText->setTextWidth(maxWidth); +} + void QGIDatumLabel::setTolString() { prepareGeometryChange(); diff --git a/src/Mod/TechDraw/Gui/QGIViewDimension.h b/src/Mod/TechDraw/Gui/QGIViewDimension.h index da44783129..14b5c3c611 100644 --- a/src/Mod/TechDraw/Gui/QGIViewDimension.h +++ b/src/Mod/TechDraw/Gui/QGIViewDimension.h @@ -73,6 +73,7 @@ public: void setFont(QFont f); QFont getFont(void) { return m_dimText->font(); } void setDimString(QString t); + void setDimString(QString t, qreal maxWidth); void setTolString(); void setPrettySel(void); void setPrettyPre(void); diff --git a/src/Mod/TechDraw/Gui/QGVPage.cpp b/src/Mod/TechDraw/Gui/QGVPage.cpp index e2a1c27640..8077e60e51 100644 --- a/src/Mod/TechDraw/Gui/QGVPage.cpp +++ b/src/Mod/TechDraw/Gui/QGVPage.cpp @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -75,6 +76,7 @@ #include "TemplateTextField.h" #include "QGIViewCollection.h" #include "QGIViewDimension.h" +#include "QGIViewBalloon.h" #include "QGIProjGroup.h" #include "QGIViewPart.h" #include "QGIViewSection.h" @@ -134,6 +136,8 @@ QGVPage::QGVPage(ViewProviderPage *vp, QGraphicsScene* s, QWidget *parent) bkgBrush = new QBrush(getBackgroundColor()); + balloonIndex = 1; + resetCachedContent(); } @@ -255,7 +259,13 @@ int QGVPage::removeQViewByName(const char* name) break; } } + if (found) { + int balloonItemType = QGraphicsItem::UserType + 140; + if (ourItem->type() == balloonItemType) { + QGIViewBalloon* balloon = dynamic_cast(ourItem); + balloon->disconnect(); + } removeQViewFromScene(ourItem); delete ourItem; } @@ -383,6 +393,39 @@ QGIView * QGVPage::addDrawViewImage(TechDraw::DrawViewImage *view) return qview; } +QGIView * QGVPage::addViewBalloon(TechDraw::DrawViewBalloon *balloon) +{ + auto balloonGroup( new QGIViewBalloon ); + + auto ourScene( scene() ); + assert(ourScene); + ourScene->addItem(balloonGroup); + + balloonGroup->setViewPartFeature(balloon); + + // Find if it belongs to a parent + QGIView *parent = 0; + parent = findParent(balloonGroup); + + if(parent) { + balloonGroup->connect(parent); + addBalloonToParent(balloonGroup,parent); + } + + return balloonGroup; +} + +void QGVPage::addBalloonToParent(QGIViewBalloon* balloon, QGIView* parent) +{ + assert(balloon); + assert(parent); //blow up if we don't have Dimension or Parent + QPointF posRef(0.,0.); + QPointF mapPos = balloon->mapToItem(parent, posRef); + balloon->moveBy(-mapPos.x(), -mapPos.y()); + parent->addToGroup(balloon); + balloon->setZValue(ZVALUE::DIMENSION); +} + QGIView * QGVPage::addViewDimension(TechDraw::DrawViewDimension *dim) { auto dimGroup( new QGIViewDimension ); @@ -469,6 +512,23 @@ QGIView * QGVPage::findParent(QGIView *view) const } } + //If type is balloon we check references first + TechDraw::DrawViewBalloon *balloon = 0; + balloon = dynamic_cast(myView); + + if(balloon) { + App::DocumentObject* obj = balloon->sourceView.getValue(); + + if(obj) { + // Attach the dimension to the first object's group + for(std::vector::const_iterator it = qviews.begin(); it != qviews.end(); ++it) { + if(strcmp((*it)->getViewName(), obj->getNameInDocument()) == 0) { + return *it; + } + } + } + } + // Check if part of view collection for(std::vector::const_iterator it = qviews.begin(); it != qviews.end(); ++it) { QGIViewCollection *grp = 0; diff --git a/src/Mod/TechDraw/Gui/QGVPage.h b/src/Mod/TechDraw/Gui/QGVPage.h index 54307be5f5..0280e434bf 100644 --- a/src/Mod/TechDraw/Gui/QGVPage.h +++ b/src/Mod/TechDraw/Gui/QGVPage.h @@ -41,6 +41,7 @@ class DrawViewClip; class DrawViewCollection; class DrawViewSpreadsheet; class DrawViewImage; +class DrawViewBalloon; } namespace TechDrawGui @@ -49,6 +50,7 @@ class QGIView; class QGIViewDimension; class QGITemplate; class ViewProviderPage; +class QGIViewBalloon; class TechDrawGuiExport QGVPage : public QGraphicsView { @@ -64,6 +66,7 @@ public: void drawBackground(QPainter *p, const QRectF &rect) override; QGIView * addViewDimension(TechDraw::DrawViewDimension *dim); + QGIView * addViewBalloon(TechDraw::DrawViewBalloon *balloon); QGIView * addProjectionGroup(TechDraw::DrawProjGroup *view); QGIView * addViewPart(TechDraw::DrawViewPart *part); QGIView * addViewSection(TechDraw::DrawViewPart *part); @@ -80,6 +83,7 @@ public: QGIView* getQGIVByName(std::string name); QGIView* findParent(QGIView *) const; + void addBalloonToParent(QGIViewBalloon* balloon, QGIView* parent); void addDimToParent(QGIViewDimension* dim, QGIView* parent); // const std::vector & getViews() const { return views; } //only used in MDIVP std::vector getViews() const; //only used in MDIVP @@ -104,6 +108,8 @@ public: void saveSvg(QString filename); void postProcessXml(QTemporaryFile* tempFile, QString filename, QString pagename); + int balloonIndex; + public Q_SLOTS: void setHighQualityAntialiasing(bool highQualityAntialiasing); diff --git a/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc b/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc index fd66ad7988..0334b719e5 100644 --- a/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc +++ b/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc @@ -30,6 +30,7 @@ icons/TechDraw_Dimension_Horizontal.svg icons/TechDraw_Dimension_Length.svg icons/TechDraw_Dimension_Radius.svg + icons/TechDraw_Balloon.svg icons/TechDraw_Dimension_Vertical.svg icons/TechDraw_Dimension_Link.svg icons/preferences-techdraw.svg diff --git a/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Balloon.svg b/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Balloon.svg new file mode 100644 index 0000000000..7caaa4af9a --- /dev/null +++ b/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Balloon.svg @@ -0,0 +1,359 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + [WandererFan] + + + TechDraw_Dimension_Diameter + 2016-04-27 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Dimension_Diameter.svg + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + [agryson] Alexander Gryson + + + + + double arrow + circle + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/TechDraw/Gui/ViewProviderBalloon.cpp b/src/Mod/TechDraw/Gui/ViewProviderBalloon.cpp new file mode 100644 index 0000000000..cdd2138857 --- /dev/null +++ b/src/Mod/TechDraw/Gui/ViewProviderBalloon.cpp @@ -0,0 +1,128 @@ +/*************************************************************************** + * Copyright (c) 2004 Jürgen Riegel * + * Copyright (c) 2012 Luke Parry * + * Copyright (c) 2019 Franck Jullien * + * * + * 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_ +#endif + +/// Here the FreeCAD includes sorted by Base,App,Gui...... +#include +#include +#include +#include +#include +#include +#include + +#include + + +#include "ViewProviderBalloon.h" + +using namespace TechDrawGui; + +PROPERTY_SOURCE(TechDrawGui::ViewProviderBalloon, TechDrawGui::ViewProviderDrawingView) + +//************************************************************************** +// Construction/Destruction + +ViewProviderBalloon::ViewProviderBalloon() +{ + sPixmap = "TechDraw_Balloon"; + + static const char *group = "Balloon Format"; + + Base::Reference hGrp = App::GetApplication().GetUserParameter() + .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/Labels"); + std::string fontName = hGrp->GetASCII("LabelFont", "osifont"); + + hGrp = App::GetApplication().GetUserParameter() + .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/Dimensions"); + double fontSize = hGrp->GetFloat("FontSize", 3.5); + ADD_PROPERTY_TYPE(Font ,(fontName.c_str()),group,App::Prop_None, "The name of the font to use"); + ADD_PROPERTY_TYPE(Fontsize,(fontSize) ,group,(App::PropertyType)(App::Prop_None),"Dimension text size in units"); + + + hGrp = App::GetApplication().GetUserParameter().GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/Decorations"); + std::string lgName = hGrp->GetASCII("LineGroup","FC 0.70mm"); + auto lg = TechDraw::LineGroup::lineGroupFactory(lgName); + double weight = lg->getWeight("Thin"); + delete lg; //Coverity CID 174670 + ADD_PROPERTY_TYPE(LineWidth,(weight) ,group,(App::PropertyType)(App::Prop_None),"Dimension line weight"); + + + hGrp = App::GetApplication().GetUserParameter() + .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/Dimensions"); + App::Color fcColor; + fcColor.setPackedValue(hGrp->GetUnsigned("Color", 0x00000000)); + ADD_PROPERTY_TYPE(Color,(fcColor),group,App::Prop_None,"The color of the Dimension"); +} + +ViewProviderBalloon::~ViewProviderBalloon() +{ +} + +void ViewProviderBalloon::attach(App::DocumentObject *pcFeat) +{ + // call parent attach method + ViewProviderDrawingView::attach(pcFeat); +} + +void ViewProviderBalloon::setDisplayMode(const char* ModeName) +{ + ViewProviderDrawingView::setDisplayMode(ModeName); +} + +std::vector ViewProviderBalloon::getDisplayModes(void) const +{ + // get the modes of the father + std::vector StrList = ViewProviderDrawingView::getDisplayModes(); + + return StrList; +} + +void ViewProviderBalloon::updateData(const App::Property* p) +{ + ViewProviderDrawingView::updateData(p); +} + +void ViewProviderBalloon::onChanged(const App::Property* p) +{ + if ((p == &Font) || + (p == &Fontsize) || + (p == &LineWidth)) { + QGIView* qgiv = getQView(); + if (qgiv) { + qgiv->updateView(true); + } + } + Gui::ViewProviderDocumentObject::onChanged(p); +} + +TechDraw::DrawViewBalloon* ViewProviderBalloon::getViewObject() const +{ + return dynamic_cast(pcObject); +} diff --git a/src/Mod/TechDraw/Gui/ViewProviderBalloon.h b/src/Mod/TechDraw/Gui/ViewProviderBalloon.h new file mode 100644 index 0000000000..3549cf49cc --- /dev/null +++ b/src/Mod/TechDraw/Gui/ViewProviderBalloon.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (c) 2004 Jürgen Riegel * + * Copyright (c) 2012 Luke Parry * + * Copyright (c) 2019 Franck Jullien * + * * + * 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 DRAWINGGUI_VIEWPROVIDERBALLOON_H +#define DRAWINGGUI_VIEWPROVIDERBALLOON_H + +#include + +#include "ViewProviderDrawingView.h" +#include + + +namespace TechDrawGui { + + +class TechDrawGuiExport ViewProviderBalloon : public ViewProviderDrawingView +{ + PROPERTY_HEADER(TechDrawGui::ViewProviderBalloon); + +public: + /// constructor + ViewProviderBalloon(); + /// destructor + virtual ~ViewProviderBalloon(); + + App::PropertyFont Font; + App::PropertyLength Fontsize; + App::PropertyFloat LineWidth; + App::PropertyColor Color; + + + virtual void attach(App::DocumentObject *); + virtual void setDisplayMode(const char* ModeName); + virtual bool useNewSelectionModel(void) const {return false;} + /// returns a list of all possible modes + virtual std::vector getDisplayModes(void) const; + virtual void updateData(const App::Property*); + virtual void onChanged(const App::Property* p); + + virtual TechDraw::DrawViewBalloon* getViewObject() const; +}; + +} // namespace TechDrawGui + + +#endif // DRAWINGGUI_VIEWPROVIDERBALLOON_H diff --git a/src/Mod/TechDraw/Gui/ViewProviderPage.cpp b/src/Mod/TechDraw/Gui/ViewProviderPage.cpp index b03a2159f6..a4089706d2 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderPage.cpp +++ b/src/Mod/TechDraw/Gui/ViewProviderPage.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -259,6 +260,7 @@ std::vector ViewProviderPage::claimChildren(void) const // Collect any child views // for Page, valid children are any View except: DrawProjGroupItem // DrawViewDimension + // DrawViewBalloon // any FeatuerView in a DrawViewClip // DrawHatch @@ -272,6 +274,7 @@ std::vector ViewProviderPage::claimChildren(void) const if(docObj->isDerivedFrom(TechDraw::DrawProjGroupItem::getClassTypeId()) || docObj->isDerivedFrom(TechDraw::DrawViewDimension::getClassTypeId()) || docObj->isDerivedFrom(TechDraw::DrawHatch::getClassTypeId()) || + docObj->isDerivedFrom(TechDraw::DrawViewBalloon::getClassTypeId()) || (featView && featView->isInClip()) ) continue; else diff --git a/src/Mod/TechDraw/Gui/ViewProviderViewPart.cpp b/src/Mod/TechDraw/Gui/ViewProviderViewPart.cpp index fe3948f649..bd8024c2ee 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderViewPart.cpp +++ b/src/Mod/TechDraw/Gui/ViewProviderViewPart.cpp @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -178,6 +179,8 @@ std::vector ViewProviderViewPart::claimChildren(void) cons temp.push_back((*it)); } else if ((*it)->getTypeId().isDerivedFrom(TechDraw::DrawGeomHatch::getClassTypeId())) { temp.push_back((*it)); + } else if ((*it)->getTypeId().isDerivedFrom(TechDraw::DrawViewBalloon::getClassTypeId())) { + temp.push_back((*it)); } } return temp; diff --git a/src/Mod/TechDraw/Gui/Workbench.cpp b/src/Mod/TechDraw/Gui/Workbench.cpp index f089cdede9..e004df4afb 100644 --- a/src/Mod/TechDraw/Gui/Workbench.cpp +++ b/src/Mod/TechDraw/Gui/Workbench.cpp @@ -78,6 +78,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const *draw << "TechDraw_NewAngleDimension"; *draw << "TechDraw_NewAngle3PtDimension"; *draw << "TechDraw_LinkDimension"; + *draw << "TechDraw_NewBalloon"; *draw << "Separator"; *draw << "TechDraw_ExportPage"; *draw << "TechDraw_ExportPageDxf"; @@ -128,6 +129,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const *dims << "TechDraw_NewAngleDimension"; *dims << "TechDraw_NewAngle3PtDimension"; *dims << "TechDraw_LinkDimension"; + *dims << "TechDraw_NewBalloon"; // *dims << "TechDraw_NewDimension" Gui::ToolBarItem *file = new Gui::ToolBarItem(root); @@ -181,6 +183,7 @@ Gui::ToolBarItem* Workbench::setupCommandBars() const *dims << "TechDraw_NewAngleDimension"; *dims << "TechDraw_NewAngle3PtDimension"; *dims << "TechDraw_LinkDimension"; + *dims << "TechDraw_NewBalloon"; // *dims << "TechDraw_NewDimension"; Gui::ToolBarItem *file = new Gui::ToolBarItem(root); diff --git a/src/Mod/TechDraw/Gui/ZVALUE.h b/src/Mod/TechDraw/Gui/ZVALUE.h index ef968c61b8..34f420bef3 100644 --- a/src/Mod/TechDraw/Gui/ZVALUE.h +++ b/src/Mod/TechDraw/Gui/ZVALUE.h @@ -13,6 +13,7 @@ namespace ZVALUE { const int EDGE = 50; const int VERTEX = 60; const int DIMENSION = 110; + const int LABEL = 120; const int SECTIONLINE = 80; //TODO: change to "DECORATION"? section lines, symmetry lines, etc? const int HIGHLIGHT = 80; const int MATTING = 100; diff --git a/src/Mod/TechDraw/TDTest/DVBalloonTest.py b/src/Mod/TechDraw/TDTest/DVBalloonTest.py new file mode 100644 index 0000000000..f6e3ea8c11 --- /dev/null +++ b/src/Mod/TechDraw/TDTest/DVBalloonTest.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# test script for TechDraw module +# creates a page and 2 views +# adds 1 length dimension to view1 +# adds 1 radius dimension to view2 +from __future__ import print_function + +import FreeCAD +import Part +import Measure +import TechDraw +import os + +def DVBalloonTest(): + path = os.path.dirname(os.path.abspath(__file__)) + print ('TDBalloon path: ' + path) + templateFileSpec = path + '/TestTemplate.svg' + + FreeCAD.newDocument("TDBalloon") + FreeCAD.setActiveDocument("TDBalloon") + FreeCAD.ActiveDocument=FreeCAD.getDocument("TDBalloon") + + #make source feature + box = FreeCAD.ActiveDocument.addObject("Part::Box","Box") + sphere = FreeCAD.ActiveDocument.addObject("Part::Sphere","Sphere") + + #make a page + page = FreeCAD.ActiveDocument.addObject('TechDraw::DrawPage','Page') + FreeCAD.ActiveDocument.addObject('TechDraw::DrawSVGTemplate','Template') + FreeCAD.ActiveDocument.Template.Template = templateFileSpec + FreeCAD.ActiveDocument.Page.Template = FreeCAD.ActiveDocument.Template + page.Scale = 5.0 +# page.ViewObject.show() # unit tests run in console mode + + #make Views + view1 = FreeCAD.ActiveDocument.addObject('TechDraw::DrawViewPart','View') + FreeCAD.ActiveDocument.View.Source = [FreeCAD.ActiveDocument.Box] + rc = page.addView(view1) + view1.X = 30 + view1.Y = 150 + view2 = FreeCAD.activeDocument().addObject('TechDraw::DrawViewPart','View001') + FreeCAD.activeDocument().View001.Source = [FreeCAD.activeDocument().Sphere] + rc = page.addView(view2) + view2.X = 220 + view2.Y = 150 + FreeCAD.ActiveDocument.recompute() + + print("Place balloon") + balloon1 = FreeCAD.ActiveDocument.addObject('TechDraw::DrawViewBalloon','Balloon1') + balloon1.sourceView=view1 + balloon1.OriginIsSet=1 + balloon1.OriginX=view1.X + 20 + balloon1.OriginY=view1.Y + 20 + balloon1.Text="1" + balloon1.Y=balloon1.OriginX + 20 + balloon1.X=balloon1.OriginY + 20 + + print("adding balloon1 to page") + rc = page.addView(balloon1) + + balloon2 = FreeCAD.ActiveDocument.addObject('TechDraw::DrawViewBalloon','Balloon2') + balloon2.sourceView=view2 + balloon2.OriginIsSet=1 + balloon2.OriginX=view2.X + 20 + balloon2.OriginY=view2.Y + 20 + balloon2.Text="2" + balloon2.Y=balloon2.OriginX + 20 + balloon2.X=balloon2.OriginY + 20 + + print("adding balloon2 to page") + rc = page.addView(balloon2) + + FreeCAD.ActiveDocument.recompute() + + rc = False + if ("Up-to-date" in balloon2.State) and ("Up-to-date" in balloon2.State): + rc = True + FreeCAD.closeDocument("TDBalloon") + return rc + +if __name__ == '__main__': + DVBalloonTest() diff --git a/src/Mod/TechDraw/TestTechDrawApp.py b/src/Mod/TechDraw/TestTechDrawApp.py index 78f22a7a9d..743b0e6fd8 100644 --- a/src/Mod/TechDraw/TestTechDrawApp.py +++ b/src/Mod/TechDraw/TestTechDrawApp.py @@ -31,6 +31,7 @@ from TDTest.DVAnnoSymImageTest import DVAnnoSymImageTest from TDTest.DVDimensionTest import DVDimensionTest from TDTest.DVPartTest import DVPartTest from TDTest.DVSectionTest import DVSectionTest +from TDTest.DVBalloonTest import DVBalloonTest #--------------------------------------------------------------------------- # define the test cases to test the FreeCAD TechDraw module @@ -86,3 +87,10 @@ class TechDrawTestCases(unittest.TestCase): else: print("TD DrawViewSection test failed") + def testBalloonCase(self): + print("starting TD DrawViewBalloon test") + rc = DVBalloonTest() + if rc: + print("TD DrawViewBalloon test passed") + else: + print("TD DrawViewBalloon test failed")