Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgslayerdefinition.cpp
3 : : ---------------------
4 : : begin : January 2015
5 : : copyright : (C) 2015 by Nathan Woodrow
6 : : email : woodrow dot nathan 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 : : #include <QFileInfo>
16 : : #include <QFile>
17 : : #include <QDir>
18 : : #include <QTextStream>
19 : :
20 : : #include "qgslayerdefinition.h"
21 : : #include "qgslayertree.h"
22 : : #include "qgslogger.h"
23 : : #include "qgsmaplayer.h"
24 : : #include "qgspathresolver.h"
25 : : #include "qgspluginlayer.h"
26 : : #include "qgspluginlayerregistry.h"
27 : : #include "qgsproject.h"
28 : : #include "qgsrasterlayer.h"
29 : : #include "qgsreadwritecontext.h"
30 : : #include "qgsvectorlayer.h"
31 : : #include "qgsvectortilelayer.h"
32 : : #include "qgsapplication.h"
33 : : #include "qgsmaplayerfactory.h"
34 : : #include "qgsmeshlayer.h"
35 : : #include "qgspointcloudlayer.h"
36 : : #include "qgsannotationlayer.h"
37 : :
38 : 0 : bool QgsLayerDefinition::loadLayerDefinition( const QString &path, QgsProject *project, QgsLayerTreeGroup *rootGroup, QString &errorMessage )
39 : : {
40 : 0 : QFile file( path );
41 : 0 : if ( !file.open( QIODevice::ReadOnly ) )
42 : : {
43 : 0 : errorMessage = QStringLiteral( "Can not open file" );
44 : 0 : return false;
45 : : }
46 : :
47 : 0 : QDomDocument doc;
48 : 0 : QString message;
49 : 0 : if ( !doc.setContent( &file, &message ) )
50 : : {
51 : 0 : errorMessage = message;
52 : 0 : return false;
53 : : }
54 : :
55 : 0 : QFileInfo fileinfo( file );
56 : 0 : QDir::setCurrent( fileinfo.absoluteDir().path() );
57 : :
58 : 0 : QgsReadWriteContext context;
59 : 0 : context.setPathResolver( QgsPathResolver( path ) );
60 : 0 : context.setProjectTranslator( project );
61 : :
62 : 0 : return loadLayerDefinition( doc, project, rootGroup, errorMessage, context );
63 : 0 : }
64 : :
65 : 0 : bool QgsLayerDefinition::loadLayerDefinition( QDomDocument doc, QgsProject *project, QgsLayerTreeGroup *rootGroup, QString &errorMessage, QgsReadWriteContext &context )
66 : : {
67 : 0 : errorMessage.clear();
68 : :
69 : 0 : QgsLayerTreeGroup *root = new QgsLayerTreeGroup();
70 : :
71 : : // reorder maplayer nodes based on dependencies
72 : : // dependencies have to be resolved before IDs get changed
73 : 0 : DependencySorter depSorter( doc );
74 : 0 : if ( !depSorter.hasMissingDependency() )
75 : : {
76 : 0 : QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
77 : 0 : QVector<QDomNode> clonedSorted;
78 : 0 : const auto constSortedLayerNodes = sortedLayerNodes;
79 : 0 : for ( const QDomNode &node : constSortedLayerNodes )
80 : : {
81 : 0 : clonedSorted << node.cloneNode();
82 : : }
83 : 0 : QDomNode layersNode = doc.elementsByTagName( QStringLiteral( "maplayers" ) ).at( 0 );
84 : : // replace old children with new ones
85 : 0 : QDomNode childNode = layersNode.firstChild();
86 : 0 : for ( int i = 0; ! childNode.isNull(); i++ )
87 : : {
88 : 0 : layersNode.replaceChild( clonedSorted.at( i ), childNode );
89 : 0 : childNode = childNode.nextSibling();
90 : 0 : }
91 : 0 : }
92 : : // if a dependency is missing, we still try to load layers, since dependencies may already be loaded
93 : :
94 : : // IDs of layers should be changed otherwise we may have more then one layer with the same id
95 : : // We have to replace the IDs before we load them because it's too late once they are loaded
96 : 0 : const QDomNodeList treeLayerNodes = doc.elementsByTagName( QStringLiteral( "layer-tree-layer" ) );
97 : 0 : QDomNode treeLayerNode = treeLayerNodes.at( 0 );
98 : 0 : for ( int i = 0; ! treeLayerNode.isNull(); ++i )
99 : : {
100 : 0 : treeLayerNode = treeLayerNodes.at( i );
101 : 0 : QDomElement treeLayerElem = treeLayerNode.toElement();
102 : 0 : QString oldid = treeLayerElem.attribute( QStringLiteral( "id" ) );
103 : 0 : QString layername = treeLayerElem.attribute( QStringLiteral( "name" ) );
104 : 0 : QString newid = QgsMapLayer::generateId( layername );
105 : 0 : treeLayerElem.setAttribute( QStringLiteral( "id" ), newid );
106 : :
107 : : // Replace IDs for map layers
108 : 0 : const QDomNodeList ids = doc.elementsByTagName( QStringLiteral( "id" ) );
109 : 0 : QDomNode idnode = ids.at( 0 );
110 : 0 : for ( int j = 0; ! idnode.isNull() ; ++j )
111 : : {
112 : 0 : idnode = ids.at( j );
113 : 0 : QDomElement idElem = idnode.toElement();
114 : 0 : if ( idElem.text() == oldid )
115 : : {
116 : 0 : idElem.firstChild().setNodeValue( newid );
117 : 0 : }
118 : 0 : }
119 : :
120 : : // change layer IDs for vector joins
121 : 0 : const QDomNodeList vectorJoinNodes = doc.elementsByTagName( QStringLiteral( "join" ) ); // TODO: Find a better way of searching for vectorjoins, there might be other <join> elements within the project.
122 : 0 : for ( int j = 0; j < vectorJoinNodes.size(); ++j )
123 : : {
124 : 0 : QDomNode joinNode = vectorJoinNodes.at( j );
125 : 0 : QDomElement joinElement = joinNode.toElement();
126 : 0 : if ( joinElement.attribute( QStringLiteral( "joinLayerId" ) ) == oldid )
127 : : {
128 : 0 : joinNode.toElement().setAttribute( QStringLiteral( "joinLayerId" ), newid );
129 : 0 : }
130 : 0 : }
131 : :
132 : : // change IDs of dependencies
133 : 0 : const QDomNodeList dataDeps = doc.elementsByTagName( QStringLiteral( "dataDependencies" ) );
134 : 0 : for ( int i = 0; i < dataDeps.size(); i++ )
135 : : {
136 : 0 : QDomNodeList layers = dataDeps.at( i ).childNodes();
137 : 0 : for ( int j = 0; j < layers.size(); j++ )
138 : : {
139 : 0 : QDomElement elt = layers.at( j ).toElement();
140 : 0 : if ( elt.attribute( QStringLiteral( "id" ) ) == oldid )
141 : : {
142 : 0 : elt.setAttribute( QStringLiteral( "id" ), newid );
143 : 0 : }
144 : 0 : }
145 : 0 : }
146 : :
147 : : // Change IDs of widget config values
148 : 0 : QDomNodeList widgetConfig = doc.elementsByTagName( QStringLiteral( "editWidget" ) );
149 : 0 : for ( int i = 0; i < widgetConfig.size(); i++ )
150 : : {
151 : 0 : QDomNodeList config = widgetConfig.at( i ).childNodes();
152 : 0 : for ( int j = 0; j < config.size(); j++ )
153 : : {
154 : 0 : QDomNodeList optMap = config.at( j ).childNodes();
155 : 0 : for ( int z = 0; z < optMap.size(); z++ )
156 : : {
157 : 0 : QDomNodeList opts = optMap.at( z ).childNodes();
158 : 0 : for ( int k = 0; k < opts.size(); k++ )
159 : : {
160 : 0 : QDomElement opt = opts.at( k ).toElement();
161 : 0 : if ( opt.attribute( QStringLiteral( "value" ) ) == oldid )
162 : : {
163 : 0 : opt.setAttribute( QStringLiteral( "value" ), newid );
164 : 0 : }
165 : 0 : }
166 : 0 : }
167 : 0 : }
168 : 0 : }
169 : 0 : }
170 : :
171 : 0 : QDomElement layerTreeElem = doc.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
172 : 0 : bool loadInLegend = true;
173 : 0 : if ( !layerTreeElem.isNull() )
174 : : {
175 : 0 : root->readChildrenFromXml( layerTreeElem, context );
176 : 0 : loadInLegend = false;
177 : 0 : }
178 : :
179 : 0 : QList<QgsMapLayer *> layers = QgsLayerDefinition::loadLayerDefinitionLayersInternal( doc, context, errorMessage );
180 : :
181 : 0 : project->addMapLayers( layers, loadInLegend );
182 : :
183 : : // Now that all layers are loaded, refresh the vectorjoins to get the joined fields
184 : 0 : const auto constLayers = layers;
185 : 0 : for ( QgsMapLayer *layer : constLayers )
186 : : {
187 : 0 : if ( QgsVectorLayer *vlayer = qobject_cast< QgsVectorLayer * >( layer ) )
188 : : {
189 : 0 : vlayer->resolveReferences( project );
190 : 0 : }
191 : : }
192 : :
193 : 0 : root->resolveReferences( project );
194 : :
195 : 0 : QList<QgsLayerTreeNode *> nodes = root->children();
196 : 0 : root->abandonChildren();
197 : 0 : delete root;
198 : :
199 : 0 : rootGroup->insertChildNodes( -1, nodes );
200 : :
201 : : return true;
202 : 0 : }
203 : :
204 : 0 : bool QgsLayerDefinition::exportLayerDefinition( QString path, const QList<QgsLayerTreeNode *> &selectedTreeNodes, QString &errorMessage )
205 : : {
206 : 0 : if ( !path.endsWith( QLatin1String( ".qlr" ) ) )
207 : 0 : path = path.append( ".qlr" );
208 : :
209 : 0 : QFile file( path );
210 : :
211 : 0 : if ( !file.open( QFile::WriteOnly | QFile::Truncate ) )
212 : : {
213 : 0 : errorMessage = file.errorString();
214 : 0 : return false;
215 : : }
216 : :
217 : 0 : QgsReadWriteContext context;
218 : 0 : bool writeAbsolutePath = QgsProject::instance()->readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
219 : 0 : context.setPathResolver( QgsPathResolver( writeAbsolutePath ? QString() : path ) );
220 : :
221 : 0 : QDomDocument doc( QStringLiteral( "qgis-layer-definition" ) );
222 : 0 : if ( !exportLayerDefinition( doc, selectedTreeNodes, errorMessage, context ) )
223 : 0 : return false;
224 : :
225 : 0 : QTextStream qlayerstream( &file );
226 : 0 : doc.save( qlayerstream, 2 );
227 : 0 : return true;
228 : 0 : }
229 : :
230 : 0 : bool QgsLayerDefinition::exportLayerDefinition( QDomDocument doc, const QList<QgsLayerTreeNode *> &selectedTreeNodes, QString &errorMessage, const QgsReadWriteContext &context )
231 : : {
232 : 0 : Q_UNUSED( errorMessage )
233 : 0 : QDomElement qgiselm = doc.createElement( QStringLiteral( "qlr" ) );
234 : 0 : doc.appendChild( qgiselm );
235 : 0 : QList<QgsLayerTreeNode *> nodes = selectedTreeNodes;
236 : 0 : QgsLayerTreeGroup *root = new QgsLayerTreeGroup;
237 : 0 : const auto constNodes = nodes;
238 : 0 : for ( QgsLayerTreeNode *node : constNodes )
239 : : {
240 : 0 : QgsLayerTreeNode *newnode = node->clone();
241 : 0 : root->addChildNode( newnode );
242 : : }
243 : 0 : root->writeXml( qgiselm, context );
244 : :
245 : 0 : QDomElement layerselm = doc.createElement( QStringLiteral( "maplayers" ) );
246 : 0 : QList<QgsLayerTreeLayer *> layers = root->findLayers();
247 : 0 : const auto constLayers = layers;
248 : 0 : for ( QgsLayerTreeLayer *layer : constLayers )
249 : : {
250 : 0 : if ( ! layer->layer() )
251 : : {
252 : 0 : QgsDebugMsgLevel( QStringLiteral( "Not a valid map layer: skipping %1" ).arg( layer->name( ) ), 4 );
253 : 0 : continue;
254 : : }
255 : 0 : QDomElement layerelm = doc.createElement( QStringLiteral( "maplayer" ) );
256 : 0 : layer->layer()->writeLayerXml( layerelm, doc, context );
257 : 0 : layerselm.appendChild( layerelm );
258 : 0 : }
259 : 0 : qgiselm.appendChild( layerselm );
260 : : return true;
261 : 0 : }
262 : :
263 : 0 : QDomDocument QgsLayerDefinition::exportLayerDefinitionLayers( const QList<QgsMapLayer *> &layers, const QgsReadWriteContext &context )
264 : : {
265 : 0 : QDomDocument doc( QStringLiteral( "qgis-layer-definition" ) );
266 : 0 : QDomElement qgiselm = doc.createElement( QStringLiteral( "qlr" ) );
267 : 0 : doc.appendChild( qgiselm );
268 : 0 : QDomElement layerselm = doc.createElement( QStringLiteral( "maplayers" ) );
269 : 0 : const auto constLayers = layers;
270 : 0 : for ( QgsMapLayer *layer : constLayers )
271 : : {
272 : 0 : QDomElement layerelm = doc.createElement( QStringLiteral( "maplayer" ) );
273 : 0 : layer->writeLayerXml( layerelm, doc, context );
274 : 0 : layerselm.appendChild( layerelm );
275 : 0 : }
276 : 0 : qgiselm.appendChild( layerselm );
277 : 0 : return doc;
278 : 0 : }
279 : :
280 : 0 : QList<QgsMapLayer *> QgsLayerDefinition::loadLayerDefinitionLayers( QDomDocument &document, QgsReadWriteContext &context )
281 : : {
282 : 0 : QString errorMessage;
283 : 0 : return loadLayerDefinitionLayersInternal( document, context, errorMessage );
284 : 0 : }
285 : :
286 : 0 : QList<QgsMapLayer *> QgsLayerDefinition::loadLayerDefinitionLayersInternal( QDomDocument &document, QgsReadWriteContext &context, QString &errorMessage )
287 : : {
288 : 0 : QList<QgsMapLayer *> layers;
289 : 0 : QDomElement layerElem = document.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
290 : : // For QLR:
291 : 0 : if ( layerElem.isNull() )
292 : : {
293 : 0 : layerElem = document.documentElement().firstChildElement( QStringLiteral( "maplayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
294 : 0 : }
295 : :
296 : 0 : while ( ! layerElem.isNull() )
297 : : {
298 : 0 : const QString type = layerElem.attribute( QStringLiteral( "type" ) );
299 : 0 : QgsMapLayer *layer = nullptr;
300 : :
301 : 0 : bool ok = false;
302 : 0 : const QgsMapLayerType layerType = QgsMapLayerFactory::typeFromString( type, ok );
303 : 0 : if ( ok )
304 : : {
305 : 0 : switch ( layerType )
306 : : {
307 : : case QgsMapLayerType::VectorLayer:
308 : 0 : layer = new QgsVectorLayer();
309 : 0 : break;
310 : :
311 : : case QgsMapLayerType::RasterLayer:
312 : 0 : layer = new QgsRasterLayer();
313 : 0 : break;
314 : :
315 : : case QgsMapLayerType::PluginLayer:
316 : : {
317 : 0 : QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
318 : 0 : layer = QgsApplication::pluginLayerRegistry()->createLayer( typeName );
319 : : break;
320 : 0 : }
321 : :
322 : : case QgsMapLayerType::MeshLayer:
323 : 0 : layer = new QgsMeshLayer();
324 : 0 : break;
325 : :
326 : : case QgsMapLayerType::VectorTileLayer:
327 : 0 : layer = new QgsVectorTileLayer;
328 : 0 : break;
329 : :
330 : : case QgsMapLayerType::PointCloudLayer:
331 : 0 : layer = new QgsPointCloudLayer();
332 : 0 : break;
333 : :
334 : : case QgsMapLayerType::AnnotationLayer:
335 : 0 : break;
336 : : }
337 : 0 : }
338 : :
339 : 0 : if ( layer )
340 : : {
341 : : // always add the layer, even if the source is invalid -- this allows users to fix the source
342 : : // at a later stage and still retain all the layer properties intact
343 : 0 : layer->readLayerXml( layerElem, context );
344 : 0 : layers << layer;
345 : 0 : }
346 : : else
347 : : {
348 : 0 : errorMessage = QObject::tr( "Unsupported layer type: %1" ).arg( type );
349 : : }
350 : 0 : layerElem = layerElem.nextSiblingElement( QStringLiteral( "maplayer" ) );
351 : 0 : }
352 : 0 : return layers;
353 : 0 : }
354 : :
355 : 0 : QList<QgsMapLayer *> QgsLayerDefinition::loadLayerDefinitionLayers( const QString &qlrfile )
356 : : {
357 : 0 : QFile file( qlrfile );
358 : 0 : if ( !file.open( QIODevice::ReadOnly ) )
359 : : {
360 : 0 : QgsDebugMsg( QStringLiteral( "Can't open file" ) );
361 : 0 : return QList<QgsMapLayer *>();
362 : : }
363 : :
364 : 0 : QDomDocument doc;
365 : 0 : if ( !doc.setContent( &file ) )
366 : : {
367 : 0 : QgsDebugMsg( QStringLiteral( "Can't set content" ) );
368 : 0 : return QList<QgsMapLayer *>();
369 : : }
370 : :
371 : 0 : QgsReadWriteContext context;
372 : 0 : context.setPathResolver( QgsPathResolver( qlrfile ) );
373 : : //no project translator defined here
374 : 0 : return QgsLayerDefinition::loadLayerDefinitionLayers( doc, context );
375 : 0 : }
376 : :
377 : 0 : void QgsLayerDefinition::DependencySorter::init( const QDomDocument &doc )
378 : : {
379 : : // Determine a loading order of layers based on a graph of dependencies
380 : 0 : QMap< QString, QVector< QString > > dependencies;
381 : 0 : QStringList sortedLayers;
382 : 0 : QList< QPair<QString, QDomNode> > layersToSort;
383 : 0 : QStringList layerIds;
384 : :
385 : 0 : QDomElement layerElem = doc.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
386 : : // For QLR:
387 : 0 : if ( layerElem.isNull() )
388 : : {
389 : 0 : layerElem = doc.documentElement().firstChildElement( QStringLiteral( "maplayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
390 : 0 : }
391 : : // For tests (I don't know if there is a real use case for such a document except for test_qgslayerdefinition.py)
392 : 0 : if ( layerElem.isNull() )
393 : : {
394 : 0 : layerElem = doc.documentElement().firstChildElement( QStringLiteral( "maplayer" ) );
395 : 0 : }
396 : :
397 : 0 : const QDomElement &firstElement { layerElem };
398 : :
399 : 0 : QVector<QString> deps; //avoid expensive allocation for list for every iteration
400 : 0 : while ( !layerElem.isNull() )
401 : : {
402 : 0 : deps.resize( 0 ); // preserve capacity - don't use clear
403 : :
404 : 0 : QString id = layerElem.namedItem( QStringLiteral( "id" ) ).toElement().text();
405 : 0 : layerIds << id;
406 : :
407 : : // dependencies for this layer
408 : 0 : QDomElement layerDependenciesElem = layerElem.firstChildElement( QStringLiteral( "layerDependencies" ) );
409 : 0 : if ( !layerDependenciesElem.isNull() )
410 : : {
411 : 0 : QDomNodeList dependencyList = layerDependenciesElem.elementsByTagName( QStringLiteral( "layer" ) );
412 : 0 : for ( int j = 0; j < dependencyList.size(); ++j )
413 : : {
414 : 0 : QDomElement depElem = dependencyList.at( j ).toElement();
415 : 0 : deps << depElem.attribute( QStringLiteral( "id" ) );
416 : 0 : }
417 : 0 : }
418 : 0 : dependencies[id] = deps;
419 : :
420 : 0 : if ( deps.empty() )
421 : : {
422 : 0 : sortedLayers << id;
423 : 0 : mSortedLayerNodes << layerElem;
424 : 0 : mSortedLayerIds << id;
425 : 0 : }
426 : : else
427 : : {
428 : 0 : layersToSort << qMakePair( id, layerElem );
429 : : }
430 : 0 : layerElem = layerElem.nextSiblingElement( );
431 : 0 : }
432 : :
433 : : // check that all dependencies are present
434 : 0 : const auto constDependencies = dependencies;
435 : 0 : for ( const QVector< QString > &ids : constDependencies )
436 : : {
437 : 0 : const auto constIds = ids;
438 : 0 : for ( const QString &depId : constIds )
439 : : {
440 : 0 : if ( !dependencies.contains( depId ) )
441 : : {
442 : : // some dependencies are not satisfied
443 : 0 : mHasMissingDependency = true;
444 : 0 : layerElem = firstElement;
445 : 0 : while ( ! layerElem.isNull() )
446 : : {
447 : 0 : mSortedLayerNodes << layerElem;
448 : 0 : layerElem = layerElem.nextSiblingElement( );
449 : : }
450 : 0 : mSortedLayerIds = layerIds;
451 : 0 : return;
452 : : }
453 : : }
454 : 0 : }
455 : :
456 : : // cycles should be very rare, since layers with cyclic dependencies may only be created by
457 : : // manually modifying the project file
458 : 0 : mHasCycle = false;
459 : :
460 : 0 : while ( !layersToSort.empty() && !mHasCycle )
461 : : {
462 : 0 : QList< QPair<QString, QDomNode> >::iterator it = layersToSort.begin();
463 : 0 : while ( it != layersToSort.end() )
464 : : {
465 : 0 : QString idToSort = it->first;
466 : 0 : QDomNode node = it->second;
467 : 0 : mHasCycle = true;
468 : 0 : bool resolved = true;
469 : 0 : const auto deps { dependencies.value( idToSort ) };
470 : 0 : for ( const QString &dep : deps )
471 : : {
472 : 0 : if ( !sortedLayers.contains( dep ) )
473 : : {
474 : 0 : resolved = false;
475 : 0 : break;
476 : : }
477 : : }
478 : 0 : if ( resolved ) // dependencies for this layer are resolved
479 : : {
480 : 0 : sortedLayers << idToSort;
481 : 0 : mSortedLayerNodes << node;
482 : 0 : mSortedLayerIds << idToSort;
483 : 0 : it = layersToSort.erase( it ); // erase and go to the next
484 : 0 : mHasCycle = false;
485 : 0 : }
486 : : else
487 : : {
488 : 0 : ++it;
489 : : }
490 : 0 : }
491 : : }
492 : 0 : }
493 : :
494 : 0 : QgsLayerDefinition::DependencySorter::DependencySorter( const QDomDocument &doc )
495 : 0 : : mHasCycle( false )
496 : 0 : , mHasMissingDependency( false )
497 : : {
498 : 0 : init( doc );
499 : 0 : }
500 : :
501 : 0 : QgsLayerDefinition::DependencySorter::DependencySorter( const QString &fileName )
502 : 0 : : mHasCycle( false )
503 : 0 : , mHasMissingDependency( false )
504 : : {
505 : 0 : QString qgsProjectFile = fileName;
506 : 0 : QgsProjectArchive archive;
507 : 0 : if ( fileName.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
508 : : {
509 : 0 : archive.unzip( fileName );
510 : 0 : qgsProjectFile = archive.projectFile();
511 : 0 : }
512 : :
513 : 0 : QDomDocument doc;
514 : 0 : QFile pFile( qgsProjectFile );
515 : 0 : ( void )pFile.open( QIODevice::ReadOnly );
516 : 0 : ( void )doc.setContent( &pFile );
517 : 0 : init( doc );
518 : 0 : }
519 : :
520 : :
|