1
/***************************************************************************
2
* Copyright (c) 2020 Wanderer Fan <wandererfan@gmail.com> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
27
#include <QDomDocument>
30
#include <QSvgGenerator>
31
#include <QTemporaryFile>
35
#include <App/Document.h>
36
#include <Base/Console.h>
37
#include <Base/Parameter.h>
38
#include <Gui/Command.h>
39
#include <Gui/Document.h>
40
#include <Gui/Selection.h>
42
#include <Mod/TechDraw/App/DrawHatch.h>
43
#include <Mod/TechDraw/App/DrawLeaderLine.h>
44
#include <Mod/TechDraw/App/DrawPage.h>
45
#include <Mod/TechDraw/App/DrawParametricTemplate.h>
46
#include <Mod/TechDraw/App/DrawProjGroup.h>
47
#include <Mod/TechDraw/App/DrawRichAnno.h>
48
#include <Mod/TechDraw/App/DrawSVGTemplate.h>
49
#include <Mod/TechDraw/App/DrawTemplate.h>
50
#include <Mod/TechDraw/App/DrawUtil.h>
51
#include <Mod/TechDraw/App/DrawViewAnnotation.h>
52
#include <Mod/TechDraw/App/DrawViewBalloon.h>
53
#include <Mod/TechDraw/App/DrawViewClip.h>
54
#include <Mod/TechDraw/App/DrawViewCollection.h>
55
#include <Mod/TechDraw/App/DrawViewDimension.h>
56
#include <Mod/TechDraw/App/DrawViewImage.h>
57
#include <Mod/TechDraw/App/DrawViewPart.h>
58
#include <Mod/TechDraw/App/DrawViewSection.h>
59
#include <Mod/TechDraw/App/DrawViewSpreadsheet.h>
60
#include <Mod/TechDraw/App/DrawViewSymbol.h>
61
#include <Mod/TechDraw/App/DrawWeldSymbol.h>
62
#include <Mod/TechDraw/App/Preferences.h>
64
#include "QGIDrawingTemplate.h"
65
#include "QGILeaderLine.h"
66
#include "QGIProjGroup.h"
67
#include "QGIRichAnno.h"
68
#include "QGISVGTemplate.h"
69
#include "QGITemplate.h"
70
#include "QGIViewAnnotation.h"
71
#include "QGIViewBalloon.h"
72
#include "QGIViewClip.h"
73
#include "QGIViewCollection.h"
74
#include "QGIViewDimension.h"
75
#include "QGIViewImage.h"
76
#include "QGIViewPart.h"
77
#include "QGIViewSection.h"
78
#include "QGIViewSpreadsheet.h"
79
#include "QGIViewSymbol.h"
80
#include "QGIWeldSymbol.h"
83
#include "ViewProviderDrawingView.h"
84
#include "ViewProviderPage.h"
89
#define CC_NS_URI "http://creativecommons.org/ns#"
90
#define DC_NS_URI "http://purl.org/dc/elements/1.1/"
91
#define RDF_NS_URI "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
92
#define INKSCAPE_NS_URI "http://www.inkscape.org/namespaces/inkscape"
93
#define SODIPODI_NS_URI "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
96
using namespace TechDraw;
97
using namespace TechDrawGui;
100
QGSPage::QGSPage(ViewProviderPage* vpPage, QWidget* parent)
101
: QGraphicsScene(parent), pageTemplate(nullptr), m_vpPage(nullptr)
105
setItemIndexMethod(QGraphicsScene::BspTreeIndex);//the default
106
// setItemIndexMethod(QGraphicsScene::NoIndex); //sometimes faster
109
void QGSPage::addChildrenToPage()
111
// Base::Console().Message("QGSP::addChildrenToPage()\n");
112
// A fresh page is added and we iterate through its collected children and add these to Canvas View -MLP
113
// if docobj is a featureviewcollection (ex orthogroup), add its child views. if there are ever children that have children,
114
// we'll have to make this recursive. -WF
115
const std::vector<App::DocumentObject*>& grp = m_vpPage->getDrawPage()->Views.getValues();
116
std::vector<App::DocumentObject*> childViews;
117
for (std::vector<App::DocumentObject*>::const_iterator it = grp.begin(); it != grp.end();
120
TechDraw::DrawViewCollection* collect = dynamic_cast<TechDraw::DrawViewCollection*>(*it);
122
childViews = collect->Views.getValues();
123
for (std::vector<App::DocumentObject*>::iterator itChild = childViews.begin();
124
itChild != childViews.end(); ++itChild) {
125
attachView(*itChild);
129
//when restoring, it is possible for a Dimension to be loaded before the ViewPart it applies to
130
//therefore we need to make sure parentage of the graphics representation is set properly. bit of a kludge.
131
setDimensionGroups();
134
App::DocumentObject* obj = m_vpPage->getDrawPage()->Template.getValue();
135
auto pageTemplate(dynamic_cast<TechDraw::DrawTemplate*>(obj));
137
attachTemplate(pageTemplate);
138
matchSceneRectToTemplate();
144
//********** template related routines *********
146
void QGSPage::attachTemplate(TechDraw::DrawTemplate* obj)
148
// Base::Console().Message("QGSP::attachTemplate()\n");
149
setPageTemplate(obj);
152
void QGSPage::updateTemplate(bool forceUpdate)
154
// Base::Console().Message("QGSP::updateTemplate()\n");
155
App::DocumentObject* templObj = m_vpPage->getDrawPage()->Template.getValue();
156
// TODO: what if template has been deleted? templObj will be NULL. segfault?
161
if (m_vpPage->getDrawPage()->Template.isTouched() || templObj->isTouched()) {
162
// Template is touched so update
165
|| (templObj && templObj->isTouched()
166
&& templObj->isDerivedFrom(TechDraw::DrawTemplate::getClassTypeId()))) {
168
QGITemplate* qItemTemplate = getTemplate();
171
TechDraw::DrawTemplate* pageTemplate =
172
dynamic_cast<TechDraw::DrawTemplate*>(templObj);
173
qItemTemplate->setTemplate(pageTemplate);
174
qItemTemplate->updateView();
180
QPointF QGSPage::getTemplateCenter()
182
App::DocumentObject* obj = m_vpPage->getDrawPage()->Template.getValue();
183
auto pageTemplate(dynamic_cast<TechDraw::DrawTemplate*>(obj));
185
double cx = Rez::guiX(pageTemplate->Width.getValue()) / 2.0;
186
double cy = -Rez::guiX(pageTemplate->Height.getValue()) / 2.0;
187
return QPointF(cx, cy);
189
return QPointF(0.0, 0.0);
192
void QGSPage::matchSceneRectToTemplate(void)
194
// Base::Console().Message("QGSP::matchSceneRectToTemplate()\n");
195
App::DocumentObject* obj = m_vpPage->getDrawPage()->Template.getValue();
196
auto pageTemplate(dynamic_cast<TechDraw::DrawTemplate*>(obj));
198
//make sceneRect 1 pagesize bigger in every direction
199
double width = Rez::guiX(pageTemplate->Width.getValue());
200
double height = Rez::guiX(pageTemplate->Height.getValue());
201
setSceneRect(QRectF(-width, -2.0 * height, 3.0 * width, 3.0 * height));
205
void QGSPage::setPageTemplate(TechDraw::DrawTemplate* templateFeat)
207
// Base::Console().Message("QGSP::setPageTemplate()\n");
210
if (templateFeat->isDerivedFrom(TechDraw::DrawParametricTemplate::getClassTypeId())) {
211
pageTemplate = new QGIDrawingTemplate(this);
213
else if (templateFeat->isDerivedFrom(TechDraw::DrawSVGTemplate::getClassTypeId())) {
214
pageTemplate = new QGISVGTemplate(this);
216
pageTemplate->setTemplate(templateFeat);
217
pageTemplate->updateView();
220
QGITemplate* QGSPage::getTemplate() const { return pageTemplate; }
222
void QGSPage::removeTemplate()
225
removeItem(pageTemplate);
226
pageTemplate->deleteLater();
227
pageTemplate = nullptr;
231
//******* QGIView related routines
233
//! retrieve the QGIView objects currently in the scene
234
std::vector<QGIView*> QGSPage::getViews() const
236
std::vector<QGIView*> result;
237
QList<QGraphicsItem*> items = this->items();
238
for (auto& v : items) {
239
QGIView* qv = dynamic_cast<QGIView*>(v);
241
result.push_back(qv);
247
int QGSPage::addQView(QGIView* view)
250
QGIView* existing = getQGIVByName(view->getViewName());
254
TechDraw::DrawView *viewObj = view->getViewObject();
255
// Preserve the desired position, as addToGroup() adjusts the child view's position
256
QPointF viewPos(Rez::guiX(viewObj->X.getValue()), -Rez::guiX(viewObj->Y.getValue()));
257
// Find if it belongs to a parent
258
QGIView *parent = findParent(view);
260
parent->addToGroup(view);
262
view->setPos(viewPos);
264
auto viewProvider = dynamic_cast<ViewProviderDrawingView *>(QGIView::getViewProvider(view->getViewObject()));
266
view->setZValue(viewProvider->StackOrder.getValue());
269
view->updateView(true);
274
int QGSPage::removeQView(QGIView* view)
277
removeQViewFromScene(view);
283
int QGSPage::removeQViewByName(const char* name)
285
std::vector<QGIView*> items = getViews();
286
QString qsName = QString::fromUtf8(name);
288
QGIView* ourItem = nullptr;
289
for (auto& i : items) {
290
if (qsName == i->data(1).toString()) {//is there a QGIV with this name in scene?
298
int balloonItemType = QGraphicsItem::UserType + 140;
299
if (ourItem->type() == balloonItemType) {
300
QGIViewBalloon* balloon = dynamic_cast<QGIViewBalloon*>(ourItem);
301
balloon->disconnect();
303
removeQViewFromScene(ourItem);
304
delete ourItem;//commenting this prevents crash but means a small memory waste.
305
//alternate fix(?) is to change indexing/caching option in scene/view
311
void QGSPage::removeQViewFromScene(QGIView* view)
313
QGIView* qgParent = dynamic_cast<QGIView*>(view->parentItem());
315
qgParent->removeChild(view);
322
bool QGSPage::addView(const App::DocumentObject* obj)
324
return attachView(const_cast<App::DocumentObject*>(obj));
327
bool QGSPage::attachView(App::DocumentObject* obj)
329
// Base::Console().Message("QGSP::attachView(%s)\n", obj->getNameInDocument());
330
QGIView* existing = findQViewForDocObj(obj);
334
auto typeId(obj->getTypeId());
336
QGIView* qview(nullptr);
338
if (typeId.isDerivedFrom(TechDraw::DrawViewSection::getClassTypeId())) {
339
qview = addViewSection(static_cast<TechDraw::DrawViewSection*>(obj));
341
else if (typeId.isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) {
342
qview = addViewPart(static_cast<TechDraw::DrawViewPart*>(obj));
344
else if (typeId.isDerivedFrom(TechDraw::DrawProjGroup::getClassTypeId())) {
345
qview = addProjectionGroup(static_cast<TechDraw::DrawProjGroup*>(obj));
347
else if (typeId.isDerivedFrom(TechDraw::DrawViewCollection::getClassTypeId())) {
348
qview = addDrawView(static_cast<TechDraw::DrawViewCollection*>(obj));
350
else if (typeId.isDerivedFrom(TechDraw::DrawViewDimension::getClassTypeId())) {
351
qview = addViewDimension(static_cast<TechDraw::DrawViewDimension*>(obj));
353
else if (typeId.isDerivedFrom(TechDraw::DrawViewBalloon::getClassTypeId())) {
354
qview = addViewBalloon(static_cast<TechDraw::DrawViewBalloon*>(obj));
356
else if (typeId.isDerivedFrom(TechDraw::DrawViewAnnotation::getClassTypeId())) {
357
qview = addDrawViewAnnotation(static_cast<TechDraw::DrawViewAnnotation*>(obj));
359
else if (typeId.isDerivedFrom(TechDraw::DrawViewSymbol::getClassTypeId())) {
360
qview = addDrawViewSymbol(static_cast<TechDraw::DrawViewSymbol*>(obj));
362
else if (typeId.isDerivedFrom(TechDraw::DrawViewClip::getClassTypeId())) {
363
qview = addDrawViewClip(static_cast<TechDraw::DrawViewClip*>(obj));
365
else if (typeId.isDerivedFrom(TechDraw::DrawViewSpreadsheet::getClassTypeId())) {
366
qview = addDrawViewSpreadsheet(static_cast<TechDraw::DrawViewSpreadsheet*>(obj));
368
else if (typeId.isDerivedFrom(TechDraw::DrawViewImage::getClassTypeId())) {
369
qview = addDrawViewImage(static_cast<TechDraw::DrawViewImage*>(obj));
371
else if (typeId.isDerivedFrom(TechDraw::DrawLeaderLine::getClassTypeId())) {
372
qview = addViewLeader(static_cast<TechDraw::DrawLeaderLine*>(obj));
374
else if (typeId.isDerivedFrom(TechDraw::DrawRichAnno::getClassTypeId())) {
375
qview = addRichAnno(static_cast<TechDraw::DrawRichAnno*>(obj));
377
else if (typeId.isDerivedFrom(TechDraw::DrawWeldSymbol::getClassTypeId())) {
378
qview = addWeldSymbol(static_cast<TechDraw::DrawWeldSymbol*>(obj));
380
else if (typeId.isDerivedFrom(TechDraw::DrawHatch::getClassTypeId())) {
381
//Hatch is not attached like other Views (since it isn't really a View)
385
return (qview != nullptr);
388
QGIView* QGSPage::addViewPart(TechDraw::DrawViewPart* partFeat)
390
// Base::Console().Message("QGSP::addViewPart(%s)\n", partFeat->Label.getValue());
391
auto viewPart(new QGIViewPart);
393
viewPart->setViewPartFeature(partFeat);
396
// we need to install an event filter for any views derived from DrawViewPart
397
viewPart->installSceneEventFilter(viewPart);
401
QGIView* QGSPage::addViewSection(DrawViewSection* sectionFeat)
403
auto viewSection(new QGIViewSection);
405
viewSection->setViewPartFeature(sectionFeat);
407
addQView(viewSection);
408
viewSection->installSceneEventFilter(viewSection);
412
QGIView* QGSPage::addProjectionGroup(TechDraw::DrawProjGroup* projGroupFeat)
414
// Base::Console().Message("QGSP::addprojectionGroup(%s)\n", projGroupFeat->Label.getValue());
415
auto qview(new QGIProjGroup);
417
qview->setViewFeature(projGroupFeat);
419
qview->installSceneEventFilter(qview);
424
QGIView* QGSPage::addDrawView(TechDraw::DrawView* view)
426
auto qview(new QGIView);
428
qview->setViewFeature(view);
433
QGIView* QGSPage::addDrawViewCollection(TechDraw::DrawViewCollection* collectionFeat)
435
auto qview(new QGIViewCollection);
437
qview->setViewFeature(collectionFeat);
442
QGIView* QGSPage::addDrawViewAnnotation(TechDraw::DrawViewAnnotation* annoFeat)
444
auto qview(new QGIViewAnnotation);
446
qview->setViewAnnoFeature(annoFeat);
452
QGIView* QGSPage::addDrawViewSymbol(TechDraw::DrawViewSymbol* symbolFeat)
454
QGIViewSymbol *symbolView = new QGIViewSymbol;
455
symbolView->setViewFeature(symbolFeat);
457
addQView(symbolView);
461
QGIView* QGSPage::addDrawViewClip(TechDraw::DrawViewClip* view)
463
auto qview(new QGIViewClip);
465
qview->setPosition(Rez::guiX(view->X.getValue()), Rez::guiX(view->Y.getValue()));
466
qview->setViewFeature(view);
472
QGIView* QGSPage::addDrawViewSpreadsheet(TechDraw::DrawViewSpreadsheet* sheetFeat)
474
auto qview(new QGIViewSpreadsheet);
476
qview->setViewFeature(sheetFeat);
482
QGIView* QGSPage::addDrawViewImage(TechDraw::DrawViewImage* imageFeat)
484
auto qview(new QGIViewImage);
486
qview->setViewFeature(imageFeat);
492
QGIView* QGSPage::addViewBalloon(TechDraw::DrawViewBalloon* balloonFeat)
494
// Base::Console().Message("QGSP::addViewBalloon(%s)\n", balloonFeat->getNameInDocument());
495
auto vBalloon(new QGIViewBalloon);
499
vBalloon->setViewPartFeature(balloonFeat);
501
QGIView* parent = nullptr;
502
parent = findParent(vBalloon);
505
addBalloonToParent(vBalloon, parent);
511
void QGSPage::addBalloonToParent(QGIViewBalloon* balloon, QGIView* parent)
513
// Base::Console().Message("QGSP::addBalloonToParent()\n");
515
assert(parent);//blow up if we don't have Dimension or Parent
516
QPointF posRef(0., 0.);
517
QPointF mapPos = balloon->mapToItem(parent, posRef);
518
balloon->moveBy(-mapPos.x(), -mapPos.y());
519
parent->addToGroup(balloon);
520
balloon->setZValue(ZVALUE::DIMENSION);
523
//origin is in scene coordinates from QGViewPage
524
void QGSPage::createBalloon(QPointF origin, DrawView* parent)
526
// Base::Console().Message("QGSP::createBalloon(%s)\n", DrawUtil::formatVector(origin).c_str());
527
std::string featName = getDrawPage()->getDocument()->getUniqueObjectName("Balloon");
528
std::string pageName = getDrawPage()->getNameInDocument();
530
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Balloon"));
531
Command::doCommand(Command::Doc,
532
"App.activeDocument().addObject('TechDraw::DrawViewBalloon', '%s')",
534
Command::doCommand(Command::Doc, "App.activeDocument().%s.translateLabel('DrawViewBalloon', 'Balloon', '%s')",
535
featName.c_str(), featName.c_str());
537
TechDraw::DrawViewBalloon* balloon = dynamic_cast<TechDraw::DrawViewBalloon*>(
538
getDrawPage()->getDocument()->getObject(featName.c_str()));
540
throw Base::TypeError("QGSP::createBalloon - balloon not found\n");
542
Command::doCommand(Command::Doc,
543
"App.activeDocument().%s.SourceView = (App.activeDocument().%s)",
544
featName.c_str(), parent->getNameInDocument());
545
QGIView* qgParent = getQGIVByName(parent->getNameInDocument());
546
//convert from scene coords to qgParent coords and unscale
547
QPointF parentOrigin = qgParent->mapFromScene(origin) / parent->getScale();
548
balloon->setOrigin(parentOrigin);
549
//convert origin to App side coords
550
QPointF appOrigin = Rez::appPt(parentOrigin);
551
appOrigin = DrawUtil::invertY(appOrigin);
552
balloon->OriginX.setValue(appOrigin.x());
553
balloon->OriginY.setValue(appOrigin.y());
554
double textOffset = 20.0 / parent->getScale();
555
balloon->X.setValue(appOrigin.x() + textOffset);
556
balloon->Y.setValue(appOrigin.y() + textOffset);
558
int idx = getDrawPage()->getNextBalloonIndex();
559
balloon->Text.setValue(std::to_string(idx).c_str());
561
Command::doCommand(Command::Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)",
562
pageName.c_str(), featName.c_str());
564
Gui::Command::commitCommand();
566
// Touch the parent feature so the balloon in tree view appears as a child
570
QGIView* QGSPage::addViewDimension(TechDraw::DrawViewDimension* dimFeat)
572
auto dimGroup(new QGIViewDimension);
576
dimGroup->setViewPartFeature(dimFeat);
578
// Find if it belongs to a parent
579
QGIView* parent = nullptr;
580
parent = findParent(dimGroup);
583
addDimToParent(dimGroup, parent);
589
void QGSPage::addDimToParent(QGIViewDimension* dim, QGIView* parent)
591
// Base::Console().Message("QGSP::addDimToParent()\n");
593
assert(parent);//blow up if we don't have Dimension or Parent
594
QPointF posRef(0., 0.);
595
QPointF mapPos = dim->mapToItem(parent, posRef);
596
dim->moveBy(-mapPos.x(), -mapPos.y());
597
parent->addToGroup(dim);
598
dim->setZValue(ZVALUE::DIMENSION);
601
QGIView* QGSPage::addViewLeader(TechDraw::DrawLeaderLine* leaderFeat)
603
QGILeaderLine *leaderView = new QGILeaderLine;
604
leaderView->setViewFeature(leaderFeat);
606
addQView(leaderView);
610
QGIView* QGSPage::addRichAnno(TechDraw::DrawRichAnno* richFeat)
612
QGIRichAnno *richView = new QGIRichAnno;
613
richView->setViewFeature(richFeat);
619
QGIView* QGSPage::addWeldSymbol(TechDraw::DrawWeldSymbol* weldFeat)
621
QGIWeldSymbol *weldView = new QGIWeldSymbol;
622
weldView->setViewFeature(weldFeat);
628
void QGSPage::setDimensionGroups(void)
630
const std::vector<QGIView*>& allItems = getViews();
631
int dimItemType = QGraphicsItem::UserType + 106;
633
for (auto& item : allItems) {
634
if (item->type() == dimItemType && !item->group()) {
635
QGIView* parent = findParent(item);
637
QGIViewDimension* dim = dynamic_cast<QGIViewDimension*>(item);
638
addDimToParent(dim, parent);
644
void QGSPage::setBalloonGroups(void)
646
const std::vector<QGIView*>& allItems = getViews();
647
int balloonItemType = QGraphicsItem::UserType + 140;
649
for (auto& item : allItems) {
650
if (item->type() == balloonItemType && !item->group()) {
651
QGIView* parent = findParent(item);
653
QGIViewBalloon* balloon = dynamic_cast<QGIViewBalloon*>(item);
654
addBalloonToParent(balloon, parent);
660
//! find the graphic for a DocumentObject
661
QGIView* QGSPage::findQViewForDocObj(App::DocumentObject* obj) const
663
// Base::Console().Message("QGSP::findQViewForDocObj(%s)\n", obj->getNameInDocument());
665
const std::vector<QGIView*> qviews = getViews();
666
for (std::vector<QGIView*>::const_iterator it = qviews.begin(); it != qviews.end(); ++it) {
667
if (strcmp(obj->getNameInDocument(), (*it)->getViewName()) == 0)
674
//! find the graphic for DocumentObject with name
675
QGIView* QGSPage::getQGIVByName(std::string name) const
677
QList<QGraphicsItem*> qgItems = items();
678
QList<QGraphicsItem*>::iterator it = qgItems.begin();
679
for (; it != qgItems.end(); it++) {
680
QGIView* qv = dynamic_cast<QGIView*>((*it));
682
const char* qvName = qv->getViewName();
683
if (name.compare(qvName) == 0) {
691
//find the parent of a QGIV based on the corresponding feature's parentage
692
QGIView* QGSPage::findParent(QGIView* view) const
694
// Base::Console().Message("QGSP::findParent(%s)\n", view->getViewName());
695
const std::vector<QGIView*> qviews = getViews();
696
TechDraw::DrawView* myFeat = view->getViewObject();
698
TechDraw::DrawView *ownerFeat = myFeat->claimParent();
700
QGIView *ownerView = getQGIVByName(ownerFeat->getNameInDocument());
706
//If type is dimension we check references first
707
TechDraw::DrawViewDimension* dim = nullptr;
708
dim = dynamic_cast<TechDraw::DrawViewDimension*>(myFeat);
710
std::vector<App::DocumentObject*> objs = dim->References2D.getValues();
713
std::vector<App::DocumentObject*> objs = dim->References2D.getValues();
714
// Attach the dimension to the first object's group
715
for (std::vector<QGIView*>::const_iterator it = qviews.begin(); it != qviews.end();
717
if (strcmp((*it)->getViewName(), objs.at(0)->getNameInDocument()) == 0) {
724
//If type is balloon we check references first
725
TechDraw::DrawViewBalloon* balloon = nullptr;
726
balloon = dynamic_cast<TechDraw::DrawViewBalloon*>(myFeat);
729
App::DocumentObject* obj = balloon->SourceView.getValue();
732
// Attach the Balloon to the first object's group
733
for (std::vector<QGIView*>::const_iterator it = qviews.begin(); it != qviews.end();
735
if (strcmp((*it)->getViewName(), obj->getNameInDocument()) == 0) {
742
// Not found a parent
746
bool QGSPage::hasQView(App::DocumentObject* obj)
748
const std::vector<QGIView*>& views = getViews();
749
std::vector<QGIView*>::const_iterator qview = views.begin();
751
while (qview != views.end()) {
752
// Unsure if we can compare pointers so rely on name
753
if (strcmp((*qview)->getViewName(), obj->getNameInDocument()) == 0) {
762
void QGSPage::refreshViews()
764
// Base::Console().Message("QGSP::refreshViews()\n");
765
QList<QGraphicsItem*> list = items();
766
QList<QGraphicsItem*> qgiv;
768
for (auto q : list) {
769
QString viewFamily = QString::fromUtf8("QGIV");
770
if (viewFamily == q->data(0).toString()) {
774
for (auto q : qgiv) {
775
QGIView* itemView = dynamic_cast<QGIView*>(q);
777
itemView->updateView(true);
782
void QGSPage::findMissingViews(const std::vector<App::DocumentObject*>& list,
783
std::vector<App::DocumentObject*>& missing)
785
for (std::vector<App::DocumentObject*>::const_iterator it = list.begin(); it != list.end();
789
missing.push_back(*it);
791
if ((*it)->isDerivedFrom<TechDraw::DrawViewCollection>()) {
792
std::vector<App::DocumentObject*> missingChildViews;
793
TechDraw::DrawViewCollection* collection =
794
dynamic_cast<TechDraw::DrawViewCollection*>(*it);
795
// Find Child Views recursively
796
findMissingViews(collection->Views.getValues(), missingChildViews);
798
// Append the views to current missing list
799
for (std::vector<App::DocumentObject*>::const_iterator it = missingChildViews.begin();
800
it != missingChildViews.end(); ++it) {
801
missing.push_back(*it);
807
//this is time consuming. should only be used when there is a problem.
808
//Solve the situation where a DrawView belonging to this DrawPage has no QGraphicsItem in
809
//the QGScene for the DrawPage -or-
810
//a QGraphics item exists in the DrawPage's QGScene, but there is no corresponding DrawView
812
void QGSPage::fixOrphans(bool force)
816
// get all the DrawViews for this page, including the second level ones
817
// if we ever have collections of collections, we'll need to revisit this
818
TechDraw::DrawPage* thisPage = m_vpPage->getDrawPage();
820
if (!thisPage->isAttachedToDocument())
823
std::vector<App::DocumentObject*> pChildren = thisPage->getAllViews();
825
// if dv doesn't have a graphic, make one
826
for (auto& dv : pChildren) {
827
if (dv->isRemoving())
829
QGIView* qv = findQViewForDocObj(dv);
833
// if qView doesn't have a Feature on this Page, delete it
834
std::vector<QGIView*> qvss = getViews();
835
// qvss may contain an item and its child item(s) and to avoid to access a deleted item a QPointer is needed
836
std::vector<QPointer<QGIView>> qvs;
837
std::for_each(qvss.begin(), qvss.end(), [&qvs](QGIView* v) { qvs.emplace_back(v); });
838
App::Document* doc = m_vpPage->getDrawPage()->getDocument();
839
for (auto& qv : qvs) {
841
continue;// already deleted?
843
App::DocumentObject* obj = doc->getObject(qv->getViewName());
845
//no DrawView anywhere in Document
849
//DrawView exists in Document. Does it belong to this DrawPage?
850
int numParentPages = qv->getViewObject()->countParentPages();
851
if (numParentPages == 0) {
852
//DrawView does not belong to any DrawPage
853
//remove QGItem from QGScene
856
else if (numParentPages == 1) {
857
//Does DrawView belong to this DrawPage?
858
TechDraw::DrawPage* parentPage = qv->getViewObject()->findParentPage();
859
if (thisPage != parentPage) {
860
//DrawView does not belong to this DrawPage
861
//remove QGItem from QGScene
865
else if (numParentPages > 1) {
866
//DrawView belongs to multiple DrawPages
867
//check if this MDIViewPage corresponds to any parent DrawPage
868
//if not, delete the QGItem
869
std::vector<TechDraw::DrawPage*> potentialParentPages =
870
qv->getViewObject()->findAllParentPages();
872
for (auto p : potentialParentPages) {
879
//none of the parent Pages for View correspond to this Page
887
bool QGSPage::orphanExists(const char* viewName, const std::vector<App::DocumentObject*>& list)
889
for (std::vector<App::DocumentObject*>::const_iterator it = list.begin(); it != list.end();
892
//Check child objects too recursively
893
if ((*it)->isDerivedFrom(TechDraw::DrawViewCollection::getClassTypeId())) {
894
TechDraw::DrawViewCollection* collection =
895
dynamic_cast<TechDraw::DrawViewCollection*>(*it);
896
if (orphanExists(viewName, collection->Views.getValues()))
900
// Unsure if we can compare pointers so rely on name
901
if (strcmp(viewName, (*it)->getNameInDocument()) == 0) {
908
//NOTE: this doesn't add missing views. see fixOrphans()
909
void QGSPage::redrawAllViews()
911
// Base::Console().Message("QGSP::redrawAllViews() - views: %d\n", getViews().size());
912
const std::vector<QGIView*>& upviews = getViews();
913
for (std::vector<QGIView*>::const_iterator it = upviews.begin(); it != upviews.end(); ++it) {
914
(*it)->updateView(true);
918
//NOTE: this doesn't add missing views. see fixOrphans()
919
void QGSPage::redraw1View(TechDraw::DrawView* dView)
921
std::string dvName = dView->getNameInDocument();
922
const std::vector<QGIView*>& upviews = getViews();
923
for (std::vector<QGIView*>::const_iterator it = upviews.begin(); it != upviews.end(); ++it) {
924
std::string qgivName = (*it)->getViewName();
925
if (dvName == qgivName) {
926
(*it)->updateView(true);
931
// RichTextAnno needs to know when it is rendering an Svg as the font size
932
// is handled differently in Svg compared to the screen or Pdf.
933
void QGSPage::setExportingSvg(bool enable)
935
QList<QGraphicsItem*> sceneItems = items();
936
for (auto& qgi : sceneItems) {
937
QGIRichAnno* qgiRTA = dynamic_cast<QGIRichAnno*>(qgi);
939
qgiRTA->setExportingSvg(enable);
944
void QGSPage::saveSvg(QString filename)
946
// TODO: We only have m_vpPage because constructor gets passed a view provider...
947
//NOTE: this makes wrong size pages in low-Rez
948
TechDraw::DrawPage* page(m_vpPage->getDrawPage());
950
const QString docName(QString::fromUtf8(page->getDocument()->getName()));
951
const QString pageName(QString::fromUtf8(page->getNameInDocument()));
952
QString svgDescription = QString::fromUtf8("Drawing page: ") + pageName
953
+ QString::fromUtf8(" exported from FreeCAD document: ") + docName;
955
QSvgGenerator svgGen;
956
QTemporaryFile temporaryFile;
957
svgGen.setOutputDevice(&temporaryFile);
959
// Set resolution in DPI. Use the actual one, i.e. Rez::guiX(inch)
960
svgGen.setResolution(Rez::guiX(25.4));
962
// Set size in pixels, which Qt recomputes using DPI to mm.
963
int pixelWidth = Rez::guiX(page->getPageWidth());
964
int pixelHeight = Rez::guiX(page->getPageHeight());
965
svgGen.setSize(QSize(pixelWidth, pixelHeight));
967
//"By default this property is set to QSize(-1, -1), which indicates that the generator should not output
968
// the width and height attributes of the <svg> element." >> but Inkscape won't read it without size info??
969
svgGen.setViewBox(QRect(0, 0, pixelWidth, pixelHeight));
971
svgGen.setTitle(QString::fromUtf8("FreeCAD SVG Export"));
972
svgGen.setDescription(svgDescription);
974
Gui::Selection().clearSelection();
976
bool saveState = m_vpPage->getFrameState();
977
m_vpPage->setFrameState(false);
978
m_vpPage->setTemplateMarkers(false);
979
setExportingSvg(true);
981
// Here we temporarily hide the page template, because Qt would otherwise convert the SVG template
982
// texts into series of paths, making the later document edits practically unfeasible.
983
// We will insert the SVG template ourselves in the final XML postprocessing operation.
984
QGISVGTemplate* svgTemplate = dynamic_cast<QGISVGTemplate*>(pageTemplate);
985
bool templateVisible = false;
987
templateVisible = svgTemplate->isVisible();
993
double width = Rez::guiX(page->getPageWidth());
994
double height = Rez::guiX(page->getPageHeight());
995
QRectF sourceRect(0.0, -height, width, height);
996
QRectF targetRect(0.0, 0.0, width, height);
998
Gui::Selection().clearSelection();
1002
render(&p, targetRect, sourceRect);//note: scene render, not item render!
1005
m_vpPage->setFrameState(saveState);
1006
m_vpPage->setTemplateMarkers(saveState);
1007
setExportingSvg(false);
1008
if (templateVisible && svgTemplate) {
1009
svgTemplate->show();
1014
temporaryFile.close();
1015
postProcessXml(temporaryFile, filename, pageName);
1018
static void removeEmptyGroups(QDomElement e)
1020
while (!e.isNull()) {
1021
QDomElement next = e.nextSiblingElement();
1022
if (e.hasChildNodes()) {
1023
removeEmptyGroups(e.firstChildElement());
1025
else if (e.tagName() == QLatin1String("g")) {
1026
e.parentNode().removeChild(e);
1032
void QGSPage::postProcessXml(QTemporaryFile& temporaryFile, QString fileName, QString pageName)
1034
QDomDocument exportDoc(QString::fromUtf8("SvgDoc"));
1035
QFile file(temporaryFile.fileName());
1036
if (!file.open(QIODevice::ReadOnly)) {
1037
Base::Console().Error("QGSPage::ppsvg - tempfile open error\n");
1040
if (!exportDoc.setContent(&file)) {
1041
Base::Console().Error("QGSPage::ppsvg - xml error\n");
1047
QDomElement exportDocElem = exportDoc.documentElement();//root <svg>
1049
// Insert Freecad SVG namespace into namespace declarations
1050
exportDocElem.setAttribute(QString::fromUtf8("xmlns:freecad"),
1051
QString::fromUtf8(FREECAD_SVG_NS_URI));
1052
// Insert all namespaces used by TechDraw's page template SVGs
1053
exportDocElem.setAttribute(QString::fromUtf8("xmlns:svg"), QString::fromUtf8(SVG_NS_URI));
1054
exportDocElem.setAttribute(QString::fromUtf8("xmlns:cc"), QString::fromUtf8(CC_NS_URI));
1055
exportDocElem.setAttribute(QString::fromUtf8("xmlns:dc"), QString::fromUtf8(DC_NS_URI));
1056
exportDocElem.setAttribute(QString::fromUtf8("xmlns:rdf"), QString::fromUtf8(RDF_NS_URI));
1057
exportDocElem.setAttribute(QString::fromUtf8("xmlns:inkscape"),
1058
QString::fromUtf8(INKSCAPE_NS_URI));
1059
exportDocElem.setAttribute(QString::fromUtf8("xmlns:sodipodi"),
1060
QString::fromUtf8(SODIPODI_NS_URI));
1062
// Create the root group which will host the drawing group and the template group
1063
QDomElement rootGroup = exportDoc.createElement(QString::fromUtf8("g"));
1064
rootGroup.setAttribute(QString::fromUtf8("id"), pageName);
1065
rootGroup.setAttribute(QString::fromUtf8("inkscape:groupmode"), QString::fromUtf8("layer"));
1066
rootGroup.setAttribute(QString::fromUtf8("inkscape:label"), QString::fromUtf8("TechDraw"));
1068
// Now insert our template
1069
QGISVGTemplate* svgTemplate = dynamic_cast<QGISVGTemplate*>(pageTemplate);
1071
DrawSVGTemplate* drawTemplate = svgTemplate->getSVGTemplate();
1073
QString templateSvg = drawTemplate->processTemplate();
1074
QDomDocument templateResultDoc(QString::fromUtf8("SvgDoc"));
1075
if (templateResultDoc.setContent(templateSvg)) {
1076
QDomElement templateDocElem = templateResultDoc.documentElement();
1078
// Insert the template into a new group with id set to template name
1079
QDomElement templateGroup = exportDoc.createElement(QString::fromUtf8("g"));
1080
Base::FileInfo fi(drawTemplate->PageResult.getValue());
1081
templateGroup.setAttribute(QString::fromUtf8("id"),
1082
QString::fromUtf8(fi.fileName().c_str()));
1083
templateGroup.setAttribute(QString::fromUtf8("style"),
1084
QString::fromUtf8("stroke: none;"));
1086
// Scale the template group correctly
1087
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
1088
templateGroup.setAttribute(
1089
QString::fromUtf8("transform"),
1090
QString().sprintf("scale(%f, %f)", Rez::guiX(1.0), Rez::guiX(1.0)));
1092
templateGroup.setAttribute(QString::fromUtf8("transform"),
1093
QString::fromLatin1("scale(%1, %2)")
1094
.arg(Rez::guiX(1.0), 0, 'f')
1095
.arg(Rez::guiX(1.0), 0, 'f'));
1098
// Finally, transfer all template document child nodes under the template group
1099
while (!templateDocElem.firstChild().isNull()) {
1100
templateGroup.appendChild(templateDocElem.firstChild());
1103
rootGroup.appendChild(templateGroup);
1108
// Obtain the drawing group element, move it under root node and set its id to "DrawingContent"
1109
QDomElement drawingGroup = exportDocElem.firstChildElement(QLatin1String("g"));
1110
if (!drawingGroup.isNull()) {
1111
drawingGroup.setAttribute(QString::fromUtf8("id"), QString::fromUtf8("DrawingContent"));
1112
rootGroup.appendChild(drawingGroup);
1114
exportDocElem.appendChild(rootGroup);
1116
// As icing on the cake, get rid of the empty <g>'s Qt SVG generator painting inserts.
1117
removeEmptyGroups(exportDocElem);
1119
// Time to save our product
1120
QFile outFile(fileName);
1121
if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
1122
Base::Console().Error("QGSP::ppxml - failed to open file for writing: %s\n",
1123
qPrintable(fileName));
1126
QTextStream stream(&outFile);
1127
stream.setGenerateByteOrderMark(false);
1128
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1129
stream.setCodec("UTF-8");
1132
stream << exportDoc.toByteArray();
1136
TechDraw::DrawPage* QGSPage::getDrawPage() { return m_vpPage->getDrawPage(); }
1138
QColor QGSPage::getBackgroundColor()
1141
fcColor.setPackedValue(Preferences::getPreferenceGroup("Colors")->GetUnsigned("Background", 0x70707000));
1142
return fcColor.asValue<QColor>();
1146
#include <Mod/TechDraw/Gui/moc_QGSPage.cpp>