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