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