QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgslayoutmanager.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutmanager.cpp
3 --------------------
4 Date : January 2017
5 Copyright : (C) 2017 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgslayoutmanager.h"
17
19#include "qgslayout.h"
20#include "qgslayoutundostack.h"
21#include "qgsprintlayout.h"
22#include "qgsproject.h"
23#include "qgsreadwritecontext.h"
24#include "qgsreport.h"
25#include "qgsruntimeprofiler.h"
27
28#include "moc_qgslayoutmanager.cpp"
29
39
44
46{
47 return addObject( layout );
48}
49
51{
52 return removeObject( layout );
53}
54
59
60QList<QgsMasterLayoutInterface *> QgsLayoutManager::layouts() const
61{
62 return mObjects;
63}
64
65QList<QgsPrintLayout *> QgsLayoutManager::printLayouts() const
66{
67 QList<QgsPrintLayout *> result;
68 const QList<QgsMasterLayoutInterface *> constLayouts( mObjects );
69 result.reserve( constLayouts.size() );
70 for ( const auto &layout : constLayouts )
71 {
72 QgsPrintLayout *_item( dynamic_cast<QgsPrintLayout *>( layout ) );
73 if ( _item )
74 result.push_back( _item );
75 }
76 return result;
77}
78
80{
81 return objectByName( name );
82}
83
84bool QgsLayoutManager::readXml( const QDomElement &element, const QDomDocument &doc )
85{
86 clear();
87
88 QDomElement layoutsElem = element;
89 if ( element.tagName() != QLatin1String( "Layouts" ) )
90 {
91 layoutsElem = element.firstChildElement( QStringLiteral( "Layouts" ) );
92 }
93 if ( layoutsElem.isNull() )
94 {
95 // handle legacy projects
96 layoutsElem = doc.documentElement();
97 }
98
99 //restore each composer
100 bool result = true;
101 QDomNodeList composerNodes = element.elementsByTagName( QStringLiteral( "Composer" ) );
102 QgsScopedRuntimeProfile profile( tr( "Loading QGIS 2.x compositions" ), QStringLiteral( "projectload" ) );
103 for ( int i = 0; i < composerNodes.size(); ++i )
104 {
105 // This legacy title is the Composer "title" (that can be overridden by the Composition "name")
106 QString legacyTitle = composerNodes.at( i ).toElement().attribute( QStringLiteral( "title" ) );
107 // Convert compositions to layouts
108 QDomNodeList compositionNodes = composerNodes.at( i ).toElement().elementsByTagName( QStringLiteral( "Composition" ) );
109 for ( int j = 0; j < compositionNodes.size(); ++j )
110 {
111 std::unique_ptr< QgsPrintLayout > l( QgsCompositionConverter::createLayoutFromCompositionXml( compositionNodes.at( j ).toElement(), mProject ) );
112 if ( l )
113 {
114 if ( l->name().isEmpty() )
115 l->setName( legacyTitle );
116
117 // some 2.x projects could end in a state where they had duplicated layout names. This is strictly forbidden in 3.x
118 // so check for duplicate name in layouts already added
119 int id = 2;
120 bool isDuplicateName = false;
121 QString originalName = l->name();
122 do
123 {
124 isDuplicateName = false;
125 for ( QgsMasterLayoutInterface *layout : std::as_const( mObjects ) )
126 {
127 if ( l->name() == layout->name() )
128 {
129 isDuplicateName = true;
130 break;
131 }
132 }
133 if ( isDuplicateName )
134 {
135 l->setName( QStringLiteral( "%1 %2" ).arg( originalName ).arg( id ) );
136 id++;
137 }
138 }
139 while ( isDuplicateName );
140
141 bool added = addLayout( l.release() );
142 result = added && result;
143 }
144 }
145 }
146
147 QgsReadWriteContext context;
148 context.setPathResolver( mProject->pathResolver() );
149
150 profile.switchTask( tr( "Creating layouts" ) );
151
152 // restore layouts
153 const QDomNodeList layoutNodes = layoutsElem.childNodes();
154 for ( int i = 0; i < layoutNodes.size(); ++i )
155 {
156 if ( layoutNodes.at( i ).nodeName() != QLatin1String( "Layout" ) )
157 continue;
158
159 const QString layoutName = layoutNodes.at( i ).toElement().attribute( QStringLiteral( "name" ) );
160 QgsScopedRuntimeProfile profile( layoutName, QStringLiteral( "projectload" ) );
161
162 auto l = std::make_unique< QgsPrintLayout >( mProject );
163 l->undoStack()->blockCommands( true );
164 if ( !l->readLayoutXml( layoutNodes.at( i ).toElement(), doc, context ) )
165 {
166 result = false;
167 continue;
168 }
169 l->undoStack()->blockCommands( false );
170 if ( !addLayout( l.release() ) )
171 {
172 result = false;
173 }
174 }
175 //reports
176 profile.switchTask( tr( "Creating reports" ) );
177 const QDomNodeList reportNodes = element.elementsByTagName( QStringLiteral( "Report" ) );
178 for ( int i = 0; i < reportNodes.size(); ++i )
179 {
180 const QString layoutName = reportNodes.at( i ).toElement().attribute( QStringLiteral( "name" ) );
181 QgsScopedRuntimeProfile profile( layoutName, QStringLiteral( "projectload" ) );
182
183 auto r = std::make_unique< QgsReport >( mProject );
184 if ( !r->readLayoutXml( reportNodes.at( i ).toElement(), doc, context ) )
185 {
186 result = false;
187 continue;
188 }
189 if ( !addLayout( r.release() ) )
190 {
191 result = false;
192 }
193 }
194 return result;
195}
196
197QDomElement QgsLayoutManager::writeXml( QDomDocument &doc ) const
198{
199 QDomElement layoutsElem = doc.createElement( QStringLiteral( "Layouts" ) );
200
201 QgsReadWriteContext context;
202 context.setPathResolver( mProject->pathResolver() );
204 {
205 QDomElement layoutElem = l->writeLayoutXml( doc, context );
206 layoutsElem.appendChild( layoutElem );
207 }
208 return layoutsElem;
209}
210
212{
213 if ( !layout )
214 return nullptr;
215
216 std::unique_ptr< QgsMasterLayoutInterface > newLayout( layout->clone() );
217 if ( !newLayout )
218 {
219 return nullptr;
220 }
221
222 newLayout->setName( newName );
223 QgsMasterLayoutInterface *l = newLayout.get();
224 if ( !addLayout( newLayout.release() ) )
225 {
226 return nullptr;
227 }
228 else
229 {
230 // cppcheck-suppress returnDanglingLifetime
231 return l;
232 }
233}
234
236{
237 QStringList names;
238 names.reserve( mObjects.size() );
240 {
241 names << l->name();
242 }
243 QString name;
244 int id = 1;
245 while ( name.isEmpty() || names.contains( name ) )
246 {
247 switch ( type )
248 {
250 name = tr( "Layout %1" ).arg( id );
251 break;
253 name = tr( "Report %1" ).arg( id );
254 break;
255 }
256 id++;
257 }
258 return name;
259}
260
262{
263 if ( mObjects.empty() )
264 return true;
265
266 // NOTE: if visitEnter returns false it means "don't visit the layouts", not "abort all further visitations"
267 if ( !visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layouts, QStringLiteral( "layouts" ), tr( "Layouts" ) ) ) )
268 return true;
269
271 {
272 if ( !l->layoutAccept( visitor ) )
273 return false;
274 }
275
276 if ( !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layouts, QStringLiteral( "layouts" ), tr( "Layouts" ) ) ) )
277 return false;
278
279 return true;
280}
281
283{
284 // ugly, but unavoidable for interfaces...
285 if ( QgsPrintLayout *l = dynamic_cast< QgsPrintLayout * >( layout ) )
286 {
287 connect( l, &QgsPrintLayout::nameChanged, this, [this, l]( const QString & newName )
288 {
289 emit layoutRenamed( l, newName );
290 } );
291 }
292 else if ( QgsReport *r = dynamic_cast< QgsReport * >( layout ) )
293 {
294 connect( r, &QgsReport::nameChanged, this, [this, r]( const QString & newName )
295 {
296 emit layoutRenamed( r, newName );
297 } );
298 }
299}
QgsMasterLayoutInterface * objectByName(const QString &name) const
static std::unique_ptr< QgsPrintLayout > createLayoutFromCompositionXml(const QDomElement &composerElement, QgsProject *project)
createLayoutFromCompositionXml is a factory that creates layout instances from a QGIS 2....
bool readXml(const QDomElement &element, const QDomDocument &doc)
Reads the manager's state from a DOM element, restoring all layouts present in the XML document.
QgsLayoutManager(QgsProject *project=nullptr)
Constructor for QgsLayoutManager.
QList< QgsMasterLayoutInterface * > layouts() const
Returns a list of all layouts contained in the manager.
QList< QgsPrintLayout * > printLayouts() const
Returns a list of all print layouts contained in the manager.
void setupObjectConnections(QgsMasterLayoutInterface *layout) override
void layoutAboutToBeRemoved(const QString &name)
Emitted when a layout is about to be removed from the manager.
~QgsLayoutManager() override
bool removeLayout(QgsMasterLayoutInterface *layout)
Removes a layout from the manager.
QgsMasterLayoutInterface * layoutByName(const QString &name) const
Returns the layout with a matching name, or nullptr if no matching layouts were found.
void clear()
Removes and deletes all layouts from the manager.
bool addLayout(QgsMasterLayoutInterface *layout)
Adds a layout to the manager.
void layoutAboutToBeAdded(const QString &name)
Emitted when a layout is about to be added to the manager.
bool accept(QgsStyleEntityVisitorInterface *visitor) const
Accepts the specified style entity visitor, causing it to visit all style entities associated within ...
void layoutRenamed(QgsMasterLayoutInterface *layout, const QString &newName)
Emitted when a layout is renamed.
void layoutRemoved(const QString &name)
Emitted when a layout was removed from the manager.
QDomElement writeXml(QDomDocument &doc) const
Returns a DOM element representing the state of the manager.
QString generateUniqueTitle(QgsMasterLayoutInterface::Type type=QgsMasterLayoutInterface::PrintLayout) const
Generates a unique title for a new layout of the specified type, which does not clash with any alread...
void layoutAdded(const QString &name)
Emitted when a layout has been added to the manager.
QgsMasterLayoutInterface * duplicateLayout(const QgsMasterLayoutInterface *layout, const QString &newName)
Duplicates an existing layout from the manager.
Interface for master layout type objects, such as print layouts and reports.
@ PrintLayout
Individual print layout (QgsPrintLayout).
virtual QgsMasterLayoutInterface * clone() const =0
Creates a clone of the layout.
Print layout, a QgsLayout subclass for static or atlas-based layouts.
void nameChanged(const QString &name)
Emitted when the layout's name is changed.
void objectAdded(const QString &name)
Emitted when an object has been added to the manager.
void objectRemoved(const QString &name)
Emitted when an object was removed from the manager.
void objectAboutToBeAdded(const QString &name)
Emitted when an object is about to be added to the manager.
void objectAboutToBeRemoved(const QString &name)
Emitted when an object is about to be removed from the manager.
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.
void setPathResolver(const QgsPathResolver &resolver)
Sets up path resolver for conversion between relative and absolute paths.
Scoped object for logging of the runtime for a single operation or group of operations.
void switchTask(const QString &name)
Switches the current task managed by the scoped profile to a new task with the given name.
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.