Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgscptcityarchive.cpp
3 : : ---------------------
4 : : begin : August 2012
5 : : copyright : (C) 2009 by Martin Dobias
6 : : copyright : (C) 2011 Radim Blazek
7 : : copyright : (C) 2012 by Etienne Tourigny
8 : : email : etourigny.dev at gmail.com
9 : : ***************************************************************************
10 : : * *
11 : : * This program is free software; you can redistribute it and/or modify *
12 : : * it under the terms of the GNU General Public License as published by *
13 : : * the Free Software Foundation; either version 2 of the License, or *
14 : : * (at your option) any later version. *
15 : : * *
16 : : ***************************************************************************/
17 : :
18 : : #include <QApplication>
19 : : #include <QDateTime>
20 : : #include <QDir>
21 : : #include <QFileInfo>
22 : : #include <QMenu>
23 : : #include <QMouseEvent>
24 : : #include <QTreeWidget>
25 : : #include <QTreeWidgetItem>
26 : : #include <QVector>
27 : : #include <QStyle>
28 : : #include <QDomDocument>
29 : : #include <QDomElement>
30 : :
31 : : #include "qgssettings.h"
32 : : #include "qgscptcityarchive.h"
33 : : #include "qgis.h"
34 : : #include "qgsdataprovider.h"
35 : : #include "qgslogger.h"
36 : : #include "qgsconfig.h"
37 : : #include "qgsmimedatautils.h"
38 : : #include "qgsapplication.h"
39 : : #include "qgssymbollayerutils.h"
40 : :
41 : : typedef QMap< QString, QgsCptCityArchive * > ArchiveRegistry;
42 : : typedef QMap< QString, QMap< QString, QString > > CopyingInfoMap;
43 : :
44 : 0 : Q_GLOBAL_STATIC( QString, sDefaultArchiveName )
45 : 0 : Q_GLOBAL_STATIC( ArchiveRegistry, sArchiveRegistry )
46 : 0 : Q_GLOBAL_STATIC( CopyingInfoMap, sCopyingInfoMap )
47 : :
48 : 0 : QMap< QString, QgsCptCityArchive * > QgsCptCityArchive::archiveRegistry()
49 : : {
50 : 0 : return *sArchiveRegistry();
51 : : }
52 : :
53 : 0 : QgsCptCityArchive::QgsCptCityArchive( const QString &archiveName, const QString &baseDir )
54 : 0 : : mArchiveName( archiveName )
55 : 0 : , mBaseDir( baseDir )
56 : : {
57 : 0 : QgsDebugMsg( "archiveName = " + archiveName + " baseDir = " + baseDir );
58 : :
59 : : // make Author items
60 : 0 : QgsCptCityDirectoryItem *dirItem = nullptr;
61 : 0 : const auto constEntryList = QDir( mBaseDir ).entryList( QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name );
62 : 0 : for ( const QString &path : constEntryList )
63 : : {
64 : 0 : if ( path == QLatin1String( "selections" ) )
65 : 0 : continue;
66 : 0 : QgsDebugMsg( "path= " + path );
67 : 0 : dirItem = new QgsCptCityDirectoryItem( nullptr, QFileInfo( path ).baseName(), path );
68 : 0 : if ( dirItem->isValid() )
69 : 0 : mRootItems << dirItem;
70 : : else
71 : 0 : delete dirItem;
72 : : }
73 : :
74 : : // make selection items
75 : 0 : QgsCptCitySelectionItem *selItem = nullptr;
76 : 0 : QDir seldir( mBaseDir + '/' + "selections" );
77 : 0 : QgsDebugMsg( "populating selection from " + seldir.path() );
78 : 0 : const QStringList fileList = seldir.entryList( QStringList() << QStringLiteral( "*.xml" ), QDir::Files );
79 : 0 : for ( const QString &selfile : fileList )
80 : : {
81 : 0 : QgsDebugMsg( "file= " + seldir.path() + '/' + selfile );
82 : 0 : selItem = new QgsCptCitySelectionItem( nullptr, QFileInfo( selfile ).baseName(),
83 : 0 : seldir.dirName() + '/' + selfile );
84 : : //TODO remove item if there are no children (e.g. esri in qgis-sel)
85 : 0 : if ( selItem->isValid() )
86 : 0 : mSelectionItems << selItem;
87 : : else
88 : 0 : delete selItem;
89 : : }
90 : :
91 : : // make "All Ramps items" (which will contain all ramps without hierarchy)
92 : 0 : QgsCptCityAllRampsItem *allRampsItem = nullptr;
93 : 0 : allRampsItem = new QgsCptCityAllRampsItem( nullptr, QObject::tr( "All Ramps" ),
94 : 0 : mRootItems );
95 : 0 : mRootItems.prepend( allRampsItem );
96 : 0 : allRampsItem = new QgsCptCityAllRampsItem( nullptr, QObject::tr( "All Ramps" ),
97 : 0 : mSelectionItems );
98 : 0 : mSelectionItems.prepend( allRampsItem );
99 : 0 : }
100 : :
101 : 0 : QgsCptCityArchive::~QgsCptCityArchive()
102 : : {
103 : 0 : const auto constMRootItems = mRootItems;
104 : 0 : for ( QgsCptCityDataItem *item : constMRootItems )
105 : 0 : delete item;
106 : 0 : const auto constMSelectionItems = mSelectionItems;
107 : 0 : for ( QgsCptCityDataItem *item : constMSelectionItems )
108 : 0 : delete item;
109 : 0 : mRootItems.clear();
110 : 0 : mSelectionItems.clear();
111 : 0 : }
112 : :
113 : 0 : QString QgsCptCityArchive::baseDir() const
114 : : {
115 : : // if was set with setBaseDir, return that value
116 : : // else return global default
117 : 0 : if ( ! mBaseDir.isNull() )
118 : 0 : return mBaseDir;
119 : : else
120 : 0 : return QgsCptCityArchive::defaultBaseDir();
121 : 0 : }
122 : :
123 : 0 : QString QgsCptCityArchive::baseDir( QString archiveName )
124 : : {
125 : : // search for matching archive in the registry
126 : 0 : if ( archiveName.isNull() )
127 : 0 : archiveName = DEFAULT_CPTCITY_ARCHIVE;
128 : 0 : if ( QgsCptCityArchive *archive = sArchiveRegistry()->value( archiveName, nullptr ) )
129 : 0 : return archive->baseDir();
130 : : else
131 : 0 : return defaultBaseDir();
132 : 0 : }
133 : :
134 : 0 : QString QgsCptCityArchive::defaultBaseDir()
135 : : {
136 : 0 : QString baseDir, archiveName;
137 : 0 : QgsSettings settings;
138 : :
139 : : // use CptCity/baseDir setting if set, default is user dir
140 : 0 : baseDir = settings.value( QStringLiteral( "CptCity/baseDir" ),
141 : 0 : QString( QgsApplication::pkgDataPath() + "/resources" ) ).toString();
142 : : // sub-dir defaults to cpt-city
143 : 0 : archiveName = settings.value( QStringLiteral( "CptCity/archiveName" ), DEFAULT_CPTCITY_ARCHIVE ).toString();
144 : :
145 : 0 : return baseDir + '/' + archiveName;
146 : 0 : }
147 : :
148 : :
149 : 0 : QString QgsCptCityArchive::findFileName( const QString &target, const QString &startDir, const QString &baseDir )
150 : : {
151 : : // QgsDebugMsg( "target= " + target + " startDir= " + startDir + " baseDir= " + baseDir );
152 : :
153 : 0 : if ( startDir.isEmpty() || ! startDir.startsWith( baseDir ) )
154 : 0 : return QString();
155 : :
156 : 0 : QDir dir = QDir( startDir );
157 : : //todo test when
158 : 0 : while ( ! dir.exists( target ) && dir.path() != baseDir )
159 : : {
160 : 0 : if ( ! dir.cdUp() )
161 : 0 : break;
162 : : }
163 : 0 : if ( ! dir.exists( target ) )
164 : 0 : return QString();
165 : : else
166 : 0 : return dir.path() + '/' + target;
167 : 0 : }
168 : :
169 : :
170 : 0 : QString QgsCptCityArchive::copyingFileName( const QString &path ) const
171 : : {
172 : 0 : return QgsCptCityArchive::findFileName( QStringLiteral( "COPYING.xml" ),
173 : 0 : baseDir() + '/' + path, baseDir() );
174 : 0 : }
175 : :
176 : 0 : QString QgsCptCityArchive::descFileName( const QString &path ) const
177 : : {
178 : 0 : return QgsCptCityArchive::findFileName( QStringLiteral( "DESC.xml" ),
179 : 0 : baseDir() + '/' + path, baseDir() );
180 : 0 : }
181 : :
182 : 0 : QgsStringMap QgsCptCityArchive::copyingInfo( const QString &fileName )
183 : : {
184 : 0 : QgsStringMap copyingMap;
185 : :
186 : 0 : if ( fileName.isNull() )
187 : 0 : return copyingMap;
188 : :
189 : 0 : if ( sCopyingInfoMap()->contains( fileName ) )
190 : : {
191 : 0 : QgsDebugMsg( "found copying info in copyingInfoMap, file = " + fileName );
192 : 0 : return sCopyingInfoMap()->value( fileName );
193 : : }
194 : :
195 : 0 : QgsDebugMsg( "fileName = " + fileName );
196 : :
197 : : // import xml file
198 : 0 : QFile f( fileName );
199 : 0 : if ( !f.open( QFile::ReadOnly ) )
200 : : {
201 : 0 : QgsDebugMsg( "Couldn't open xml file: " + fileName );
202 : 0 : return copyingMap;
203 : : }
204 : :
205 : : // parse the document
206 : 0 : QDomDocument doc( QStringLiteral( "license" ) );
207 : 0 : if ( !doc.setContent( &f ) )
208 : : {
209 : 0 : f.close();
210 : 0 : QgsDebugMsg( "Couldn't parse xml file: " + fileName );
211 : 0 : return copyingMap;
212 : : }
213 : 0 : f.close();
214 : :
215 : : // get root element
216 : 0 : QDomElement docElem = doc.documentElement();
217 : 0 : if ( docElem.tagName() != QLatin1String( "copying" ) )
218 : : {
219 : 0 : QgsDebugMsg( "Incorrect root tag: " + docElem.tagName() );
220 : 0 : return copyingMap;
221 : : }
222 : :
223 : : // load author information
224 : 0 : QDomElement authorsElement = docElem.firstChildElement( QStringLiteral( "authors" ) );
225 : 0 : if ( authorsElement.isNull() )
226 : : {
227 : 0 : QgsDebugMsg( QStringLiteral( "authors tag missing" ) );
228 : 0 : }
229 : : else
230 : : {
231 : 0 : QDomElement e = authorsElement.firstChildElement();
232 : 0 : QStringList authors;
233 : 0 : while ( ! e.isNull() )
234 : : {
235 : 0 : if ( e.tagName() == QLatin1String( "author" ) )
236 : : {
237 : 0 : if ( ! e.firstChildElement( QStringLiteral( "name" ) ).isNull() )
238 : 0 : authors << e.firstChildElement( QStringLiteral( "name" ) ).text().simplified();
239 : : // org???
240 : 0 : }
241 : 0 : e = e.nextSiblingElement();
242 : : }
243 : 0 : copyingMap[ QStringLiteral( "authors" )] = authors.join( QLatin1String( ", " ) );
244 : 0 : }
245 : :
246 : : // load license information
247 : 0 : QDomElement licenseElement = docElem.firstChildElement( QStringLiteral( "license" ) );
248 : 0 : if ( licenseElement.isNull() )
249 : : {
250 : 0 : QgsDebugMsg( QStringLiteral( "license tag missing" ) );
251 : 0 : }
252 : : else
253 : : {
254 : 0 : QDomElement e = licenseElement.firstChildElement( QStringLiteral( "informal" ) );
255 : 0 : if ( ! e.isNull() )
256 : 0 : copyingMap[ QStringLiteral( "license/informal" )] = e.text().simplified();
257 : 0 : e = licenseElement.firstChildElement( QStringLiteral( "year" ) );
258 : 0 : if ( ! e.isNull() )
259 : 0 : copyingMap[ QStringLiteral( "license/year" )] = e.text().simplified();
260 : 0 : e = licenseElement.firstChildElement( QStringLiteral( "text" ) );
261 : 0 : if ( ! e.isNull() && e.attribute( QStringLiteral( "href" ) ) != QString() )
262 : 0 : copyingMap[ QStringLiteral( "license/url" )] = e.attribute( QStringLiteral( "href" ) );
263 : 0 : }
264 : :
265 : : // load src information
266 : 0 : QDomElement element = docElem.firstChildElement( QStringLiteral( "src" ) );
267 : 0 : if ( element.isNull() )
268 : : {
269 : 0 : QgsDebugMsg( QStringLiteral( "src tag missing" ) );
270 : 0 : }
271 : : else
272 : : {
273 : 0 : QDomElement e = element.firstChildElement( QStringLiteral( "link" ) );
274 : 0 : if ( ! e.isNull() && e.attribute( QStringLiteral( "href" ) ) != QString() )
275 : 0 : copyingMap[ QStringLiteral( "src/link" )] = e.attribute( QStringLiteral( "href" ) );
276 : 0 : }
277 : :
278 : : // save copyingMap for further access
279 : 0 : ( *sCopyingInfoMap() )[ fileName ] = copyingMap;
280 : 0 : return copyingMap;
281 : 0 : }
282 : :
283 : 0 : QgsStringMap QgsCptCityArchive::description( const QString &fileName )
284 : : {
285 : 0 : QgsStringMap descMap;
286 : :
287 : 0 : QgsDebugMsg( "description fileName = " + fileName );
288 : :
289 : 0 : QFile f( fileName );
290 : 0 : if ( ! f.open( QFile::ReadOnly ) )
291 : : {
292 : 0 : QgsDebugMsg( "description file " + fileName + " ] does not exist" );
293 : 0 : return descMap;
294 : : }
295 : :
296 : : // parse the document
297 : 0 : QString errMsg;
298 : 0 : QDomDocument doc( QStringLiteral( "description" ) );
299 : 0 : if ( !doc.setContent( &f, &errMsg ) )
300 : : {
301 : 0 : f.close();
302 : 0 : QgsDebugMsg( "Couldn't parse file " + fileName + " : " + errMsg );
303 : 0 : return descMap;
304 : : }
305 : 0 : f.close();
306 : :
307 : : // read description
308 : 0 : QDomElement docElem = doc.documentElement();
309 : 0 : if ( docElem.tagName() != QLatin1String( "description" ) )
310 : : {
311 : 0 : QgsDebugMsg( "Incorrect root tag: " + docElem.tagName() );
312 : 0 : return descMap;
313 : : }
314 : : // should we make sure the <dir> tag is OK?
315 : :
316 : 0 : QDomElement e = docElem.firstChildElement( QStringLiteral( "name" ) );
317 : 0 : if ( e.isNull() )
318 : : {
319 : 0 : QgsDebugMsg( QStringLiteral( "name tag missing" ) );
320 : 0 : }
321 : 0 : descMap[ QStringLiteral( "name" )] = e.text().simplified();
322 : 0 : e = docElem.firstChildElement( QStringLiteral( "full" ) );
323 : 0 : if ( e.isNull() )
324 : : {
325 : 0 : QgsDebugMsg( QStringLiteral( "full tag missing" ) );
326 : 0 : }
327 : 0 : descMap[ QStringLiteral( "full" )] = e.text().simplified();
328 : :
329 : 0 : return descMap;
330 : 0 : }
331 : :
332 : 0 : QMap< double, QPair<QColor, QColor> >QgsCptCityArchive::gradientColorMap( const QString &fileName )
333 : : {
334 : 0 : QMap< double, QPair<QColor, QColor> > colorMap;
335 : :
336 : : // import xml file
337 : 0 : QFile f( fileName );
338 : 0 : if ( !f.open( QFile::ReadOnly ) )
339 : : {
340 : 0 : QgsDebugMsg( "Couldn't open SVG file: " + fileName );
341 : 0 : return colorMap;
342 : : }
343 : :
344 : : // parse the document
345 : 0 : QDomDocument doc( QStringLiteral( "gradient" ) );
346 : 0 : if ( !doc.setContent( &f ) )
347 : : {
348 : 0 : f.close();
349 : 0 : QgsDebugMsg( "Couldn't parse SVG file: " + fileName );
350 : 0 : return colorMap;
351 : : }
352 : 0 : f.close();
353 : :
354 : 0 : QDomElement docElem = doc.documentElement();
355 : :
356 : 0 : if ( docElem.tagName() != QLatin1String( "svg" ) )
357 : : {
358 : 0 : QgsDebugMsg( "Incorrect root tag: " + docElem.tagName() );
359 : 0 : return colorMap;
360 : : }
361 : :
362 : : // load color ramp from first linearGradient node
363 : 0 : QDomElement rampsElement = docElem.firstChildElement( QStringLiteral( "linearGradient" ) );
364 : 0 : if ( rampsElement.isNull() )
365 : : {
366 : 0 : QDomNodeList nodeList = docElem.elementsByTagName( QStringLiteral( "linearGradient" ) );
367 : 0 : if ( ! nodeList.isEmpty() )
368 : 0 : rampsElement = nodeList.at( 0 ).toElement();
369 : 0 : }
370 : 0 : if ( rampsElement.isNull() )
371 : : {
372 : 0 : QgsDebugMsg( QStringLiteral( "linearGradient tag missing" ) );
373 : 0 : return colorMap;
374 : : }
375 : :
376 : : // loop for all stop tags
377 : 0 : QDomElement e = rampsElement.firstChildElement();
378 : :
379 : 0 : while ( !e.isNull() )
380 : : {
381 : 0 : if ( e.tagName() == QLatin1String( "stop" ) )
382 : : {
383 : : //todo integrate this into symbollayerutils, keep here for now...
384 : : double offset;
385 : 0 : QString offsetStr = e.attribute( QStringLiteral( "offset" ) ); // offset="50.00%" | offset="0.5"
386 : 0 : QString colorStr = e.attribute( QStringLiteral( "stop-color" ), QString() ); // stop-color="rgb(222,235,247)"
387 : 0 : QString opacityStr = e.attribute( QStringLiteral( "stop-opacity" ), QStringLiteral( "1.0" ) ); // stop-opacity="1.0000"
388 : 0 : if ( offsetStr.endsWith( '%' ) )
389 : 0 : offset = offsetStr.remove( offsetStr.size() - 1, 1 ).toDouble() / 100.0;
390 : : else
391 : 0 : offset = offsetStr.toDouble();
392 : :
393 : : // QColor color( 255, 0, 0 ); // red color as a warning :)
394 : 0 : QColor color = QgsSymbolLayerUtils::parseColor( colorStr );
395 : 0 : if ( color != QColor() )
396 : : {
397 : 0 : int alpha = opacityStr.toDouble() * 255; // test
398 : 0 : color.setAlpha( alpha );
399 : 0 : if ( colorMap.contains( offset ) )
400 : 0 : colorMap[offset].second = color;
401 : : else
402 : 0 : colorMap[offset] = qMakePair( color, color );
403 : 0 : }
404 : : else
405 : : {
406 : 0 : QgsDebugMsg( QStringLiteral( "at offset=%1 invalid color" ).arg( offset ) );
407 : : }
408 : 0 : }
409 : : else
410 : : {
411 : 0 : QgsDebugMsg( "unknown tag: " + e.tagName() );
412 : : }
413 : :
414 : 0 : e = e.nextSiblingElement();
415 : : }
416 : :
417 : 0 : return colorMap;
418 : 0 : }
419 : :
420 : 0 : bool QgsCptCityArchive::isEmpty()
421 : : {
422 : 0 : return ( mRootItems.isEmpty() );
423 : : }
424 : :
425 : :
426 : 0 : QgsCptCityArchive *QgsCptCityArchive::defaultArchive()
427 : : {
428 : 0 : QgsSettings settings;
429 : 0 : *sDefaultArchiveName() = settings.value( QStringLiteral( "CptCity/archiveName" ), DEFAULT_CPTCITY_ARCHIVE ).toString();
430 : 0 : if ( sArchiveRegistry()->contains( *sDefaultArchiveName() ) )
431 : 0 : return sArchiveRegistry()->value( *sDefaultArchiveName() );
432 : : else
433 : 0 : return nullptr;
434 : 0 : }
435 : :
436 : 0 : void QgsCptCityArchive::initArchive( const QString &archiveName, const QString &archiveBaseDir )
437 : : {
438 : 0 : QgsDebugMsg( "archiveName = " + archiveName + " archiveBaseDir = " + archiveBaseDir );
439 : 0 : QgsCptCityArchive *archive = new QgsCptCityArchive( archiveName, archiveBaseDir );
440 : 0 : if ( sArchiveRegistry()->contains( archiveName ) )
441 : 0 : delete ( *sArchiveRegistry() )[ archiveName ];
442 : 0 : ( *sArchiveRegistry() )[ archiveName ] = archive;
443 : 0 : }
444 : :
445 : 0 : void QgsCptCityArchive::initDefaultArchive()
446 : : {
447 : 0 : QgsSettings settings;
448 : : // use CptCity/baseDir setting if set, default is user dir
449 : 0 : QString baseDir = settings.value( QStringLiteral( "CptCity/baseDir" ),
450 : 0 : QString( QgsApplication::pkgDataPath() + "/resources" ) ).toString();
451 : : // sub-dir defaults to
452 : 0 : QString defArchiveName = settings.value( QStringLiteral( "CptCity/archiveName" ), DEFAULT_CPTCITY_ARCHIVE ).toString();
453 : :
454 : 0 : if ( ! sArchiveRegistry()->contains( defArchiveName ) )
455 : 0 : initArchive( defArchiveName, baseDir + '/' + defArchiveName );
456 : 0 : }
457 : :
458 : 0 : void QgsCptCityArchive::initArchives( bool loadAll )
459 : : {
460 : 0 : QgsStringMap archivesMap;
461 : 0 : QString baseDir, defArchiveName;
462 : 0 : QgsSettings settings;
463 : :
464 : : // use CptCity/baseDir setting if set, default is user dir
465 : 0 : baseDir = settings.value( QStringLiteral( "CptCity/baseDir" ),
466 : 0 : QString( QgsApplication::pkgDataPath() + "/resources" ) ).toString();
467 : : // sub-dir defaults to
468 : 0 : defArchiveName = settings.value( QStringLiteral( "CptCity/archiveName" ), DEFAULT_CPTCITY_ARCHIVE ).toString();
469 : :
470 : 0 : QgsDebugMsg( "baseDir= " + baseDir + " defArchiveName= " + defArchiveName );
471 : 0 : if ( loadAll )
472 : : {
473 : 0 : QDir dir( baseDir );
474 : 0 : const QStringList fileList = dir.entryList( QStringList() << QStringLiteral( "cpt-city*" ), QDir::Dirs );
475 : 0 : for ( const QString &entry : fileList )
476 : : {
477 : 0 : if ( QFile::exists( baseDir + '/' + entry + "/VERSION.xml" ) )
478 : 0 : archivesMap[ entry ] = baseDir + '/' + entry;
479 : : }
480 : 0 : }
481 : : else
482 : : {
483 : 0 : archivesMap[ defArchiveName ] = baseDir + '/' + defArchiveName;
484 : : }
485 : :
486 : 0 : for ( QgsStringMap::iterator it = archivesMap.begin();
487 : 0 : it != archivesMap.end(); ++it )
488 : : {
489 : 0 : if ( QDir( it.value() ).exists() )
490 : 0 : QgsCptCityArchive::initArchive( it.key(), it.value() );
491 : : else
492 : : {
493 : 0 : QgsDebugMsg( QStringLiteral( "not loading archive [%1] because dir %2 does not exist " ).arg( it.key(), it.value() ) );
494 : : }
495 : 0 : }
496 : 0 : *sDefaultArchiveName() = defArchiveName;
497 : 0 : }
498 : :
499 : 0 : void QgsCptCityArchive::clearArchives()
500 : : {
501 : 0 : qDeleteAll( *sArchiveRegistry() );
502 : 0 : sArchiveRegistry()->clear();
503 : 0 : }
504 : :
505 : :
506 : : // --------
507 : :
508 : 0 : QgsCptCityDataItem::QgsCptCityDataItem( QgsCptCityDataItem::Type type, QgsCptCityDataItem *parent,
509 : : const QString &name, const QString &path )
510 : : // Do not pass parent to QObject, Qt would delete this when parent is deleted
511 : 0 : : mType( type )
512 : 0 : , mParent( parent )
513 : 0 : , mPopulated( false )
514 : 0 : , mName( name )
515 : 0 : , mPath( path )
516 : 0 : , mValid( true )
517 : 0 : {
518 : 0 : }
519 : :
520 : 0 : QVector<QgsCptCityDataItem *> QgsCptCityDataItem::createChildren()
521 : : {
522 : 0 : QVector<QgsCptCityDataItem *> children;
523 : 0 : return children;
524 : 0 : }
525 : :
526 : 0 : void QgsCptCityDataItem::populate()
527 : : {
528 : 0 : if ( mPopulated )
529 : 0 : return;
530 : :
531 : 0 : QgsDebugMsg( "mPath = " + mPath );
532 : :
533 : 0 : QApplication::setOverrideCursor( Qt::WaitCursor );
534 : :
535 : 0 : QVector<QgsCptCityDataItem *> children = createChildren();
536 : 0 : const auto constChildren = children;
537 : 0 : for ( QgsCptCityDataItem *child : constChildren )
538 : : {
539 : : // initialization, do not refresh! That would result in infinite loop (beginInsertItems->rowCount->populate)
540 : 0 : addChildItem( child );
541 : : }
542 : 0 : mPopulated = true;
543 : :
544 : 0 : QApplication::restoreOverrideCursor();
545 : 0 : }
546 : :
547 : 0 : int QgsCptCityDataItem::rowCount()
548 : : {
549 : : // if ( !mPopulated )
550 : : // populate();
551 : 0 : return mChildren.size();
552 : : }
553 : :
554 : 0 : int QgsCptCityDataItem::leafCount() const
555 : : {
556 : 0 : if ( !mPopulated )
557 : 0 : return 0;
558 : :
559 : 0 : int count = 0;
560 : 0 : const auto constMChildren = mChildren;
561 : 0 : for ( QgsCptCityDataItem *child : constMChildren )
562 : : {
563 : 0 : if ( child )
564 : 0 : count += child->leafCount();
565 : : }
566 : 0 : return count;
567 : 0 : }
568 : :
569 : :
570 : 0 : bool QgsCptCityDataItem::hasChildren()
571 : : {
572 : 0 : return ( mPopulated ? !mChildren.isEmpty() : true );
573 : : }
574 : :
575 : 0 : void QgsCptCityDataItem::addChildItem( QgsCptCityDataItem *child, bool refresh )
576 : : {
577 : 0 : QgsDebugMsg( QStringLiteral( "add child #%1 - %2 - %3" ).arg( mChildren.size() ).arg( child->mName ).arg( child->mType ) );
578 : :
579 : : int i;
580 : 0 : if ( type() == ColorRamp )
581 : : {
582 : 0 : for ( i = 0; i < mChildren.size(); i++ )
583 : : {
584 : : // sort items by type, so directories are after data items
585 : 0 : if ( mChildren.at( i )->mType == child->mType &&
586 : 0 : mChildren.at( i )->mName.localeAwareCompare( child->mName ) >= 0 )
587 : 0 : break;
588 : 0 : }
589 : 0 : }
590 : : else
591 : : {
592 : 0 : for ( i = 0; i < mChildren.size(); i++ )
593 : : {
594 : 0 : if ( mChildren.at( i )->mName.localeAwareCompare( child->mName ) >= 0 )
595 : 0 : break;
596 : 0 : }
597 : : }
598 : :
599 : 0 : if ( refresh )
600 : 0 : emit beginInsertItems( this, i, i );
601 : :
602 : 0 : mChildren.insert( i, child );
603 : :
604 : 0 : connect( child, &QgsCptCityDataItem::beginInsertItems, this, &QgsCptCityDataItem::beginInsertItems );
605 : 0 : connect( child, &QgsCptCityDataItem::endInsertItems, this, &QgsCptCityDataItem::endInsertItems );
606 : 0 : connect( child, &QgsCptCityDataItem::beginRemoveItems, this, &QgsCptCityDataItem::beginRemoveItems );
607 : 0 : connect( child, &QgsCptCityDataItem::endRemoveItems, this, &QgsCptCityDataItem::endRemoveItems );
608 : :
609 : 0 : if ( refresh )
610 : 0 : emit endInsertItems();
611 : 0 : }
612 : 0 : void QgsCptCityDataItem::deleteChildItem( QgsCptCityDataItem *child )
613 : : {
614 : : // QgsDebugMsg( "mName = " + child->mName );
615 : 0 : int i = mChildren.indexOf( child );
616 : : Q_ASSERT( i >= 0 );
617 : 0 : emit beginRemoveItems( this, i, i );
618 : 0 : mChildren.remove( i );
619 : 0 : delete child;
620 : 0 : emit endRemoveItems();
621 : 0 : }
622 : :
623 : 0 : QgsCptCityDataItem *QgsCptCityDataItem::removeChildItem( QgsCptCityDataItem *child )
624 : : {
625 : : // QgsDebugMsg( "mName = " + child->mName );
626 : 0 : int i = mChildren.indexOf( child );
627 : : Q_ASSERT( i >= 0 );
628 : 0 : emit beginRemoveItems( this, i, i );
629 : 0 : mChildren.remove( i );
630 : 0 : emit endRemoveItems();
631 : 0 : disconnect( child, &QgsCptCityDataItem::beginInsertItems, this, &QgsCptCityDataItem::beginInsertItems );
632 : 0 : disconnect( child, &QgsCptCityDataItem::endInsertItems, this, &QgsCptCityDataItem::endInsertItems );
633 : 0 : disconnect( child, &QgsCptCityDataItem::beginRemoveItems, this, &QgsCptCityDataItem::beginRemoveItems );
634 : 0 : disconnect( child, &QgsCptCityDataItem::endRemoveItems, this, &QgsCptCityDataItem::endRemoveItems );
635 : 0 : child->setParent( nullptr );
636 : 0 : return child;
637 : : }
638 : :
639 : 0 : int QgsCptCityDataItem::findItem( QVector<QgsCptCityDataItem *> items, QgsCptCityDataItem *item )
640 : : {
641 : 0 : for ( int i = 0; i < items.size(); i++ )
642 : : {
643 : : // QgsDebugMsg( QString::number( i ) + " : " + items[i]->mPath + " x " + item->mPath );
644 : 0 : if ( items[i]->equal( item ) )
645 : 0 : return i;
646 : 0 : }
647 : 0 : return -1;
648 : 0 : }
649 : :
650 : 0 : void QgsCptCityDataItem::refresh()
651 : : {
652 : 0 : QgsDebugMsg( "mPath = " + mPath );
653 : :
654 : 0 : QApplication::setOverrideCursor( Qt::WaitCursor );
655 : :
656 : 0 : QVector<QgsCptCityDataItem *> items = createChildren();
657 : :
658 : : // Remove no more present items
659 : 0 : QVector<QgsCptCityDataItem *> remove;
660 : 0 : const auto constMChildren = mChildren;
661 : 0 : for ( QgsCptCityDataItem *child : constMChildren )
662 : : {
663 : 0 : if ( findItem( items, child ) >= 0 )
664 : 0 : continue;
665 : 0 : remove.append( child );
666 : : }
667 : 0 : const auto constRemove = remove;
668 : 0 : for ( QgsCptCityDataItem *child : constRemove )
669 : : {
670 : 0 : deleteChildItem( child );
671 : : }
672 : :
673 : : // Add new items
674 : 0 : const auto constItems = items;
675 : 0 : for ( QgsCptCityDataItem *item : constItems )
676 : : {
677 : : // Is it present in children?
678 : 0 : if ( findItem( mChildren, item ) >= 0 )
679 : : {
680 : 0 : delete item;
681 : 0 : continue;
682 : : }
683 : 0 : addChildItem( item, true );
684 : : }
685 : :
686 : 0 : QApplication::restoreOverrideCursor();
687 : 0 : }
688 : :
689 : 0 : bool QgsCptCityDataItem::equal( const QgsCptCityDataItem *other )
690 : : {
691 : 0 : return ( metaObject()->className() == other->metaObject()->className() &&
692 : 0 : mPath == other->path() );
693 : : }
694 : :
695 : : // ---------------------------------------------------------------------
696 : :
697 : 0 : QgsCptCityColorRampItem::QgsCptCityColorRampItem( QgsCptCityDataItem *parent,
698 : : const QString &name, const QString &path, const QString &variantName, bool initialize )
699 : 0 : : QgsCptCityDataItem( ColorRamp, parent, name, path )
700 : 0 : , mInitialized( false )
701 : 0 : , mRamp( path, variantName, false )
702 : 0 : {
703 : : // QgsDebugMsg( "name= " + name + " path= " + path );
704 : 0 : mPopulated = true;
705 : 0 : if ( initialize )
706 : 0 : init();
707 : 0 : }
708 : :
709 : 0 : QgsCptCityColorRampItem::QgsCptCityColorRampItem( QgsCptCityDataItem *parent,
710 : : const QString &name, const QString &path, const QStringList &variantList, bool initialize )
711 : 0 : : QgsCptCityDataItem( ColorRamp, parent, name, path )
712 : 0 : , mInitialized( false )
713 : 0 : , mRamp( path, variantList, QString(), false )
714 : 0 : {
715 : : // QgsDebugMsg( "name= " + name + " path= " + path );
716 : 0 : mPopulated = true;
717 : 0 : if ( initialize )
718 : 0 : init();
719 : 0 : }
720 : :
721 : : // TODO only load file when icon is requested...
722 : 0 : void QgsCptCityColorRampItem::init()
723 : : {
724 : 0 : if ( mInitialized )
725 : 0 : return;
726 : 0 : mInitialized = true;
727 : :
728 : 0 : QgsDebugMsg( "path = " + path() );
729 : :
730 : : // make preview from variant if exists
731 : 0 : QStringList variantList = mRamp.variantList();
732 : 0 : if ( mRamp.variantName().isNull() && ! variantList.isEmpty() )
733 : 0 : mRamp.setVariantName( variantList[ variantList.count() / 2 ] );
734 : :
735 : 0 : mRamp.loadFile();
736 : :
737 : : // is this item valid? this might fail when there are variants, check
738 : 0 : if ( ! QFile::exists( mRamp.fileName() ) )
739 : 0 : mValid = false;
740 : : else
741 : 0 : mValid = true;
742 : :
743 : : // load file and set info
744 : 0 : if ( mRamp.count() > 0 )
745 : : {
746 : 0 : if ( variantList.isEmpty() )
747 : : {
748 : 0 : int count = mRamp.count();
749 : 0 : if ( mRamp.isDiscrete() )
750 : 0 : count--;
751 : 0 : mInfo = QString::number( count ) + ' ' + tr( "colors" ) + " - ";
752 : 0 : if ( mRamp.isDiscrete() )
753 : 0 : mInfo += tr( "discrete" );
754 : : else
755 : : {
756 : 0 : if ( !mRamp.hasMultiStops() )
757 : 0 : mInfo += tr( "continuous" );
758 : : else
759 : 0 : mInfo += tr( "continuous (multi)" );
760 : : }
761 : 0 : mShortInfo = QFileInfo( mName ).fileName();
762 : 0 : }
763 : : else
764 : : {
765 : 0 : mInfo = QString::number( variantList.count() ) + ' ' + tr( "variants" );
766 : : // mShortInfo = QFileInfo( mName ).fileName() + " (" + QString::number( variantList.count() ) + ')';
767 : 0 : mShortInfo = QFileInfo( mName ).fileName();
768 : : }
769 : 0 : }
770 : : else
771 : : {
772 : 0 : mInfo.clear();
773 : : }
774 : :
775 : 0 : }
776 : :
777 : 0 : bool QgsCptCityColorRampItem::equal( const QgsCptCityDataItem *other )
778 : : {
779 : : //QgsDebugMsg ( mPath + " x " + other->mPath );
780 : 0 : if ( type() != other->type() )
781 : : {
782 : 0 : return false;
783 : : }
784 : : //const QgsCptCityColorRampItem *o = qobject_cast<const QgsCptCityColorRampItem *> ( other );
785 : 0 : const QgsCptCityColorRampItem *o = qobject_cast<const QgsCptCityColorRampItem *>( other );
786 : 0 : return o &&
787 : 0 : mPath == o->mPath &&
788 : 0 : mName == o->mName &&
789 : 0 : ramp().variantName() == o->ramp().variantName();
790 : 0 : }
791 : :
792 : 0 : QIcon QgsCptCityColorRampItem::icon()
793 : : {
794 : 0 : return icon( QSize( 100, 15 ) );
795 : : }
796 : :
797 : 0 : QIcon QgsCptCityColorRampItem::icon( QSize size )
798 : : {
799 : 0 : const auto constMIcons = mIcons;
800 : 0 : for ( const QIcon &icon : constMIcons )
801 : : {
802 : 0 : if ( icon.availableSizes().contains( size ) )
803 : 0 : return icon;
804 : : }
805 : :
806 : 0 : QIcon icon;
807 : :
808 : 0 : init();
809 : :
810 : 0 : if ( mValid && mRamp.count() > 0 )
811 : : {
812 : 0 : icon = QgsSymbolLayerUtils::colorRampPreviewIcon( &mRamp, size );
813 : 0 : }
814 : : else
815 : : {
816 : 0 : QPixmap blankPixmap( size );
817 : 0 : blankPixmap.fill( Qt::white );
818 : 0 : icon = QIcon( blankPixmap );
819 : 0 : mInfo.clear();
820 : 0 : }
821 : :
822 : 0 : mIcons.append( icon );
823 : 0 : return icon;
824 : 0 : }
825 : :
826 : : // ---------------------------------------------------------------------
827 : 0 : QgsCptCityCollectionItem::QgsCptCityCollectionItem( QgsCptCityDataItem *parent,
828 : : const QString &name, const QString &path )
829 : 0 : : QgsCptCityDataItem( Collection, parent, name, path )
830 : 0 : , mPopulatedRamps( false )
831 : 0 : {
832 : 0 : }
833 : :
834 : 0 : QgsCptCityCollectionItem::~QgsCptCityCollectionItem()
835 : 0 : {
836 : 0 : qDeleteAll( mChildren );
837 : 0 : }
838 : :
839 : 0 : QVector< QgsCptCityDataItem * > QgsCptCityCollectionItem::childrenRamps( bool recursive )
840 : : {
841 : 0 : QVector< QgsCptCityDataItem * > rampItems;
842 : 0 : QVector< QgsCptCityDataItem * > deleteItems;
843 : :
844 : 0 : populate();
845 : :
846 : : // recursively add children
847 : 0 : const auto constChildren = children();
848 : 0 : for ( QgsCptCityDataItem *childItem : constChildren )
849 : : {
850 : 0 : QgsCptCityCollectionItem *collectionItem = qobject_cast<QgsCptCityCollectionItem *>( childItem );
851 : 0 : QgsCptCityColorRampItem *rampItem = qobject_cast<QgsCptCityColorRampItem *>( childItem );
852 : 0 : QgsDebugMsgLevel( QStringLiteral( "child path= %1 coll= %2 ramp = %3" ).arg( childItem->path() ).arg( nullptr != collectionItem ).arg( nullptr != rampItem ), 2 );
853 : 0 : if ( collectionItem && recursive )
854 : : {
855 : 0 : collectionItem->populate();
856 : 0 : rampItems << collectionItem->childrenRamps( true );
857 : 0 : }
858 : 0 : else if ( rampItem )
859 : : {
860 : : // init rampItem to get palette and icon, test if is valid after loading file
861 : 0 : rampItem->init();
862 : 0 : if ( rampItem->isValid() )
863 : 0 : rampItems << rampItem;
864 : : else
865 : 0 : deleteItems << rampItem;
866 : 0 : }
867 : : else
868 : : {
869 : 0 : QgsDebugMsg( "invalid item " + childItem->path() );
870 : : }
871 : : }
872 : :
873 : : // delete invalid items - this is not efficient, but should only happens once
874 : 0 : const auto constDeleteItems = deleteItems;
875 : 0 : for ( QgsCptCityDataItem *deleteItem : constDeleteItems )
876 : : {
877 : 0 : QgsDebugMsg( QStringLiteral( "item %1 is invalid, will be deleted" ).arg( deleteItem->path() ) );
878 : 0 : int i = mChildren.indexOf( deleteItem );
879 : 0 : if ( i != -1 )
880 : 0 : mChildren.remove( i );
881 : 0 : delete deleteItem;
882 : : }
883 : :
884 : 0 : return rampItems;
885 : 0 : }
886 : :
887 : : //-----------------------------------------------------------------------
888 : 0 : QgsCptCityDirectoryItem::QgsCptCityDirectoryItem( QgsCptCityDataItem *parent,
889 : : const QString &name, const QString &path )
890 : 0 : : QgsCptCityCollectionItem( parent, name, path )
891 : 0 : {
892 : 0 : mType = Directory;
893 : 0 : mValid = QDir( QgsCptCityArchive::defaultBaseDir() + '/' + mPath ).exists();
894 : 0 : if ( ! mValid )
895 : : {
896 : 0 : QgsDebugMsg( "created invalid dir item, path = " + QgsCptCityArchive::defaultBaseDir()
897 : : + '/' + mPath );
898 : 0 : }
899 : :
900 : : // parse DESC.xml to get mInfo
901 : 0 : mInfo.clear();
902 : 0 : QString fileName = QgsCptCityArchive::defaultBaseDir() + '/' +
903 : 0 : mPath + '/' + "DESC.xml";
904 : 0 : QgsStringMap descMap = QgsCptCityArchive::description( fileName );
905 : 0 : if ( descMap.contains( QStringLiteral( "name" ) ) )
906 : 0 : mInfo = descMap.value( QStringLiteral( "name" ) );
907 : :
908 : : // populate();
909 : 0 : }
910 : :
911 : 0 : QVector<QgsCptCityDataItem *> QgsCptCityDirectoryItem::createChildren()
912 : : {
913 : 0 : if ( ! mValid )
914 : 0 : return QVector<QgsCptCityDataItem *>();
915 : :
916 : 0 : QVector<QgsCptCityDataItem *> children;
917 : :
918 : : // add children schemes
919 : 0 : QMapIterator< QString, QStringList> it( rampsMap() );
920 : 0 : while ( it.hasNext() )
921 : : {
922 : 0 : it.next();
923 : : // QgsDebugMsg( "schemeName = " + it.key() );
924 : 0 : QgsCptCityDataItem *item =
925 : 0 : new QgsCptCityColorRampItem( this, it.key(), it.key(), it.value() );
926 : 0 : if ( item->isValid() )
927 : 0 : children << item;
928 : : else
929 : 0 : delete item;
930 : : }
931 : :
932 : : // add children dirs
933 : 0 : const auto constDirEntries = dirEntries();
934 : 0 : for ( const QString &childPath : constDirEntries )
935 : : {
936 : 0 : QgsCptCityDataItem *childItem =
937 : 0 : QgsCptCityDirectoryItem::dataItem( this, childPath, mPath + '/' + childPath );
938 : 0 : if ( childItem )
939 : 0 : children << childItem;
940 : : }
941 : :
942 : 0 : QgsDebugMsg( QStringLiteral( "name= %1 path= %2 found %3 children" ).arg( mName, mPath ).arg( children.count() ) );
943 : :
944 : 0 : return children;
945 : 0 : }
946 : :
947 : 0 : QMap< QString, QStringList > QgsCptCityDirectoryItem::rampsMap()
948 : : {
949 : 0 : if ( ! mRampsMap.isEmpty() )
950 : 0 : return mRampsMap;
951 : :
952 : 0 : QString curName, prevName, curVariant, curSep, schemeName;
953 : 0 : QStringList listVariant;
954 : 0 : QStringList schemeNamesAll, schemeNames;
955 : : bool prevAdd, curAdd;
956 : :
957 : 0 : QDir dir( QgsCptCityArchive::defaultBaseDir() + '/' + mPath );
958 : 0 : schemeNamesAll = dir.entryList( QStringList( QStringLiteral( "*.svg" ) ), QDir::Files, QDir::Name );
959 : :
960 : : // TODO detect if there are duplicate names with different variant counts, combine in 1
961 : 0 : for ( int i = 0; i < schemeNamesAll.count(); i++ )
962 : : {
963 : : // schemeName = QFileInfo( schemeNamesAll[i] ).baseName();
964 : 0 : schemeName = schemeNamesAll[i];
965 : 0 : schemeName.chop( 4 );
966 : : // QgsDebugMsg("=============");
967 : : // QgsDebugMsg("scheme = "+schemeName);
968 : 0 : curName = schemeName;
969 : 0 : curVariant.clear();
970 : :
971 : : // find if name ends with 1-3 digit number
972 : : // TODO need to detect if ends with b/c also
973 : 0 : if ( schemeName.length() > 1 && schemeName.endsWith( 'a' ) && ! listVariant.isEmpty() &&
974 : 0 : ( ( prevName + listVariant.last() + 'a' ) == curName ) )
975 : : {
976 : 0 : curName = prevName;
977 : 0 : curVariant = listVariant.last() + 'a';
978 : 0 : }
979 : : else
980 : : {
981 : 0 : QRegExp rxVariant( "^(.*[^\\d])(\\d{1,3})$" );
982 : 0 : int pos = rxVariant.indexIn( schemeName );
983 : 0 : if ( pos > -1 )
984 : : {
985 : 0 : curName = rxVariant.cap( 1 );
986 : 0 : curVariant = rxVariant.cap( 2 );
987 : 0 : }
988 : 0 : }
989 : :
990 : 0 : curSep = curName.right( 1 );
991 : 0 : if ( curSep == QLatin1String( "-" ) || curSep == QLatin1String( "_" ) )
992 : : {
993 : 0 : curName.chop( 1 );
994 : 0 : curVariant = curSep + curVariant;
995 : 0 : }
996 : :
997 : 0 : if ( prevName.isEmpty() )
998 : 0 : prevName = curName;
999 : :
1000 : : // add element, unless it is empty, or a variant of last element
1001 : 0 : prevAdd = false;
1002 : 0 : curAdd = false;
1003 : 0 : if ( curName.isEmpty() )
1004 : 0 : curName = QStringLiteral( "__empty__" );
1005 : : // if current is a variant of last, don't add previous and append current variant
1006 : 0 : if ( curName == prevName )
1007 : : {
1008 : : // add current element if it is the last one in the archive
1009 : 0 : if ( i == schemeNamesAll.count() - 1 )
1010 : 0 : prevAdd = true;
1011 : 0 : listVariant << curVariant;
1012 : 0 : }
1013 : : else
1014 : : {
1015 : 0 : if ( !prevName.isEmpty() )
1016 : : {
1017 : 0 : prevAdd = true;
1018 : 0 : }
1019 : : // add current element if it is the last one in the archive
1020 : 0 : if ( i == schemeNamesAll.count() - 1 )
1021 : 0 : curAdd = true;
1022 : : }
1023 : :
1024 : : // QgsDebugMsg(QString("prevAdd=%1 curAdd=%2 prevName=%3 curName=%4 count=%5").arg(prevAdd).arg(curAdd).arg(prevName).arg(curName).arg(listVariant.count()));
1025 : :
1026 : 0 : if ( prevAdd )
1027 : : {
1028 : : // depending on number of variants, make one or more items
1029 : 0 : if ( listVariant.isEmpty() )
1030 : : {
1031 : : // set num colors=-1 to parse file on request only
1032 : : // mSchemeNumColors[ prevName ] = -1;
1033 : 0 : schemeNames << prevName;
1034 : 0 : mRampsMap[ mPath + '/' + prevName ] = QStringList();
1035 : 0 : }
1036 : 0 : else if ( listVariant.count() <= 3 )
1037 : : {
1038 : : // for 1-2 items, create independent items
1039 : 0 : for ( int j = 0; j < listVariant.count(); j++ )
1040 : : {
1041 : : // mSchemeNumColors[ prevName + listVariant[j] ] = -1;
1042 : 0 : schemeNames << prevName + listVariant[j];
1043 : 0 : mRampsMap[ mPath + '/' + prevName + listVariant[j] ] = QStringList();
1044 : 0 : }
1045 : 0 : }
1046 : : else
1047 : : {
1048 : : // mSchemeVariants[ path + '/' + prevName ] = listVariant;
1049 : 0 : mRampsMap[ mPath + '/' + prevName ] = listVariant;
1050 : 0 : schemeNames << prevName;
1051 : : }
1052 : 0 : listVariant.clear();
1053 : 0 : }
1054 : 0 : if ( curAdd )
1055 : : {
1056 : 0 : if ( !curVariant.isEmpty() )
1057 : 0 : curName += curVariant;
1058 : 0 : schemeNames << curName;
1059 : 0 : mRampsMap[ mPath + '/' + curName ] = QStringList();
1060 : 0 : }
1061 : : // save current to compare next
1062 : 0 : if ( prevAdd || curAdd )
1063 : : {
1064 : 0 : prevName = curName;
1065 : 0 : if ( !curVariant.isEmpty() )
1066 : 0 : listVariant << curVariant;
1067 : 0 : }
1068 : :
1069 : 0 : }
1070 : : #if 0
1071 : : //TODO what to do with other vars? e.g. schemeNames
1072 : : // add schemes to archive
1073 : : mSchemeMap[ path ] = schemeNames;
1074 : : schemeCount += schemeName.count();
1075 : : schemeNames.clear();
1076 : : listVariant.clear();
1077 : : prevName = "";
1078 : : #endif
1079 : 0 : return mRampsMap;
1080 : 0 : }
1081 : :
1082 : 0 : QStringList QgsCptCityDirectoryItem::dirEntries() const
1083 : : {
1084 : 0 : return QDir( QgsCptCityArchive::defaultBaseDir() +
1085 : 0 : '/' + mPath ).entryList( QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name );
1086 : 0 : }
1087 : :
1088 : 0 : bool QgsCptCityDirectoryItem::equal( const QgsCptCityDataItem *other )
1089 : : {
1090 : : //QgsDebugMsg ( mPath + " x " + other->mPath );
1091 : 0 : if ( type() != other->type() )
1092 : : {
1093 : 0 : return false;
1094 : : }
1095 : 0 : return ( path() == other->path() );
1096 : 0 : }
1097 : :
1098 : 0 : QgsCptCityDataItem *QgsCptCityDirectoryItem::dataItem( QgsCptCityDataItem *parent,
1099 : : const QString &name, const QString &path )
1100 : : {
1101 : 0 : QgsDebugMsg( "name= " + name + " path= " + path );
1102 : :
1103 : : // first create item with constructor
1104 : 0 : QgsCptCityDirectoryItem *dirItem = new QgsCptCityDirectoryItem( parent, name, path );
1105 : 0 : if ( dirItem && ! dirItem->isValid() )
1106 : : {
1107 : 0 : delete dirItem;
1108 : 0 : return nullptr;
1109 : : }
1110 : 0 : if ( ! dirItem )
1111 : 0 : return nullptr;
1112 : :
1113 : : // fetch sub-dirs and ramps to know what to do with this item
1114 : 0 : QStringList dirEntries = dirItem->dirEntries();
1115 : 0 : QMap< QString, QStringList > rampsMap = dirItem->rampsMap();
1116 : :
1117 : 0 : QgsDebugMsg( QStringLiteral( "item has %1 dirs and %2 ramps" ).arg( dirEntries.count() ).arg( rampsMap.count() ) );
1118 : :
1119 : : // return item if has at least one subdir
1120 : 0 : if ( !dirEntries.isEmpty() )
1121 : 0 : return dirItem;
1122 : :
1123 : : // if 0 ramps, delete item
1124 : 0 : if ( rampsMap.isEmpty() )
1125 : : {
1126 : 0 : delete dirItem;
1127 : 0 : return nullptr;
1128 : : }
1129 : : // if 1 ramp, return this child's item
1130 : : // so we don't have a directory with just 1 item (with many variants possibly)
1131 : 0 : else if ( rampsMap.count() == 1 )
1132 : : {
1133 : 0 : delete dirItem;
1134 : 0 : QgsCptCityColorRampItem *rampItem =
1135 : 0 : new QgsCptCityColorRampItem( parent, rampsMap.begin().key(),
1136 : 0 : rampsMap.begin().key(), rampsMap.begin().value() );
1137 : 0 : if ( ! rampItem->isValid() )
1138 : : {
1139 : 0 : delete rampItem;
1140 : 0 : return nullptr;
1141 : : }
1142 : 0 : return rampItem;
1143 : : }
1144 : 0 : return dirItem;
1145 : 0 : }
1146 : :
1147 : :
1148 : : //-----------------------------------------------------------------------
1149 : 0 : QgsCptCitySelectionItem::QgsCptCitySelectionItem( QgsCptCityDataItem *parent,
1150 : : const QString &name, const QString &path )
1151 : 0 : : QgsCptCityCollectionItem( parent, name, path )
1152 : 0 : {
1153 : 0 : mType = Selection;
1154 : 0 : mValid = ! path.isNull();
1155 : 0 : if ( mValid )
1156 : 0 : parseXml();
1157 : 0 : }
1158 : :
1159 : 0 : QVector<QgsCptCityDataItem *> QgsCptCitySelectionItem::createChildren()
1160 : : {
1161 : 0 : if ( ! mValid )
1162 : 0 : return QVector<QgsCptCityDataItem *>();
1163 : :
1164 : 0 : QgsCptCityDataItem *item = nullptr;
1165 : 0 : QVector<QgsCptCityDataItem *> children;
1166 : :
1167 : 0 : QgsDebugMsg( "name= " + mName + " path= " + mPath );
1168 : :
1169 : : // add children archives
1170 : 0 : const auto constMSelectionsList = mSelectionsList;
1171 : 0 : for ( QString childPath : constMSelectionsList )
1172 : : {
1173 : 0 : QgsDebugMsg( "childPath = " + childPath + " name= " + QFileInfo( childPath ).baseName() );
1174 : 0 : if ( childPath.endsWith( '/' ) )
1175 : : {
1176 : 0 : childPath.chop( 1 );
1177 : 0 : QgsCptCityDataItem *childItem =
1178 : 0 : QgsCptCityDirectoryItem::dataItem( this, childPath, childPath );
1179 : 0 : if ( childItem )
1180 : : {
1181 : 0 : if ( childItem->isValid() )
1182 : 0 : children << childItem;
1183 : : else
1184 : 0 : delete childItem;
1185 : 0 : }
1186 : 0 : }
1187 : : else
1188 : : {
1189 : : // init item to test if is valid after loading file
1190 : 0 : item = new QgsCptCityColorRampItem( this, childPath, childPath, QString(), true );
1191 : 0 : if ( item->isValid() )
1192 : 0 : children << item;
1193 : : else
1194 : 0 : delete item;
1195 : : }
1196 : 0 : }
1197 : :
1198 : 0 : QgsDebugMsg( QStringLiteral( "path= %1 inserted %2 children" ).arg( mPath ).arg( children.count() ) );
1199 : :
1200 : 0 : return children;
1201 : 0 : }
1202 : :
1203 : 0 : void QgsCptCitySelectionItem::parseXml()
1204 : : {
1205 : 0 : QString filename = QgsCptCityArchive::defaultBaseDir() + '/' + mPath;
1206 : :
1207 : 0 : QgsDebugMsg( "reading file " + filename );
1208 : :
1209 : 0 : QFile f( filename );
1210 : 0 : if ( ! f.open( QFile::ReadOnly ) )
1211 : : {
1212 : 0 : QgsDebugMsg( filename + " does not exist" );
1213 : 0 : return;
1214 : : }
1215 : :
1216 : : // parse the document
1217 : 0 : QString errMsg;
1218 : 0 : QDomDocument doc( QStringLiteral( "selection" ) );
1219 : 0 : if ( !doc.setContent( &f, &errMsg ) )
1220 : : {
1221 : 0 : f.close();
1222 : 0 : QgsDebugMsg( "Couldn't parse file " + filename + " : " + errMsg );
1223 : 0 : return;
1224 : : }
1225 : 0 : f.close();
1226 : :
1227 : : // read description
1228 : 0 : QDomElement docElem = doc.documentElement();
1229 : 0 : if ( docElem.tagName() != QLatin1String( "selection" ) )
1230 : : {
1231 : 0 : QgsDebugMsg( "Incorrect root tag: " + docElem.tagName() );
1232 : 0 : return;
1233 : : }
1234 : 0 : QDomElement e = docElem.firstChildElement( QStringLiteral( "name" ) );
1235 : 0 : if ( ! e.isNull() && ! e.text().isNull() )
1236 : 0 : mName = e.text();
1237 : 0 : mInfo = docElem.firstChildElement( QStringLiteral( "synopsis" ) ).text().simplified();
1238 : :
1239 : : // get archives
1240 : 0 : QDomElement collectsElem = docElem.firstChildElement( QStringLiteral( "seealsocollects" ) );
1241 : 0 : e = collectsElem.firstChildElement( QStringLiteral( "collect" ) );
1242 : 0 : while ( ! e.isNull() )
1243 : : {
1244 : 0 : if ( ! e.attribute( QStringLiteral( "dir" ) ).isNull() )
1245 : : {
1246 : : // TODO parse description and use that, instead of default archive name
1247 : 0 : mSelectionsList << e.attribute( QStringLiteral( "dir" ) ) + '/';
1248 : 0 : }
1249 : 0 : e = e.nextSiblingElement();
1250 : : }
1251 : : // get individual gradients
1252 : 0 : QDomElement gradientsElem = docElem.firstChildElement( QStringLiteral( "gradients" ) );
1253 : 0 : e = gradientsElem.firstChildElement( QStringLiteral( "gradient" ) );
1254 : 0 : while ( ! e.isNull() )
1255 : : {
1256 : 0 : if ( ! e.attribute( QStringLiteral( "dir" ) ).isNull() )
1257 : : {
1258 : : // QgsDebugMsg( "add " + e.attribute( "dir" ) + '/' + e.attribute( "file" ) + " to " + selname );
1259 : : // TODO parse description and save elsewhere
1260 : 0 : mSelectionsList << e.attribute( QStringLiteral( "dir" ) ) + '/' + e.attribute( QStringLiteral( "file" ) );
1261 : 0 : }
1262 : 0 : e = e.nextSiblingElement();
1263 : : }
1264 : 0 : }
1265 : :
1266 : 0 : bool QgsCptCitySelectionItem::equal( const QgsCptCityDataItem *other )
1267 : : {
1268 : : //QgsDebugMsg ( mPath + " x " + other->mPath );
1269 : 0 : if ( type() != other->type() )
1270 : : {
1271 : 0 : return false;
1272 : : }
1273 : 0 : return ( path() == other->path() );
1274 : 0 : }
1275 : :
1276 : : //-----------------------------------------------------------------------
1277 : 0 : QgsCptCityAllRampsItem::QgsCptCityAllRampsItem( QgsCptCityDataItem *parent,
1278 : : const QString &name, const QVector<QgsCptCityDataItem *> &items )
1279 : 0 : : QgsCptCityCollectionItem( parent, name, QString() )
1280 : 0 : , mItems( items )
1281 : 0 : {
1282 : 0 : mType = AllRamps;
1283 : 0 : mValid = true;
1284 : : // populate();
1285 : 0 : }
1286 : :
1287 : 0 : QVector<QgsCptCityDataItem *> QgsCptCityAllRampsItem::createChildren()
1288 : : {
1289 : 0 : if ( ! mValid )
1290 : 0 : return QVector<QgsCptCityDataItem *>();
1291 : :
1292 : 0 : QVector<QgsCptCityDataItem *> children;
1293 : :
1294 : : // add children ramps of each item
1295 : 0 : const auto constMItems = mItems;
1296 : 0 : for ( QgsCptCityDataItem *item : constMItems )
1297 : : {
1298 : 0 : QgsCptCityCollectionItem *colItem = qobject_cast< QgsCptCityCollectionItem * >( item );
1299 : 0 : if ( colItem )
1300 : 0 : children += colItem->childrenRamps( true );
1301 : : }
1302 : :
1303 : 0 : return children;
1304 : 0 : }
1305 : :
1306 : : //-----------------------------------------------------------------------
1307 : :
1308 : 0 : QgsCptCityBrowserModel::QgsCptCityBrowserModel( QObject *parent,
1309 : : QgsCptCityArchive *archive, ViewType viewType )
1310 : 0 : : QAbstractItemModel( parent )
1311 : 0 : , mArchive( archive )
1312 : 0 : , mViewType( viewType )
1313 : 0 : {
1314 : : Q_ASSERT( mArchive );
1315 : 0 : QgsDebugMsg( QLatin1String( "archiveName = " ) + archive->archiveName() + " viewType=" + QString::number( static_cast< int >( viewType ) ) );
1316 : : // keep iconsize for now, but not effectively used
1317 : 0 : mIconSize = QSize( 100, 15 );
1318 : 0 : addRootItems();
1319 : 0 : }
1320 : :
1321 : 0 : QgsCptCityBrowserModel::~QgsCptCityBrowserModel()
1322 : 0 : {
1323 : 0 : removeRootItems();
1324 : 0 : }
1325 : :
1326 : 0 : void QgsCptCityBrowserModel::addRootItems()
1327 : : {
1328 : 0 : if ( mViewType == Authors )
1329 : : {
1330 : 0 : mRootItems = mArchive->rootItems();
1331 : 0 : }
1332 : 0 : else if ( mViewType == Selections )
1333 : : {
1334 : 0 : mRootItems = mArchive->selectionItems();
1335 : 0 : }
1336 : 0 : QgsDebugMsg( QStringLiteral( "added %1 root items" ).arg( mRootItems.size() ) );
1337 : 0 : }
1338 : :
1339 : 0 : void QgsCptCityBrowserModel::removeRootItems()
1340 : : {
1341 : 0 : mRootItems.clear();
1342 : 0 : }
1343 : :
1344 : 0 : Qt::ItemFlags QgsCptCityBrowserModel::flags( const QModelIndex &index ) const
1345 : : {
1346 : 0 : if ( !index.isValid() )
1347 : 0 : return Qt::ItemFlags();
1348 : :
1349 : 0 : Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
1350 : :
1351 : 0 : return flags;
1352 : 0 : }
1353 : :
1354 : 0 : QVariant QgsCptCityBrowserModel::data( const QModelIndex &index, int role ) const
1355 : : {
1356 : 0 : if ( !index.isValid() )
1357 : 0 : return QVariant();
1358 : :
1359 : 0 : QgsCptCityDataItem *item = dataItem( index );
1360 : :
1361 : 0 : if ( !item )
1362 : : {
1363 : 0 : return QVariant();
1364 : : }
1365 : 0 : else if ( role == Qt::DisplayRole )
1366 : : {
1367 : 0 : if ( index.column() == 0 )
1368 : 0 : return item->name();
1369 : 0 : if ( index.column() == 1 )
1370 : : {
1371 : 0 : return item->info();
1372 : : }
1373 : 0 : }
1374 : 0 : else if ( role == Qt::ToolTipRole )
1375 : : {
1376 : 0 : if ( item->type() == QgsCptCityDataItem::ColorRamp &&
1377 : 0 : mViewType == List )
1378 : 0 : return QString( item->path() + '\n' + item->info() );
1379 : 0 : return item->toolTip();
1380 : : }
1381 : 0 : else if ( role == Qt::DecorationRole && index.column() == 1 &&
1382 : 0 : item->type() == QgsCptCityDataItem::ColorRamp )
1383 : : {
1384 : : // keep iconsize for now, but not effectively used
1385 : 0 : return item->icon( mIconSize );
1386 : : }
1387 : 0 : else if ( role == Qt::FontRole &&
1388 : 0 : qobject_cast< QgsCptCityCollectionItem * >( item ) )
1389 : : {
1390 : : // collectionitems are larger and bold
1391 : 0 : QFont font;
1392 : 0 : font.setPointSize( 11 ); //FIXME why is the font so small?
1393 : 0 : font.setBold( true );
1394 : 0 : return font;
1395 : 0 : }
1396 : : else
1397 : : {
1398 : : // unsupported role
1399 : 0 : return QVariant();
1400 : : }
1401 : 0 : return QVariant();
1402 : 0 : }
1403 : :
1404 : 0 : QVariant QgsCptCityBrowserModel::headerData( int section, Qt::Orientation orientation, int role ) const
1405 : : {
1406 : : Q_UNUSED( section )
1407 : 0 : if ( orientation == Qt::Horizontal && role == Qt::DisplayRole )
1408 : : {
1409 : 0 : if ( section == 0 )
1410 : 0 : return QVariant( tr( "Name" ) );
1411 : 0 : else if ( section == 1 )
1412 : 0 : return QVariant( tr( "Info" ) );
1413 : 0 : }
1414 : 0 : return QVariant();
1415 : 0 : }
1416 : :
1417 : 0 : int QgsCptCityBrowserModel::rowCount( const QModelIndex &parent ) const
1418 : : {
1419 : : //qDebug("rowCount: idx: (valid %d) %d %d", parent.isValid(), parent.row(), parent.column());
1420 : :
1421 : 0 : if ( !parent.isValid() )
1422 : : {
1423 : : // root item: its children are top level items
1424 : 0 : return mRootItems.count(); // mRoot
1425 : : }
1426 : : else
1427 : : {
1428 : : // ordinary item: number of its children
1429 : 0 : QgsCptCityDataItem *item = dataItem( parent );
1430 : 0 : return item ? item->rowCount() : 0;
1431 : : }
1432 : 0 : }
1433 : :
1434 : 0 : bool QgsCptCityBrowserModel::hasChildren( const QModelIndex &parent ) const
1435 : : {
1436 : 0 : if ( !parent.isValid() )
1437 : 0 : return true; // root item: its children are top level items
1438 : :
1439 : 0 : QgsCptCityDataItem *item = dataItem( parent );
1440 : :
1441 : 0 : return item && item->hasChildren();
1442 : 0 : }
1443 : :
1444 : 0 : int QgsCptCityBrowserModel::columnCount( const QModelIndex &parent ) const
1445 : : {
1446 : 0 : Q_UNUSED( parent )
1447 : 0 : return 2;
1448 : : }
1449 : :
1450 : 0 : QModelIndex QgsCptCityBrowserModel::findPath( const QString &path )
1451 : : {
1452 : 0 : QModelIndex rootIndex; // starting from root
1453 : 0 : bool foundParent = false, foundChild = true;
1454 : 0 : QString itemPath;
1455 : :
1456 : 0 : QgsDebugMsg( "path = " + path );
1457 : :
1458 : : // special case if searching for first item "All Ramps", do not search into tree
1459 : 0 : if ( path.isEmpty() )
1460 : : {
1461 : 0 : for ( int i = 0; i < rowCount( rootIndex ); i++ )
1462 : : {
1463 : 0 : QModelIndex idx = index( i, 0, rootIndex );
1464 : 0 : QgsCptCityDataItem *item = dataItem( idx );
1465 : 0 : if ( !item )
1466 : 0 : return QModelIndex(); // an error occurred
1467 : :
1468 : 0 : itemPath = item->path();
1469 : :
1470 : 0 : if ( itemPath == path )
1471 : : {
1472 : 0 : QgsDebugMsg( "Arrived " + itemPath );
1473 : 0 : return idx; // we have found the item we have been looking for
1474 : : }
1475 : 0 : }
1476 : 0 : }
1477 : :
1478 : 0 : while ( foundChild )
1479 : : {
1480 : 0 : foundChild = false; // assume that the next child item will not be found
1481 : :
1482 : 0 : int i = 0;
1483 : : // if root skip first item "All Ramps"
1484 : 0 : if ( itemPath.isEmpty() )
1485 : 0 : i = 1;
1486 : 0 : for ( ; i < rowCount( rootIndex ); i++ )
1487 : : {
1488 : 0 : QModelIndex idx = index( i, 0, rootIndex );
1489 : 0 : QgsCptCityDataItem *item = dataItem( idx );
1490 : 0 : if ( !item )
1491 : 0 : return QModelIndex(); // an error occurred
1492 : :
1493 : 0 : itemPath = item->path();
1494 : :
1495 : 0 : if ( itemPath == path )
1496 : : {
1497 : 0 : QgsDebugMsg( "Arrived " + itemPath );
1498 : 0 : return idx; // we have found the item we have been looking for
1499 : : }
1500 : :
1501 : 0 : if ( ! itemPath.endsWith( '/' ) )
1502 : 0 : itemPath += '/';
1503 : :
1504 : 0 : foundParent = false;
1505 : :
1506 : : // QgsDebugMsg( "path= " + path + " itemPath= " + itemPath );
1507 : :
1508 : : // if we are using a selection collection, search for target in the mapping in this group
1509 : 0 : if ( item->type() == QgsCptCityDataItem::Selection )
1510 : : {
1511 : 0 : const QgsCptCitySelectionItem *selItem = qobject_cast<const QgsCptCitySelectionItem *>( item );
1512 : 0 : if ( selItem )
1513 : : {
1514 : 0 : const auto constSelectionsList = selItem->selectionsList();
1515 : 0 : for ( QString childPath : constSelectionsList )
1516 : : {
1517 : 0 : if ( childPath.endsWith( '/' ) )
1518 : 0 : childPath.chop( 1 );
1519 : : // QgsDebugMsg( "childPath= " + childPath );
1520 : 0 : if ( path.startsWith( childPath ) )
1521 : : {
1522 : 0 : foundParent = true;
1523 : 0 : break;
1524 : : }
1525 : 0 : }
1526 : 0 : }
1527 : 0 : }
1528 : : // search for target in parent directory
1529 : 0 : else if ( path.startsWith( itemPath ) )
1530 : : {
1531 : 0 : foundParent = true;
1532 : 0 : }
1533 : :
1534 : 0 : if ( foundParent )
1535 : : {
1536 : 0 : QgsDebugMsg( "found parent " + path );
1537 : : // we have found a preceding item: stop searching on this level and go deeper
1538 : 0 : foundChild = true;
1539 : 0 : rootIndex = idx;
1540 : 0 : if ( canFetchMore( rootIndex ) )
1541 : 0 : fetchMore( rootIndex );
1542 : 0 : break;
1543 : : }
1544 : 0 : }
1545 : : }
1546 : :
1547 : 0 : return QModelIndex(); // not found
1548 : 0 : }
1549 : :
1550 : 0 : void QgsCptCityBrowserModel::reload()
1551 : : {
1552 : 0 : beginResetModel();
1553 : 0 : removeRootItems();
1554 : 0 : addRootItems();
1555 : 0 : endResetModel();
1556 : 0 : }
1557 : :
1558 : : /* Refresh dir path */
1559 : 0 : void QgsCptCityBrowserModel::refresh( const QString &path )
1560 : : {
1561 : 0 : QModelIndex idx = findPath( path );
1562 : 0 : if ( idx.isValid() )
1563 : : {
1564 : 0 : QgsCptCityDataItem *item = dataItem( idx );
1565 : 0 : if ( item )
1566 : 0 : item->refresh();
1567 : 0 : }
1568 : 0 : }
1569 : :
1570 : 0 : QModelIndex QgsCptCityBrowserModel::index( int row, int column, const QModelIndex &parent ) const
1571 : : {
1572 : 0 : QgsCptCityDataItem *p = dataItem( parent );
1573 : 0 : const QVector<QgsCptCityDataItem *> &items = p ? p->children() : mRootItems;
1574 : 0 : QgsCptCityDataItem *item = items.value( row, nullptr );
1575 : 0 : return item ? createIndex( row, column, item ) : QModelIndex();
1576 : 0 : }
1577 : :
1578 : 0 : QModelIndex QgsCptCityBrowserModel::parent( const QModelIndex &index ) const
1579 : : {
1580 : 0 : QgsCptCityDataItem *item = dataItem( index );
1581 : 0 : if ( !item )
1582 : 0 : return QModelIndex();
1583 : :
1584 : 0 : return findItem( item->parent() );
1585 : 0 : }
1586 : :
1587 : 0 : QModelIndex QgsCptCityBrowserModel::findItem( QgsCptCityDataItem *item, QgsCptCityDataItem *parent ) const
1588 : : {
1589 : 0 : const QVector<QgsCptCityDataItem *> &items = parent ? parent->children() : mRootItems;
1590 : :
1591 : 0 : for ( int i = 0; i < items.size(); i++ )
1592 : : {
1593 : 0 : if ( items[i] == item )
1594 : 0 : return createIndex( i, 0, item );
1595 : :
1596 : 0 : QModelIndex childIndex = findItem( item, items[i] );
1597 : 0 : if ( childIndex.isValid() )
1598 : 0 : return childIndex;
1599 : 0 : }
1600 : :
1601 : 0 : return QModelIndex();
1602 : 0 : }
1603 : :
1604 : : /* Refresh item */
1605 : 0 : void QgsCptCityBrowserModel::refresh( const QModelIndex &index )
1606 : : {
1607 : 0 : QgsCptCityDataItem *item = dataItem( index );
1608 : 0 : if ( !item )
1609 : 0 : return;
1610 : :
1611 : 0 : QgsDebugMsg( "Refresh " + item->path() );
1612 : 0 : item->refresh();
1613 : 0 : }
1614 : :
1615 : 0 : void QgsCptCityBrowserModel::beginInsertItems( QgsCptCityDataItem *parent, int first, int last )
1616 : : {
1617 : 0 : QgsDebugMsg( "parent mPath = " + parent->path() );
1618 : 0 : QModelIndex idx = findItem( parent );
1619 : 0 : if ( !idx.isValid() )
1620 : 0 : return;
1621 : 0 : QgsDebugMsg( QStringLiteral( "valid" ) );
1622 : 0 : beginInsertRows( idx, first, last );
1623 : 0 : QgsDebugMsg( QStringLiteral( "end" ) );
1624 : 0 : }
1625 : 0 : void QgsCptCityBrowserModel::endInsertItems()
1626 : : {
1627 : 0 : endInsertRows();
1628 : 0 : }
1629 : 0 : void QgsCptCityBrowserModel::beginRemoveItems( QgsCptCityDataItem *parent, int first, int last )
1630 : : {
1631 : 0 : QgsDebugMsg( "parent mPath = " + parent->path() );
1632 : 0 : QModelIndex idx = findItem( parent );
1633 : 0 : if ( !idx.isValid() )
1634 : 0 : return;
1635 : 0 : beginRemoveRows( idx, first, last );
1636 : 0 : }
1637 : 0 : void QgsCptCityBrowserModel::endRemoveItems()
1638 : : {
1639 : 0 : endRemoveRows();
1640 : 0 : }
1641 : 0 : void QgsCptCityBrowserModel::connectItem( QgsCptCityDataItem *item )
1642 : : {
1643 : 0 : connect( item, &QgsCptCityDataItem::beginInsertItems, this, &QgsCptCityBrowserModel::beginInsertItems );
1644 : 0 : connect( item, &QgsCptCityDataItem::endInsertItems, this, &QgsCptCityBrowserModel::endInsertItems );
1645 : 0 : connect( item, &QgsCptCityDataItem::beginRemoveItems, this, &QgsCptCityBrowserModel::beginRemoveItems );
1646 : 0 : connect( item, &QgsCptCityDataItem::endRemoveItems, this, &QgsCptCityBrowserModel::endRemoveItems );
1647 : 0 : }
1648 : :
1649 : 0 : bool QgsCptCityBrowserModel::canFetchMore( const QModelIndex &parent ) const
1650 : : {
1651 : 0 : QgsCptCityDataItem *item = dataItem( parent );
1652 : : // fetch all items initially so we know which items have children
1653 : : // (nicer looking and less confusing)
1654 : :
1655 : 0 : if ( ! item )
1656 : 0 : return false;
1657 : :
1658 : : // except for "All Ramps" - this is populated when clicked on
1659 : 0 : if ( item->type() == QgsCptCityDataItem::AllRamps )
1660 : 0 : return false;
1661 : :
1662 : 0 : item->populate();
1663 : :
1664 : 0 : return ( ! item->isPopulated() );
1665 : 0 : }
1666 : :
1667 : 0 : void QgsCptCityBrowserModel::fetchMore( const QModelIndex &parent )
1668 : : {
1669 : 0 : QgsCptCityDataItem *item = dataItem( parent );
1670 : 0 : if ( item )
1671 : : {
1672 : 0 : item->populate();
1673 : 0 : QgsDebugMsg( "path = " + item->path() );
1674 : 0 : }
1675 : 0 : }
1676 : :
1677 : :
1678 : : #if 0
1679 : : QStringList QgsCptCityBrowserModel::mimeTypes() const
1680 : : {
1681 : : QStringList types;
1682 : : // In theory the mime type convention is: application/x-vnd.<vendor>.<application>.<type>
1683 : : // but it seems a bit over formalized. Would be an application/x-qgis-uri better?
1684 : : types << "application/x-vnd.qgis.qgis.uri";
1685 : : return types;
1686 : : }
1687 : :
1688 : : QMimeData *QgsCptCityBrowserModel::mimeData( const QModelIndexList &indexes ) const
1689 : : {
1690 : : QgsMimeDataUtils::UriList lst;
1691 : : const auto constIndexes = indexes;
1692 : : for ( const QModelIndex &index : constIndexes )
1693 : : {
1694 : : if ( index.isValid() )
1695 : : {
1696 : : QgsCptCityDataItem *ptr = ( QgsCptCityDataItem * ) index.internalPointer();
1697 : : if ( ptr->type() != QgsCptCityDataItem::Layer ) continue;
1698 : : QgsLayerItem *layer = ( QgsLayerItem * ) ptr;
1699 : : lst.append( QgsMimeDataUtils::Uri( ayer ) );
1700 : : }
1701 : : }
1702 : : return QgsMimeDataUtils::encodeUriList( lst );
1703 : : }
1704 : :
1705 : : bool QgsCptCityBrowserModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
1706 : : {
1707 : : Q_UNUSED( row )
1708 : : Q_UNUSED( column )
1709 : :
1710 : : QgsCptCityDataItem *destItem = dataItem( parent );
1711 : : if ( !destItem )
1712 : : {
1713 : : QgsDebugMsg( QStringLiteral( "DROP PROBLEM!" ) );
1714 : : return false;
1715 : : }
1716 : :
1717 : : return destItem->handleDrop( data, action );
1718 : : }
1719 : : #endif
1720 : :
1721 : 0 : QgsCptCityDataItem *QgsCptCityBrowserModel::dataItem( const QModelIndex &idx ) const
1722 : : {
1723 : 0 : void *v = idx.internalPointer();
1724 : 0 : QgsCptCityDataItem *d = reinterpret_cast<QgsCptCityDataItem *>( v );
1725 : : Q_ASSERT( !v || d );
1726 : 0 : return d;
1727 : : }
|