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