QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
Loading...
Searching...
No Matches
qgsabstractreportsection.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsabstractreportsection.cpp
3 --------------------
4 begin : December 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8/***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
18
19#include "qgslayout.h"
20#include "qgsreport.h"
24#include "qgsvectorlayer.h"
25
26#include <QString>
27
28using namespace Qt::StringLiterals;
29
31
32QgsAbstractReportSection::QgsAbstractReportSection( QgsAbstractReportSection *parent )
33 : mParent( parent )
34{}
35
36QgsAbstractReportSection::~QgsAbstractReportSection()
37{
38 qDeleteAll( mChildren );
39}
40
41QgsProject *QgsAbstractReportSection::project()
42{
43 if ( QgsReport *report = dynamic_cast< QgsReport * >( this ) )
44 return report->layoutProject();
45
46 QgsAbstractReportSection *current = this;
47 while ( QgsAbstractReportSection *parent = current->parentSection() )
48 {
49 if ( QgsReport *report = dynamic_cast< QgsReport * >( parent ) )
50 return report->layoutProject();
51
52 current = parent;
53 }
54 return nullptr;
55}
56
57void QgsAbstractReportSection::setContext( const QgsReportSectionContext &context )
58{
59 auto setReportContext = [&context]( QgsLayout *layout ) {
60 if ( context.currentLayer )
61 {
62 layout->reportContext().blockSignals( true );
63 layout->reportContext().setLayer( context.currentLayer );
64 layout->reportContext().blockSignals( false );
65 }
66 layout->reportContext().setFeature( context.feature );
67 };
68
69 mContext = context;
70 if ( mHeader )
71 setReportContext( mHeader.get() );
72 if ( mFooter )
73 setReportContext( mFooter.get() );
74
75 for ( QgsAbstractReportSection *section : std::as_const( mChildren ) )
76 {
77 section->setContext( mContext );
78 }
79}
80
81bool QgsAbstractReportSection::writeXml( QDomElement &parentElement, QDomDocument &doc, const QgsReadWriteContext &context ) const
82{
83 QDomElement element = doc.createElement( u"Section"_s );
84 element.setAttribute( u"type"_s, type() );
85
86 element.setAttribute( u"headerEnabled"_s, mHeaderEnabled ? u"1"_s : u"0"_s );
87 if ( mHeader )
88 {
89 QDomElement headerElement = doc.createElement( u"header"_s );
90 headerElement.appendChild( mHeader->writeXml( doc, context ) );
91 element.appendChild( headerElement );
92 }
93 element.setAttribute( u"footerEnabled"_s, mFooterEnabled ? u"1"_s : u"0"_s );
94 if ( mFooter )
95 {
96 QDomElement footerElement = doc.createElement( u"footer"_s );
97 footerElement.appendChild( mFooter->writeXml( doc, context ) );
98 element.appendChild( footerElement );
99 }
100
101 for ( QgsAbstractReportSection *section : mChildren )
102 {
103 section->writeXml( element, doc, context );
104 }
105
106 writePropertiesToElement( element, doc, context );
107
108 parentElement.appendChild( element );
109 return true;
110}
111
112bool QgsAbstractReportSection::readXml( const QDomElement &element, const QDomDocument &doc, const QgsReadWriteContext &context )
113{
114 if ( element.nodeName() != "Section"_L1 )
115 {
116 return false;
117 }
118
119 mHeaderEnabled = element.attribute( u"headerEnabled"_s, u"0"_s ).toInt();
120 mFooterEnabled = element.attribute( u"footerEnabled"_s, u"0"_s ).toInt();
121 const QDomElement headerElement = element.firstChildElement( u"header"_s );
122 if ( !headerElement.isNull() )
123 {
124 const QDomElement headerLayoutElem = headerElement.firstChild().toElement();
125 auto header = std::make_unique< QgsLayout >( project() );
126 header->readXml( headerLayoutElem, doc, context );
127 mHeader = std::move( header );
128 }
129 const QDomElement footerElement = element.firstChildElement( u"footer"_s );
130 if ( !footerElement.isNull() )
131 {
132 const QDomElement footerLayoutElem = footerElement.firstChild().toElement();
133 auto footer = std::make_unique< QgsLayout >( project() );
134 footer->readXml( footerLayoutElem, doc, context );
135 mFooter = std::move( footer );
136 }
137
138 const QDomNodeList sectionItemList = element.childNodes();
139 for ( int i = 0; i < sectionItemList.size(); ++i )
140 {
141 const QDomElement currentSectionElem = sectionItemList.at( i ).toElement();
142 if ( currentSectionElem.nodeName() != "Section"_L1 )
143 continue;
144
145 const QString sectionType = currentSectionElem.attribute( u"type"_s );
146
147 //TODO - eventually move this to a registry when there's enough subclasses to warrant it
148 std::unique_ptr< QgsAbstractReportSection > section;
149 if ( sectionType == "SectionFieldGroup"_L1 )
150 {
151 section = std::make_unique< QgsReportSectionFieldGroup >();
152 }
153 else if ( sectionType == "SectionLayout"_L1 )
154 {
155 section = std::make_unique< QgsReportSectionLayout >();
156 }
157
158 if ( section )
159 {
160 appendChild( section.get() );
161 section->readXml( currentSectionElem, doc, context );
162 ( void ) section.release(); //ownership was transferred already
163 }
164 }
165
166 bool result = readPropertiesFromElement( element, doc, context );
167 return result;
168}
169
170void QgsAbstractReportSection::reloadSettings()
171{
172 if ( mHeader )
173 mHeader->reloadSettings();
174 if ( mFooter )
175 mFooter->reloadSettings();
176}
177
178bool QgsAbstractReportSection::accept( QgsStyleEntityVisitorInterface *visitor ) const
179{
180 // NOTE: if visitEnter returns false it means "don't visit the report section", not "abort all further visitations"
181 if ( mParent && !visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::ReportSection, u"reportsection"_s, QObject::tr( "Report Section" ) ) ) )
182 return true;
183
184 if ( mHeader )
185 {
186 // NOTE: if visitEnter returns false it means "don't visit the header", not "abort all further visitations"
187 if ( visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::ReportHeader, u"reportheader"_s, QObject::tr( "Report Header" ) ) ) )
188 {
189 if ( !mHeader->accept( visitor ) )
190 return false;
191
192 if ( !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::ReportHeader, u"reportheader"_s, QObject::tr( "Report Header" ) ) ) )
193 return false;
194 }
195 }
196
197 for ( const QgsAbstractReportSection *child : mChildren )
198 {
199 if ( !child->accept( visitor ) )
200 return false;
201 }
202
203 if ( mFooter )
204 {
205 // NOTE: if visitEnter returns false it means "don't visit the footer", not "abort all further visitations"
206 if ( visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::ReportFooter, u"reportfooter"_s, QObject::tr( "Report Footer" ) ) ) )
207 {
208 if ( !mFooter->accept( visitor ) )
209 return false;
210
211 if ( !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::ReportFooter, u"reportfooter"_s, QObject::tr( "Report Footer" ) ) ) )
212 return false;
213 }
214 }
215
216 if ( mParent && !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::ReportSection, u"reportsection"_s, QObject::tr( "Report Section" ) ) ) )
217 return false;
218
219 return true;
220}
221
222QString QgsAbstractReportSection::filePath( const QString &baseFilePath, const QString &extension )
223{
224 QString base = u"%1_%2"_s.arg( baseFilePath ).arg( mSectionNumber, 4, 10, QChar( '0' ) );
225 if ( !extension.startsWith( '.' ) )
226 base += '.';
227 base += extension;
228 return base;
229}
230
231QgsLayout *QgsAbstractReportSection::layout()
232{
233 return mCurrentLayout;
234}
235
236bool QgsAbstractReportSection::beginRender()
237{
238 // reset this section
239 reset();
240 mSectionNumber = 0;
241
242 // and all children too
243 bool result = true;
244 for ( QgsAbstractReportSection *child : std::as_const( mChildren ) )
245 {
246 result = result && child->beginRender();
247 }
248 return result;
249}
250
251bool QgsAbstractReportSection::next()
252{
253 mSectionNumber++;
254
255 if ( mNextSection == Header )
256 {
257 // regardless of whether we have a header or not, the next section will be the body
258 mNextSection = Body;
259
260 // if we have a header, then the current section will be the header
261 if ( mHeaderEnabled && mHeader )
262 {
263 if ( prepareHeader() )
264 {
265 mCurrentLayout = mHeader.get();
266 return true;
267 }
268 }
269
270 // but if not, then the current section is a body
271 mNextSection = Body;
272 }
273
274 if ( mNextSection == Body )
275 {
276 mNextSection = Children;
277
278 bool ok = false;
279 // if we have a next body available, use it
280 QgsLayout *body = nextBody( ok );
281 if ( body )
282 {
283 mNextChild = 0;
284 mCurrentLayout = body;
285 return true;
286 }
287 }
288
289 if ( mNextSection == Children )
290 {
291 bool bodiesAvailable = false;
292 do
293 {
294 // we iterate through all the section's children...
295 while ( mNextChild < mChildren.count() )
296 {
297 // ... staying on the current child only while it still has content for us
298 if ( mChildren.at( mNextChild )->next() )
299 {
300 mCurrentLayout = mChildren.at( mNextChild )->layout();
301 return true;
302 }
303 else
304 {
305 // no more content for this child, so move to next child
306 mNextChild++;
307 }
308 }
309
310 // used up all the children
311 // if we have a next body available, use it
312 QgsLayout *body = nextBody( bodiesAvailable );
313 if ( bodiesAvailable )
314 {
315 mNextChild = 0;
316
317 for ( QgsAbstractReportSection *section : std::as_const( mChildren ) )
318 {
319 section->reset();
320 }
321 }
322 if ( body )
323 {
324 mCurrentLayout = body;
325 return true;
326 }
327 } while ( bodiesAvailable );
328
329 // all children and bodies have spent their content, so move to the footer
330 mNextSection = Footer;
331 }
332
333 if ( mNextSection == Footer )
334 {
335 // regardless of whether we have a footer or not, this is the last section
336 mNextSection = End;
337
338 // if we have a footer, then the current section will be the footer
339 if ( mFooterEnabled && mFooter )
340 {
341 if ( prepareFooter() )
342 {
343 mCurrentLayout = mFooter.get();
344 return true;
345 }
346 }
347
348 // if not, then we're all done
349 }
350
351 mCurrentLayout = nullptr;
352 return false;
353}
354
355bool QgsAbstractReportSection::endRender()
356{
357 // reset this section
358 reset();
359
360 // and all children too
361 bool result = true;
362 for ( QgsAbstractReportSection *child : std::as_const( mChildren ) )
363 {
364 result = result && child->endRender();
365 }
366 return result;
367}
368
369void QgsAbstractReportSection::reset()
370{
371 mCurrentLayout = nullptr;
372 mNextChild = 0;
373 mNextSection = Header;
374 for ( QgsAbstractReportSection *section : std::as_const( mChildren ) )
375 {
376 section->reset();
377 }
378}
379
380bool QgsAbstractReportSection::prepareHeader()
381{
382 return true;
383}
384
385bool QgsAbstractReportSection::prepareFooter()
386{
387 return true;
388}
389
390void QgsAbstractReportSection::setHeader( QgsLayout *header )
391{
392 mHeader.reset( header );
393}
394
395void QgsAbstractReportSection::setFooter( QgsLayout *footer )
396{
397 mFooter.reset( footer );
398}
399
400int QgsAbstractReportSection::row() const
401{
402 if ( mParent )
403 return mParent->childSections().indexOf( const_cast<QgsAbstractReportSection *>( this ) );
404
405 return 0;
406}
407
408QgsAbstractReportSection *QgsAbstractReportSection::childSection( int index )
409{
410 return mChildren.value( index );
411}
412
413void QgsAbstractReportSection::appendChild( QgsAbstractReportSection *section )
414{
415 section->setParentSection( this );
416 mChildren.append( section );
417}
418
419void QgsAbstractReportSection::insertChild( int index, QgsAbstractReportSection *section )
420{
421 section->setParentSection( this );
422 index = std::max( 0, index );
423 index = std::min( index, static_cast<int>( mChildren.count() ) );
424 mChildren.insert( index, section );
425}
426
427void QgsAbstractReportSection::removeChild( QgsAbstractReportSection *section )
428{
429 mChildren.removeAll( section );
430 delete section;
431}
432
433void QgsAbstractReportSection::removeChildAt( int index )
434{
435 if ( index < 0 || index >= mChildren.count() )
436 return;
437
438 QgsAbstractReportSection *section = mChildren.at( index );
439 removeChild( section );
440}
441
442void QgsAbstractReportSection::copyCommonProperties( QgsAbstractReportSection *destination ) const
443{
444 destination->mHeaderEnabled = mHeaderEnabled;
445 if ( mHeader )
446 destination->mHeader.reset( mHeader->clone() );
447 else
448 destination->mHeader.reset();
449
450 destination->mFooterEnabled = mFooterEnabled;
451 if ( mFooter )
452 destination->mFooter.reset( mFooter->clone() );
453 else
454 destination->mFooter.reset();
455
456 qDeleteAll( destination->mChildren );
457 destination->mChildren.clear();
458
459 for ( QgsAbstractReportSection *child : std::as_const( mChildren ) )
460 {
461 destination->appendChild( child->clone() );
462 }
463}
464
465bool QgsAbstractReportSection::writePropertiesToElement( QDomElement &, QDomDocument &, const QgsReadWriteContext & ) const
466{
467 return true;
468}
469
470bool QgsAbstractReportSection::readPropertiesFromElement( const QDomElement &, const QDomDocument &, const QgsReadWriteContext & )
471{
472 return true;
473}
474
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition qgslayout.h:50
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:113
A container for the context for various read/write operations on objects.
An interface for classes which can visit style entity (e.g.
virtual bool visitExit(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor stops visiting a node.
virtual bool visitEnter(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor starts visiting a node.
Contains information relating to a node (i.e.