QGIS API Documentation  2.17.0-Master (eef6f05)
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)
void clear()
QStringList selectionsList() const
QDomNodeList elementsByTagName(const QString &tagname) const
void beginInsertItems(QgsCptCityDataItem *parent, int first, int last)
QString descFileName(const QString &dirName) const
void fetchMore(const QModelIndex &parent) override
virtual QgsCptCityDataItem * removeChildItem(QgsCptCityDataItem *child)
void setPointSize(int pointSize)
QString cap(int nth) 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)
QgsCptCityDataItem * dataItem(const QModelIndex &idx) const
Returns a list of mime that can describe model indexes.
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.
QVector< QgsCptCityDataItem * > children() const
QgsCptCityDirectoryItem(QgsCptCityDataItem *parent, const QString &name, const QString &path)
void append(const T &value)
void fill(const QColor &color)
static QMap< QString, QgsCptCityArchive *> mArchiveRegistry
QString attribute(const QString &name, const QString &defValue) const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
const QgsCptCityColorRampV2 & ramp() const
QString name() const
int indexOf(const T &value, int from) const
static QString defaultBaseDir()
QgsCptCityAllRampsItem(QgsCptCityDataItem *parent, const QString &name, const QVector< QgsCptCityDataItem *> &items)
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...
Item that represents a layer that can be opened with one of the providers.
void setAlpha(int alpha)
bool isDiscrete() const
Returns true if the gradient is using discrete interpolation, rather than smoothly interpolating betw...
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
QString toolTip() 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)
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
QVector< QgsCptCityDataItem *> selectionItems() const
QList< QSize > availableSizes(Mode mode, State state) const
virtual int count() const override
Returns number of defined colors, or -1 if undefined.
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 text() const
virtual bool handleDrop(const QMimeData *, Qt::DropAction)
QString path() 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)
Base class for all items in the model.
QString path() const
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)
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
const T & value() const
QVector< QgsCptCityDataItem * > mChildren
bool cdUp()
iterator begin()
QString right(int n) const
static void initDefaultArchive()
QModelIndex createIndex(int row, int column, void *ptr) const
virtual QIcon icon()
QString archiveName() const
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
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)
void beginInsertItems(QgsCptCityDataItem *parent, int first, int last)
QVector< QgsCptCityDataItem * > mRootItems
int count(const T &value) const
QString info() const
static int findItem(QVector< QgsCptCityDataItem *> items, QgsCptCityDataItem *item)
bool canFetchMore(const QModelIndex &parent) const override
int column() const
int length() const
QString baseDir() const
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 *> rootItems() const
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
QgsCptCityDataItem * parent() const
QVector< QgsCptCityDataItem * > childrenRamps(bool recursive)
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
QModelIndex findItem(QgsCptCityDataItem *item, QgsCptCityDataItem *parent=nullptr) const
virtual int leafCount() const
int count(const Key &key) const
static QgsCptCityDataItem * dataItem(QgsCptCityDataItem *parent, const QString &name, const QString &path)
QVector< QgsCptCityDataItem *> mRootItems
static QString findFileName(const QString &target, const QString &startDir, const QString &baseDir)
QStringList variantList() const
bool hasNext() const
QString copyingFileName(const QString &dirName) const
QStringList dirEntries() 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
static QMap< QString, QgsCptCityArchive *> archiveRegistry()
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