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