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