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