QGIS API Documentation  2.8.2-Wien
 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 o &&
798  mPath == o->mPath &&
799  mName == o->mName &&
800  ramp().variantName() == o->ramp().variantName();
801 }
802 
804 {
805  return icon( QSize( 100, 15 ) );
806 }
807 
808 QIcon QgsCptCityColorRampItem::icon( const QSize& size )
809 {
810  foreach ( QIcon icon, mIcons )
811  {
812  if ( icon.availableSizes().contains( size ) )
813  return icon;
814  }
815 
816  QIcon icon;
817 
818  init();
819 
820  if ( mValid && mRamp.count() > 0 )
821  {
823  }
824  else
825  {
826  QPixmap blankPixmap( size );
827  blankPixmap.fill( Qt::white );
828  icon = QIcon( blankPixmap );
829  mInfo = "";
830  }
831 
832  mIcons.append( icon );
833  return icon;
834 }
835 
836 // ---------------------------------------------------------------------
838  QString name, QString path )
839  : QgsCptCityDataItem( Collection, parent, name, path ), mPopulatedRamps( false )
840 {
841 }
842 
844 {
845  // QgsDebugMsg( "Entered" );
846  foreach ( QgsCptCityDataItem* i, mChildren )
847  {
848  // QgsDebugMsg( QString( "delete child = 0x%0" ).arg(( qlonglong )i, 8, 16, QLatin1Char( '0' ) ) );
849  delete i;
850  }
851 }
852 
853 QVector< QgsCptCityDataItem* > QgsCptCityCollectionItem::childrenRamps( bool recursive )
854 {
855  QVector< QgsCptCityDataItem* > rampItems;
856  QVector< QgsCptCityDataItem* > deleteItems;
857 
858  populate();
859 
860  // recursively add children
861  foreach ( QgsCptCityDataItem* childItem, children() )
862  {
863  QgsCptCityCollectionItem* collectionItem = dynamic_cast<QgsCptCityCollectionItem*>( childItem );
864  QgsCptCityColorRampItem* rampItem = dynamic_cast<QgsCptCityColorRampItem*>( childItem );
865  QgsDebugMsgLevel( QString( "child path= %1 coll= %2 ramp = %3" ).arg( childItem->path() ).arg( collectionItem != 0 ).arg( rampItem != 0 ), 2 );
866  if ( collectionItem && recursive )
867  {
868  collectionItem->populate();
869  rampItems << collectionItem->childrenRamps( true );
870  }
871  else if ( rampItem )
872  {
873  // init rampItem to get palette and icon, test if is valid after loading file
874  rampItem->init();
875  if ( rampItem->isValid() )
876  rampItems << rampItem;
877  else
878  deleteItems << rampItem;
879  }
880  else
881  {
882  QgsDebugMsg( "invalid item " + childItem->path() );
883  }
884  }
885 
886  // delete invalid items - this is not efficient, but should only happens once
887  foreach ( QgsCptCityDataItem* deleteItem, deleteItems )
888  {
889  QgsDebugMsg( QString( "item %1 is invalid, will be deleted" ).arg( deleteItem->path() ) );
890  int i = mChildren.indexOf( deleteItem );
891  if ( i != -1 )
892  mChildren.remove( i );
893  delete deleteItem;
894  }
895 
896  return rampItems;
897 }
898 
899 //-----------------------------------------------------------------------
901  QString name, QString path )
902  : QgsCptCityCollectionItem( parent, name, path )
903 {
904  mType = Directory;
905  mValid = QDir( QgsCptCityArchive::defaultBaseDir() + "/" + mPath ).exists();
906  if ( ! mValid )
907  {
908  QgsDebugMsg( "created invalid dir item, path = " + QgsCptCityArchive::defaultBaseDir()
909  + "/" + mPath );
910  }
911 
912  // parse DESC.xml to get mInfo
913  mInfo = "";
914  QString fileName = QgsCptCityArchive::defaultBaseDir() + "/" +
915  mPath + "/" + "DESC.xml";
916  QgsStringMap descMap = QgsCptCityArchive::description( fileName );
917  if ( descMap.contains( "name" ) )
918  mInfo = descMap.value( "name" );
919 
920  // populate();
921 }
922 
924 {
925 }
926 
927 QVector<QgsCptCityDataItem*> QgsCptCityDirectoryItem::createChildren()
928 {
929  if ( ! mValid )
930  return QVector<QgsCptCityDataItem*>();
931 
932  QVector<QgsCptCityDataItem*> children;
933 
934  // add children schemes
935  QMapIterator< QString, QStringList> it( rampsMap() );
936  while ( it.hasNext() )
937  {
938  it.next();
939  // QgsDebugMsg( "schemeName = " + it.key() );
940  QgsCptCityDataItem* item =
941  new QgsCptCityColorRampItem( this, it.key(), it.key(), it.value() );
942  if ( item->isValid() )
943  children << item;
944  else
945  delete item;
946  }
947 
948  // add children dirs
949  foreach ( QString childPath, dirEntries() )
950  {
951  QgsCptCityDataItem* childItem =
952  QgsCptCityDirectoryItem::dataItem( this, childPath, mPath + "/" + childPath );
953  if ( childItem )
954  children << childItem;
955  }
956 
957  QgsDebugMsg( QString( "name= %1 path= %2 found %3 children" ).arg( mName ).arg( mPath ).arg( children.count() ) );
958 
959  return children;
960 }
961 
962 QMap< QString, QStringList > QgsCptCityDirectoryItem::rampsMap()
963 {
964  if ( ! mRampsMap.isEmpty() )
965  return mRampsMap;
966 
967  QString curName, prevName, prevPath, curVariant, curSep, schemeName;
968  QStringList listVariant;
969  QStringList schemeNamesAll, schemeNames;
970  bool prevAdd, curAdd;
971 
972  QDir dir( QgsCptCityArchive::defaultBaseDir() + "/" + mPath );
973  schemeNamesAll = dir.entryList( QStringList( "*.svg" ), QDir::Files, QDir::Name );
974 
975  // TODO detect if there are duplicate names with different variant counts, combine in 1
976  for ( int i = 0; i < schemeNamesAll.count(); i++ )
977  {
978  // schemeName = QFileInfo( schemeNamesAll[i] ).baseName();
979  schemeName = schemeNamesAll[i];
980  schemeName.chop( 4 );
981  // QgsDebugMsg("=============");
982  // QgsDebugMsg("scheme = "+schemeName);
983  curName = schemeName;
984  curVariant = "";
985 
986  // find if name ends with 1-3 digit number
987  // TODO need to detect if ends with b/c also
988  if ( schemeName.length() > 1 && schemeName.endsWith( "a" ) && ! listVariant.isEmpty() &&
989  (( prevName + listVariant.last() + "a" ) == curName ) )
990  {
991  curName = prevName;
992  curVariant = listVariant.last() + "a";
993  }
994  else
995  {
996  QRegExp rxVariant( "^(.*[^\\d])(\\d{1,3})$" );
997  int pos = rxVariant.indexIn( schemeName );
998  if ( pos > -1 )
999  {
1000  curName = rxVariant.cap( 1 );
1001  curVariant = rxVariant.cap( 2 );
1002  }
1003  }
1004 
1005  curSep = curName.right( 1 );
1006  if ( curSep == "-" || curSep == "_" )
1007  {
1008  curName.chop( 1 );
1009  curVariant = curSep + curVariant;
1010  }
1011 
1012  if ( prevName == "" )
1013  prevName = curName;
1014 
1015  // add element, unless it is empty, or a variant of last element
1016  prevAdd = false;
1017  curAdd = false;
1018  if ( curName == "" )
1019  curName = "__empty__";
1020  // if current is a variant of last, don't add previous and append current variant
1021  if ( curName == prevName )
1022  {
1023  // add current element if it is the last one in the archive
1024  if ( i == schemeNamesAll.count() - 1 )
1025  prevAdd = true;
1026  listVariant << curVariant;
1027  }
1028  else
1029  {
1030  if ( prevName != "" )
1031  {
1032  prevAdd = true;
1033  }
1034  // add current element if it is the last one in the archive
1035  if ( i == schemeNamesAll.count() - 1 )
1036  curAdd = true;
1037  }
1038 
1039  // QgsDebugMsg(QString("prevAdd=%1 curAdd=%2 prevName=%3 curName=%4 count=%5").arg(prevAdd).arg(curAdd).arg(prevName).arg(curName).arg(listVariant.count()));
1040 
1041  if ( prevAdd )
1042  {
1043  // depending on number of variants, make one or more items
1044  if ( listVariant.count() == 0 )
1045  {
1046  // set num colors=-1 to parse file on request only
1047  // mSchemeNumColors[ prevName ] = -1;
1048  schemeNames << prevName;
1049  mRampsMap[ mPath + "/" + prevName ] = QStringList();
1050  }
1051  else if ( listVariant.count() <= 3 )
1052  {
1053  // for 1-2 items, create independent items
1054  for ( int j = 0; j < listVariant.count(); j++ )
1055  {
1056  // mSchemeNumColors[ prevName + listVariant[j] ] = -1;
1057  schemeNames << prevName + listVariant[j];
1058  mRampsMap[ mPath + "/" + prevName + listVariant[j] ] = QStringList();
1059  }
1060  }
1061  else
1062  {
1063  // mSchemeVariants[ path + "/" + prevName ] = listVariant;
1064  mRampsMap[ mPath + "/" + prevName ] = listVariant;
1065  schemeNames << prevName;
1066  }
1067  listVariant.clear();
1068  }
1069  if ( curAdd )
1070  {
1071  if ( curVariant != "" )
1072  curName += curVariant;
1073  schemeNames << curName;
1074  mRampsMap[ mPath + "/" + curName ] = QStringList();
1075  }
1076  // save current to compare next
1077  if ( prevAdd || curAdd )
1078  {
1079  prevName = curName;
1080  if ( curVariant != "" )
1081  listVariant << curVariant;
1082  }
1083 
1084  }
1085  //TODO what to do with other vars? e.g. schemeNames
1086  // // add schemes to archive
1087  // mSchemeMap[ path ] = schemeNames;
1088  // schemeCount += schemeName.count();
1089  // schemeNames.clear();
1090  // listVariant.clear();
1091  // prevName = "";
1092  return mRampsMap;
1093 }
1094 
1096 {
1097  return QDir( QgsCptCityArchive::defaultBaseDir() +
1098  "/" + mPath ).entryList( QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name );
1099 }
1100 
1102 {
1103  //QgsDebugMsg ( mPath + " x " + other->mPath );
1104  if ( type() != other->type() )
1105  {
1106  return false;
1107  }
1108  return ( path() == other->path() );
1109 }
1110 
1112  QString name, QString path )
1113 {
1114  QgsDebugMsg( "name= " + name + " path= " + path );
1115 
1116  // first create item with constructor
1117  QgsCptCityDirectoryItem* dirItem = new QgsCptCityDirectoryItem( parent, name, path );
1118  if ( dirItem && ! dirItem->isValid() )
1119  {
1120  delete dirItem;
1121  return 0;
1122  }
1123  if ( ! dirItem )
1124  return 0;
1125 
1126  // fetch sub-dirs and ramps to know what to do with this item
1127  QStringList theDirEntries = dirItem->dirEntries();
1128  QMap< QString, QStringList > theRampsMap = dirItem->rampsMap();
1129 
1130  QgsDebugMsg( QString( "item has %1 dirs and %2 ramps" ).arg( theDirEntries.count() ).arg( theRampsMap.count() ) );
1131 
1132  // return item if has at least one subdir
1133  if ( theDirEntries.count() > 0 )
1134  return dirItem;
1135 
1136  // if 0 ramps, delete item
1137  if ( theRampsMap.count() == 0 )
1138  {
1139  delete dirItem;
1140  return 0;
1141  }
1142  // if 1 ramp, return this child's item
1143  // so we don't have a directory with just 1 item (with many variants possibly)
1144  else if ( theRampsMap.count() == 1 )
1145  {
1146  delete dirItem;
1147  QgsCptCityColorRampItem* rampItem =
1148  new QgsCptCityColorRampItem( parent, theRampsMap.begin().key(),
1149  theRampsMap.begin().key(), theRampsMap.begin().value() );
1150  if ( ! rampItem->isValid() )
1151  {
1152  delete rampItem;
1153  return 0;
1154  }
1155  return rampItem;
1156  }
1157  return dirItem;
1158 }
1159 
1160 
1161 //-----------------------------------------------------------------------
1163  QString name, QString path )
1164  : QgsCptCityCollectionItem( parent, name, path )
1165 {
1166  mType = Selection;
1167  mValid = ! path.isNull();
1168  if ( mValid )
1169  parseXML();
1170 }
1171 
1173 {
1174 }
1175 
1176 QVector<QgsCptCityDataItem*> QgsCptCitySelectionItem::createChildren()
1177 {
1178  if ( ! mValid )
1179  return QVector<QgsCptCityDataItem*>();
1180 
1181  QgsCptCityDataItem* item = 0;
1182  QVector<QgsCptCityDataItem*> children;
1183 
1184  QgsDebugMsg( "name= " + mName + " path= " + mPath );
1185 
1186  // add children archives
1187  foreach ( QString childPath, mSelectionsList )
1188  {
1189  QgsDebugMsg( "childPath = " + childPath + " name= " + QFileInfo( childPath ).baseName() );
1190  if ( childPath.endsWith( "/" ) )
1191  {
1192  childPath.chop( 1 );
1193  QgsCptCityDataItem* childItem =
1194  QgsCptCityDirectoryItem::dataItem( this, childPath, childPath );
1195  if ( childItem )
1196  {
1197  if ( childItem->isValid() )
1198  children << childItem;
1199  else
1200  delete childItem;
1201  }
1202  }
1203  else
1204  {
1205  // init item to test if is valid after loading file
1206  item = new QgsCptCityColorRampItem( this, childPath, childPath, QString(), true );
1207  if ( item->isValid() )
1208  children << item;
1209  else
1210  delete item;
1211  }
1212  }
1213 
1214  QgsDebugMsg( QString( "path= %1 inserted %2 children" ).arg( mPath ).arg( children.count() ) );
1215 
1216  return children;
1217 }
1218 
1220 {
1221  QString filename = QgsCptCityArchive::defaultBaseDir() + "/" + mPath;
1222 
1223  QgsDebugMsg( "reading file " + filename );
1224 
1225  QFile f( filename );
1226  if ( ! f.open( QFile::ReadOnly ) )
1227  {
1228  QgsDebugMsg( filename + " does not exist" );
1229  return;
1230  }
1231 
1232  // parse the document
1233  QString errMsg;
1234  QDomDocument doc( "selection" );
1235  if ( !doc.setContent( &f, &errMsg ) )
1236  {
1237  f.close();
1238  QgsDebugMsg( "Couldn't parse file " + filename + " : " + errMsg );
1239  return;
1240  }
1241  f.close();
1242 
1243  // read description
1244  QDomElement docElem = doc.documentElement();
1245  if ( docElem.tagName() != "selection" )
1246  {
1247  QgsDebugMsg( "Incorrect root tag: " + docElem.tagName() );
1248  return;
1249  }
1250  QDomElement e = docElem.firstChildElement( "name" );
1251  if ( ! e.isNull() && ! e.text().isNull() )
1252  mName = e.text();
1253  mInfo = docElem.firstChildElement( "synopsis" ).text().simplified();
1254 
1255  // get archives
1256  QDomElement collectsElem = docElem.firstChildElement( "seealsocollects" );
1257  e = collectsElem.firstChildElement( "collect" );
1258  while ( ! e.isNull() )
1259  {
1260  if ( ! e.attribute( "dir" ).isNull() )
1261  {
1262  // TODO parse description and use that, instead of default archive name
1263  mSelectionsList << e.attribute( "dir" ) + "/";
1264  }
1265  e = e.nextSiblingElement();
1266  }
1267  // get individual gradients
1268  QDomElement gradientsElem = docElem.firstChildElement( "gradients" );
1269  e = gradientsElem.firstChildElement( "gradient" );
1270  while ( ! e.isNull() )
1271  {
1272  if ( ! e.attribute( "dir" ).isNull() )
1273  {
1274  // QgsDebugMsg( "add " + e.attribute( "dir" ) + "/" + e.attribute( "file" ) + " to " + selname );
1275  // TODO parse description and save elsewhere
1276  mSelectionsList << e.attribute( "dir" ) + "/" + e.attribute( "file" );
1277  }
1278  e = e.nextSiblingElement();
1279  }
1280 }
1281 
1283 {
1284  //QgsDebugMsg ( mPath + " x " + other->mPath );
1285  if ( type() != other->type() )
1286  {
1287  return false;
1288  }
1289  return ( path() == other->path() );
1290 }
1291 
1292 //-----------------------------------------------------------------------
1294  QString name, QVector<QgsCptCityDataItem*> items )
1295  : QgsCptCityCollectionItem( parent, name, QString() ), mItems( items )
1296 {
1297  mType = AllRamps;
1298  mValid = true;
1299  // populate();
1300 }
1301 
1303 {
1304 }
1305 
1306 QVector<QgsCptCityDataItem*> QgsCptCityAllRampsItem::createChildren()
1307 {
1308  if ( ! mValid )
1309  return QVector<QgsCptCityDataItem*>();
1310 
1311  QVector<QgsCptCityDataItem*> children;
1312 
1313  // add children ramps of each item
1314  foreach ( QgsCptCityDataItem* item, mItems )
1315  {
1316  QgsCptCityCollectionItem* colItem = dynamic_cast< QgsCptCityCollectionItem* >( item );
1317  if ( colItem )
1318  children += colItem->childrenRamps( true );
1319  }
1320 
1321  return children;
1322 }
1323 
1324 //-----------------------------------------------------------------------
1325 
1327  QgsCptCityArchive* archive, ViewType viewType )
1328  : QAbstractItemModel( parent ), mArchive( archive ), mViewType( viewType )
1329 {
1330  Q_ASSERT( mArchive != NULL );
1331  QgsDebugMsg( "archiveName = " + archive->archiveName() + " viewType=" + ( int ) viewType );
1332  // keep iconsize for now, but not effectively used
1333  mIconSize = QSize( 100, 15 );
1334  addRootItems();
1335 }
1336 
1338 {
1339  removeRootItems();
1340 }
1341 
1343 {
1344  if ( mViewType == Authors )
1345  {
1347  }
1348  else if ( mViewType == Selections )
1349  {
1351  }
1352  QgsDebugMsg( QString( "added %1 root items" ).arg( mRootItems.size() ) );
1353 }
1354 
1356 {
1357  // don't remove root items, they belong to the QgsCptCityArchive
1358  // foreach ( QgsCptCityDataItem* item, mRootItems )
1359  // {
1360  // delete item;
1361  // }
1362 
1363  mRootItems.clear();
1364 }
1365 
1366 Qt::ItemFlags QgsCptCityBrowserModel::flags( const QModelIndex & index ) const
1367 {
1368  if ( !index.isValid() )
1369  return 0;
1370 
1371  Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
1372 
1373  return flags;
1374 }
1375 
1376 QVariant QgsCptCityBrowserModel::data( const QModelIndex &index, int role ) const
1377 {
1378  if ( !index.isValid() )
1379  return QVariant();
1380 
1381  QgsCptCityDataItem *item = dataItem( index );
1382 
1383  if ( !item )
1384  {
1385  return QVariant();
1386  }
1387  else if ( role == Qt::DisplayRole )
1388  {
1389  if ( index.column() == 0 )
1390  return item->name();
1391  if ( index.column() == 1 )
1392  {
1393  return item->info();
1394  }
1395  }
1396  else if ( role == Qt::ToolTipRole )
1397  {
1398  if ( item->type() == QgsCptCityDataItem::ColorRamp &&
1399  mViewType == List )
1400  return item->path() + "\n" + item->info();
1401  return item->toolTip();
1402  }
1403  else if ( role == Qt::DecorationRole && index.column() == 1 &&
1404  item->type() == QgsCptCityDataItem::ColorRamp )
1405  {
1406  // keep iconsize for now, but not effectively used
1407  return item->icon( mIconSize );
1408  }
1409  else if ( role == Qt::FontRole &&
1410  ( dynamic_cast< QgsCptCityCollectionItem* >( item ) != 0 ) )
1411  {
1412  // collectionitems are larger and bold
1413  QFont font;
1414  font.setPointSize( 11 ); //FIXME why is the font so small?
1415  font.setBold( true );
1416  return font;
1417  }
1418  else
1419  {
1420  // unsupported role
1421  return QVariant();
1422  }
1423  return QVariant();
1424 }
1425 
1426 QVariant QgsCptCityBrowserModel::headerData( int section, Qt::Orientation orientation, int role ) const
1427 {
1428  Q_UNUSED( section );
1429  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole )
1430  {
1431  if ( section == 0 )
1432  return QVariant( tr( "Name" ) );
1433  else if ( section == 1 )
1434  return QVariant( tr( "Info" ) );
1435  }
1436  return QVariant();
1437 }
1438 
1439 int QgsCptCityBrowserModel::rowCount( const QModelIndex &parent ) const
1440 {
1441  //qDebug("rowCount: idx: (valid %d) %d %d", parent.isValid(), parent.row(), parent.column());
1442 
1443  if ( !parent.isValid() )
1444  {
1445  // root item: its children are top level items
1446  return mRootItems.count(); // mRoot
1447  }
1448  else
1449  {
1450  // ordinary item: number of its children
1451  QgsCptCityDataItem *item = dataItem( parent );
1452  return item ? item->rowCount() : 0;
1453  }
1454 }
1455 
1456 bool QgsCptCityBrowserModel::hasChildren( const QModelIndex &parent ) const
1457 {
1458  if ( !parent.isValid() )
1459  return true; // root item: its children are top level items
1460 
1461  QgsCptCityDataItem *item = dataItem( parent );
1462 
1463  return item && item->hasChildren();
1464 }
1465 
1466 int QgsCptCityBrowserModel::columnCount( const QModelIndex &parent ) const
1467 {
1468  Q_UNUSED( parent );
1469  return 2;
1470 }
1471 
1472 QModelIndex QgsCptCityBrowserModel::findPath( QString path )
1473 {
1474  QModelIndex theIndex; // starting from root
1475  bool foundParent = false, foundChild = true;
1476  QString itemPath;
1477 
1478  QgsDebugMsg( "path = " + path );
1479 
1480  // special case if searching for first item "All Ramps", do not search into tree
1481  if ( path.isEmpty() )
1482  {
1483  for ( int i = 0; i < rowCount( theIndex ); i++ )
1484  {
1485  QModelIndex idx = index( i, 0, theIndex );
1486  QgsCptCityDataItem *item = dataItem( idx );
1487  if ( !item )
1488  return QModelIndex(); // an error occurred
1489 
1490  itemPath = item->path();
1491 
1492  if ( itemPath == path )
1493  {
1494  QgsDebugMsg( "Arrived " + itemPath );
1495  return idx; // we have found the item we have been looking for
1496  }
1497  }
1498  }
1499 
1500  while ( foundChild )
1501  {
1502  foundChild = false; // assume that the next child item will not be found
1503 
1504  int i = 0;
1505  // if root skip first item "All Ramps"
1506  if ( itemPath.isEmpty() )
1507  i = 1;
1508  for ( ; i < rowCount( theIndex ); i++ )
1509  {
1510  QModelIndex idx = index( i, 0, theIndex );
1511  QgsCptCityDataItem *item = dataItem( idx );
1512  if ( !item )
1513  return QModelIndex(); // an error occurred
1514 
1515  itemPath = item->path();
1516 
1517  if ( itemPath == path )
1518  {
1519  QgsDebugMsg( "Arrived " + itemPath );
1520  return idx; // we have found the item we have been looking for
1521  }
1522 
1523  if ( ! itemPath.endsWith( "/" ) )
1524  itemPath += "/";
1525 
1526  foundParent = false;
1527 
1528  // QgsDebugMsg( "path= " + path + " itemPath= " + itemPath );
1529 
1530  // if we are using a selection collection, search for target in the mapping in this group
1531  if ( item->type() == QgsCptCityDataItem::Selection )
1532  {
1533  const QgsCptCitySelectionItem* selItem = dynamic_cast<const QgsCptCitySelectionItem *>( item );
1534  if ( selItem )
1535  {
1536  foreach ( QString childPath, selItem->selectionsList() )
1537  {
1538  if ( childPath.endsWith( "/" ) )
1539  childPath.chop( 1 );
1540  // QgsDebugMsg( "childPath= " + childPath );
1541  if ( path.startsWith( childPath ) )
1542  {
1543  foundParent = true;
1544  break;
1545  }
1546  }
1547  }
1548  }
1549  // search for target in parent directory
1550  else if ( path.startsWith( itemPath ) )
1551  {
1552  foundParent = true;
1553  }
1554 
1555  if ( foundParent )
1556  {
1557  QgsDebugMsg( "found parent " + path );
1558  // we have found a preceding item: stop searching on this level and go deeper
1559  foundChild = true;
1560  theIndex = idx;
1561  if ( canFetchMore( theIndex ) )
1562  fetchMore( theIndex );
1563  break;
1564  }
1565  }
1566  }
1567 
1568  return QModelIndex(); // not found
1569 }
1570 
1572 {
1573  beginResetModel();
1574  removeRootItems();
1575  addRootItems();
1576  endResetModel();
1577 }
1578 
1579 /* Refresh dir path */
1581 {
1582  QModelIndex idx = findPath( path );
1583  if ( idx.isValid() )
1584  {
1585  QgsCptCityDataItem* item = dataItem( idx );
1586  if ( item )
1587  item->refresh();
1588  }
1589 }
1590 
1591 QModelIndex QgsCptCityBrowserModel::index( int row, int column, const QModelIndex &parent ) const
1592 {
1593  QgsCptCityDataItem *p = dataItem( parent );
1594  const QVector<QgsCptCityDataItem*> &items = p ? p->children() : mRootItems;
1595  QgsCptCityDataItem *item = items.value( row, 0 );
1596  return item ? createIndex( row, column, item ) : QModelIndex();
1597 }
1598 
1599 QModelIndex QgsCptCityBrowserModel::parent( const QModelIndex &index ) const
1600 {
1601  QgsCptCityDataItem *item = dataItem( index );
1602  if ( !item )
1603  return QModelIndex();
1604 
1605  return findItem( item->parent() );
1606 }
1607 
1609 {
1610  const QVector<QgsCptCityDataItem*> &items = parent ? parent->children() : mRootItems;
1611 
1612  for ( int i = 0; i < items.size(); i++ )
1613  {
1614  if ( items[i] == item )
1615  return createIndex( i, 0, item );
1616 
1617  QModelIndex childIndex = findItem( item, items[i] );
1618  if ( childIndex.isValid() )
1619  return childIndex;
1620  }
1621 
1622  return QModelIndex();
1623 }
1624 
1625 /* Refresh item */
1626 void QgsCptCityBrowserModel::refresh( const QModelIndex& theIndex )
1627 {
1628  QgsCptCityDataItem *item = dataItem( theIndex );
1629  if ( !item )
1630  return;
1631 
1632  QgsDebugMsg( "Refresh " + item->path() );
1633  item->refresh();
1634 }
1635 
1637 {
1638  QgsDebugMsg( "parent mPath = " + parent->path() );
1639  QModelIndex idx = findItem( parent );
1640  if ( !idx.isValid() )
1641  return;
1642  QgsDebugMsg( "valid" );
1643  beginInsertRows( idx, first, last );
1644  QgsDebugMsg( "end" );
1645 }
1647 {
1648  QgsDebugMsg( "Entered" );
1649  endInsertRows();
1650 }
1652 {
1653  QgsDebugMsg( "parent mPath = " + parent->path() );
1654  QModelIndex idx = findItem( parent );
1655  if ( !idx.isValid() )
1656  return;
1657  beginRemoveRows( idx, first, last );
1658 }
1660 {
1661  QgsDebugMsg( "Entered" );
1662  endRemoveRows();
1663 }
1665 {
1666  connect( item, SIGNAL( beginInsertItems( QgsCptCityDataItem*, int, int ) ),
1667  this, SLOT( beginInsertItems( QgsCptCityDataItem*, int, int ) ) );
1668  connect( item, SIGNAL( endInsertItems() ),
1669  this, SLOT( endInsertItems() ) );
1670  connect( item, SIGNAL( beginRemoveItems( QgsCptCityDataItem*, int, int ) ),
1671  this, SLOT( beginRemoveItems( QgsCptCityDataItem*, int, int ) ) );
1672  connect( item, SIGNAL( endRemoveItems() ),
1673  this, SLOT( endRemoveItems() ) );
1674 }
1675 
1676 bool QgsCptCityBrowserModel::canFetchMore( const QModelIndex & parent ) const
1677 {
1678  QgsCptCityDataItem* item = dataItem( parent );
1679  // fetch all items initially so we know which items have children
1680  // (nicer looking and less confusing)
1681 
1682  if ( ! item )
1683  return false;
1684 
1685  // except for "All Ramps" - this is populated when clicked on
1686  if ( item->type() == QgsCptCityDataItem::AllRamps )
1687  return false;
1688 
1689  item->populate();
1690 
1691  return ( ! item->isPopulated() );
1692 }
1693 
1694 void QgsCptCityBrowserModel::fetchMore( const QModelIndex & parent )
1695 {
1696  QgsCptCityDataItem* item = dataItem( parent );
1697  if ( item )
1698  {
1699  item->populate();
1700  QgsDebugMsg( "path = " + item->path() );
1701  }
1702 }
1703 
1704 
1705 #if 0
1706 QStringList QgsCptCityBrowserModel::mimeTypes() const
1707 {
1708  QStringList types;
1709  // In theory the mime type convention is: application/x-vnd.<vendor>.<application>.<type>
1710  // but it seems a bit over formalized. Would be an application/x-qgis-uri better?
1711  types << "application/x-vnd.qgis.qgis.uri";
1712  return types;
1713 }
1714 
1715 QMimeData * QgsCptCityBrowserModel::mimeData( const QModelIndexList &indexes ) const
1716 {
1718  foreach ( const QModelIndex &index, indexes )
1719  {
1720  if ( index.isValid() )
1721  {
1722  QgsCptCityDataItem* ptr = ( QgsCptCityDataItem* ) index.internalPointer();
1723  if ( ptr->type() != QgsCptCityDataItem::Layer ) continue;
1724  QgsLayerItem *layer = ( QgsLayerItem* ) ptr;
1725  lst.append( QgsMimeDataUtils::Uri( ayer ) );
1726  }
1727  }
1728  return QgsMimeDataUtils::encodeUriList( lst );
1729 }
1730 
1731 bool QgsCptCityBrowserModel::dropMimeData( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent )
1732 {
1733  Q_UNUSED( row );
1734  Q_UNUSED( column );
1735 
1736  QgsCptCityDataItem* destItem = dataItem( parent );
1737  if ( !destItem )
1738  {
1739  QgsDebugMsg( "DROP PROBLEM!" );
1740  return false;
1741  }
1742 
1743  return destItem->handleDrop( data, action );
1744 }
1745 #endif
1746 
1748 {
1749  void *v = idx.internalPointer();
1750  QgsCptCityDataItem *d = reinterpret_cast<QgsCptCityDataItem*>( v );
1751  Q_ASSERT( !v || d );
1752  return d;
1753 }