diff --git a/src/core/qgsapplication.cpp b/src/core/qgsapplication.cpp index fc3db6b53d4..30b5415986d 100644 --- a/src/core/qgsapplication.cpp +++ b/src/core/qgsapplication.cpp @@ -715,7 +715,7 @@ QStringList QgsApplication::svgPaths() } myPathList << ABISYM( mDefaultSvgPaths ); - return myPathList; + return myPathList.toSet().toList(); } /*! diff --git a/src/gui/symbology-ng/qgssvgselectorwidget.cpp b/src/gui/symbology-ng/qgssvgselectorwidget.cpp index 645130b169a..012a7c01c6f 100644 --- a/src/gui/symbology-ng/qgssvgselectorwidget.cpp +++ b/src/gui/symbology-ng/qgssvgselectorwidget.cpp @@ -32,29 +32,221 @@ #include #include +// QgsSvgSelectorLoader -//--- QgsSvgSelectorListModel +///@cond PRIVATE +QgsSvgSelectorLoader::QgsSvgSelectorLoader( QObject* parent ) + : QThread( parent ) + , mCancelled( false ) + , mTimerThreshold( 0 ) +{ +} + +QgsSvgSelectorLoader::~QgsSvgSelectorLoader() +{ + stop(); +} + +void QgsSvgSelectorLoader::run() +{ + mCancelled = false; + mQueuedSvgs.clear(); + + // start with a small initial timeout (ms) + mTimerThreshold = 10; + mTimer.start(); + + loadPath( mPath ); + + if ( !mQueuedSvgs.isEmpty() ) + { + // make sure we notify model of any remaining queued svgs (ie svgs added since last foundSvgs() signal was emitted) + emit foundSvgs( mQueuedSvgs ); + } + mQueuedSvgs.clear(); +} + +void QgsSvgSelectorLoader::stop() +{ + mCancelled = true; + while ( isRunning() ) {} +} + +void QgsSvgSelectorLoader::loadPath( const QString& path ) +{ + if ( mCancelled ) + return; + + // QgsDebugMsg( QString( "loading path: %1" ).arg( path ) ); + + if ( path.isEmpty() ) + { + QStringList svgPaths = QgsApplication::svgPaths(); + Q_FOREACH ( const QString& svgPath, svgPaths ) + { + if ( mCancelled ) + return; + + loadPath( svgPath ); + } + } + else + { + loadImages( path ); + + QDir dir( path ); + Q_FOREACH ( const QString& item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) ) + { + if ( mCancelled ) + return; + + QString newPath = dir.path() + '/' + item; + loadPath( newPath ); + // QgsDebugMsg( QString( "added path: %1" ).arg( newPath ) ); + } + } +} + +void QgsSvgSelectorLoader::loadImages( const QString& path ) +{ + QDir dir( path ); + Q_FOREACH ( const QString& item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) ) + { + if ( mCancelled ) + return; + + // TODO test if it is correct SVG + QString svgPath = dir.path() + '/' + item; + // QgsDebugMsg( QString( "adding svg: %1" ).arg( svgPath ) ); + + // add it to the list of queued SVGs + mQueuedSvgs << svgPath; + + // we need to avoid spamming the model with notifications about new svgs, so foundSvgs + // is only emitted for blocks of SVGs (otherwise the view goes all flickery) + if ( mTimer.elapsed() > mTimerThreshold ) + { + emit foundSvgs( mQueuedSvgs ); + mQueuedSvgs.clear(); + + // increase the timer threshold - this ensures that the first lots of svgs loaded are added + // to the view quickly, but as the list grows new svgs are added at a slower rate. + // ie, good for initial responsiveness but avoid being spammy as the list grows. + if ( mTimerThreshold < 1000 ) + mTimerThreshold *= 2; + mTimer.restart(); + } + } +} + + +// +// QgsSvgGroupLoader +// + +QgsSvgGroupLoader::QgsSvgGroupLoader( QObject* parent ) + : QThread( parent ) + , mCancelled( false ) +{ + +} + +QgsSvgGroupLoader::~QgsSvgGroupLoader() +{ + stop(); +} + +void QgsSvgGroupLoader::run() +{ + mCancelled = false; + + while ( !mCancelled && !mParentPaths.isEmpty() ) + { + QString parentPath = mParentPaths.takeFirst(); + loadGroup( parentPath ); + } +} + +void QgsSvgGroupLoader::stop() +{ + mCancelled = true; + while ( isRunning() ) {} +} + +void QgsSvgGroupLoader::loadGroup( const QString& parentPath ) +{ + QDir parentDir( parentPath ); + + Q_FOREACH ( const QString& item, parentDir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) ) + { + if ( mCancelled ) + return; + + emit foundPath( parentPath, item ); + mParentPaths.append( parentDir.path() + '/' + item ); + } +} + +///@endcond + +//, +// QgsSvgSelectorListModel +// QgsSvgSelectorListModel::QgsSvgSelectorListModel( QObject* parent ) : QAbstractListModel( parent ) + , mSvgLoader( new QgsSvgSelectorLoader( this ) ) { - mSvgFiles = QgsSymbolLayerUtils::listSvgFiles(); + mSvgLoader->setPath( QString() ); + connect( mSvgLoader, SIGNAL( foundSvgs( QStringList ) ), this, SLOT( addSvgs( QStringList ) ) ); + mSvgLoader->start(); } -// Constructor to create model for icons in a specific path QgsSvgSelectorListModel::QgsSvgSelectorListModel( QObject* parent, const QString& path ) : QAbstractListModel( parent ) + , mSvgLoader( new QgsSvgSelectorLoader( this ) ) { - mSvgFiles = QgsSymbolLayerUtils::listSvgFilesAt( path ); + mSvgLoader->setPath( path ); + connect( mSvgLoader, SIGNAL( foundSvgs( QStringList ) ), this, SLOT( addSvgs( QStringList ) ) ); + mSvgLoader->start(); } -int QgsSvgSelectorListModel::rowCount( const QModelIndex & parent ) const +int QgsSvgSelectorListModel::rowCount( const QModelIndex& parent ) const { Q_UNUSED( parent ); return mSvgFiles.count(); } -QVariant QgsSvgSelectorListModel::data( const QModelIndex & index, int role ) const +QPixmap QgsSvgSelectorListModel::createPreview( const QString& entry ) const +{ + // render SVG file + QColor fill, outline; + double outlineWidth, fillOpacity, outlineOpacity; + bool fillParam, fillOpacityParam, outlineParam, outlineWidthParam, outlineOpacityParam; + bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultOutlineColor = false, + hasDefaultOutlineWidth = false, hasDefaultOutlineOpacity = false; + QgsSvgCache::instance()->containsParams( entry, fillParam, hasDefaultFillColor, fill, + fillOpacityParam, hasDefaultFillOpacity, fillOpacity, + outlineParam, hasDefaultOutlineColor, outline, + outlineWidthParam, hasDefaultOutlineWidth, outlineWidth, + outlineOpacityParam, hasDefaultOutlineOpacity, outlineOpacity ); + + //if defaults not set in symbol, use these values + if ( !hasDefaultFillColor ) + fill = QColor( 200, 200, 200 ); + fill.setAlphaF( hasDefaultFillOpacity ? fillOpacity : 1.0 ); + if ( !hasDefaultOutlineColor ) + outline = Qt::black; + outline.setAlphaF( hasDefaultOutlineOpacity ? outlineOpacity : 1.0 ); + if ( !hasDefaultOutlineWidth ) + outlineWidth = 0.2; + + bool fitsInCache; // should always fit in cache at these sizes (i.e. under 559 px ^ 2, or half cache size) + const QImage& img = QgsSvgCache::instance()->svgAsImage( entry, 30.0, fill, outline, outlineWidth, 3.5 /*appr. 88 dpi*/, 1.0, fitsInCache ); + return QPixmap::fromImage( img ); +} + +QVariant QgsSvgSelectorListModel::data( const QModelIndex& index, int role ) const { QString entry = mSvgFiles.at( index.row() ); @@ -63,31 +255,7 @@ QVariant QgsSvgSelectorListModel::data( const QModelIndex & index, int role ) co QPixmap pixmap; if ( !QPixmapCache::find( entry, pixmap ) ) { - // render SVG file - QColor fill, outline; - double outlineWidth, fillOpacity, outlineOpacity; - bool fillParam, fillOpacityParam, outlineParam, outlineWidthParam, outlineOpacityParam; - bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultOutlineColor = false, - hasDefaultOutlineWidth = false, hasDefaultOutlineOpacity = false; - QgsSvgCache::instance()->containsParams( entry, fillParam, hasDefaultFillColor, fill, - fillOpacityParam, hasDefaultFillOpacity, fillOpacity, - outlineParam, hasDefaultOutlineColor, outline, - outlineWidthParam, hasDefaultOutlineWidth, outlineWidth, - outlineOpacityParam, hasDefaultOutlineOpacity, outlineOpacity ); - - //if defaults not set in symbol, use these values - if ( !hasDefaultFillColor ) - fill = QColor( 200, 200, 200 ); - fill.setAlphaF( hasDefaultFillOpacity ? fillOpacity : 1.0 ); - if ( !hasDefaultOutlineColor ) - outline = Qt::black; - outline.setAlphaF( hasDefaultOutlineOpacity ? outlineOpacity : 1.0 ); - if ( !hasDefaultOutlineWidth ) - outlineWidth = 0.2; - - bool fitsInCache; // should always fit in cache at these sizes (i.e. under 559 px ^ 2, or half cache size) - const QImage& img = QgsSvgCache::instance()->svgAsImage( entry, 30.0, fill, outline, outlineWidth, 3.5 /*appr. 88 dpi*/, 1.0, fitsInCache ); - pixmap = QPixmap::fromImage( img ); + pixmap = createPreview( entry ); QPixmapCache::insert( entry, pixmap ); } @@ -101,18 +269,30 @@ QVariant QgsSvgSelectorListModel::data( const QModelIndex & index, int role ) co return QVariant(); } +void QgsSvgSelectorListModel::addSvgs( const QStringList& svgs ) +{ + beginInsertRows( QModelIndex(), mSvgFiles.count(), mSvgFiles.count() + svgs.size() - 1 ); + mSvgFiles.append( svgs ); + endInsertRows(); +} + + + + //--- QgsSvgSelectorGroupsModel QgsSvgSelectorGroupsModel::QgsSvgSelectorGroupsModel( QObject* parent ) : QStandardItemModel( parent ) + , mLoader( new QgsSvgGroupLoader( this ) ) { QStringList svgPaths = QgsApplication::svgPaths(); QStandardItem *parentItem = invisibleRootItem(); + QStringList parentPaths; for ( int i = 0; i < svgPaths.size(); i++ ) { - QDir dir( svgPaths[i] ); + QDir dir( svgPaths.at( i ) ); QStandardItem *baseGroup; if ( dir.path().contains( QgsApplication::pkgDataPath() ) ) @@ -127,31 +307,41 @@ QgsSvgSelectorGroupsModel::QgsSvgSelectorGroupsModel( QObject* parent ) { baseGroup = new QStandardItem( dir.dirName() ); } - baseGroup->setData( QVariant( svgPaths[i] ) ); + baseGroup->setData( QVariant( svgPaths.at( i ) ) ); baseGroup->setEditable( false ); baseGroup->setCheckable( false ); baseGroup->setIcon( QgsApplication::style()->standardIcon( QStyle::SP_DirIcon ) ); baseGroup->setToolTip( dir.path() ); parentItem->appendRow( baseGroup ); - createTree( baseGroup ); + parentPaths << svgPaths.at( i ); + mPathItemHash.insert( svgPaths.at( i ), baseGroup ); QgsDebugMsg( QString( "SVG base path %1: %2" ).arg( i ).arg( baseGroup->data().toString() ) ); } + mLoader->setParentPaths( parentPaths ); + connect( mLoader, SIGNAL( foundPath( QString, QString ) ), this, SLOT( addPath( QString, QString ) ) ); + mLoader->start(); } -void QgsSvgSelectorGroupsModel::createTree( QStandardItem* &parentGroup ) +QgsSvgSelectorGroupsModel::~QgsSvgSelectorGroupsModel() { - QDir parentDir( parentGroup->data().toString() ); - Q_FOREACH ( const QString& item, parentDir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) ) - { - QStandardItem* group = new QStandardItem( item ); - group->setData( QVariant( parentDir.path() + '/' + item ) ); - group->setEditable( false ); - group->setCheckable( false ); - group->setToolTip( parentDir.path() + '/' + item ); - group->setIcon( QgsApplication::style()->standardIcon( QStyle::SP_DirIcon ) ); - parentGroup->appendRow( group ); - createTree( group ); - } + mLoader->stop(); +} + +void QgsSvgSelectorGroupsModel::addPath( const QString& parentPath, const QString& item ) +{ + QStandardItem* parentGroup = mPathItemHash.value( parentPath ); + if ( !parentGroup ) + return; + + QString fullPath = parentPath + '/' + item; + QStandardItem* group = new QStandardItem( item ); + group->setData( QVariant( fullPath ) ); + group->setEditable( false ); + group->setCheckable( false ); + group->setToolTip( fullPath ); + group->setIcon( QgsApplication::style()->standardIcon( QStyle::SP_DirIcon ) ); + parentGroup->appendRow( group ); + mPathItemHash.insert( fullPath, group ); } @@ -250,11 +440,14 @@ void QgsSvgSelectorWidget::populateIcons( const QModelIndex& idx ) { QString path = idx.data( Qt::UserRole + 1 ).toString(); + QAbstractItemModel* oldModel = mImagesListView->model(); QgsSvgSelectorListModel* m = new QgsSvgSelectorListModel( mImagesListView, path ); mImagesListView->setModel( m ); + delete oldModel; //explicitly delete old model to force any background threads to stop connect( mImagesListView->selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ), this, SLOT( svgSelectionChanged( const QModelIndex& ) ) ); + } void QgsSvgSelectorWidget::on_mFilePushButton_clicked() @@ -319,8 +512,10 @@ void QgsSvgSelectorWidget::populateList() } // Initally load the icons in the List view without any grouping + QAbstractItemModel* oldModel = mImagesListView->model(); QgsSvgSelectorListModel* m = new QgsSvgSelectorListModel( mImagesListView ); mImagesListView->setModel( m ); + delete oldModel; //explicitly delete old model to force any background threads to stop } //-- QgsSvgSelectorDialog @@ -357,3 +552,4 @@ QgsSvgSelectorDialog::~QgsSvgSelectorDialog() QSettings settings; settings.setValue( "/Windows/SvgSelectorDialog/geometry", saveGeometry() ); } + diff --git a/src/gui/symbology-ng/qgssvgselectorwidget.h b/src/gui/symbology-ng/qgssvgselectorwidget.h index e7ccbdc2709..f959d622e48 100644 --- a/src/gui/symbology-ng/qgssvgselectorwidget.h +++ b/src/gui/symbology-ng/qgssvgselectorwidget.h @@ -20,13 +20,14 @@ #include "ui_widget_svgselector.h" #include "qgisgui.h" - #include #include #include #include #include #include +#include +#include class QCheckBox; class QLayout; @@ -35,29 +36,172 @@ class QListView; class QPushButton; class QTreeView; +///@cond PRIVATE + +/** \ingroup gui + * \class QgsSvgSelectorLoader + * Recursively loads SVG images from a path in a background thread. + * \note added in QGIS 2.18 + */ +class GUI_EXPORT QgsSvgSelectorLoader : public QThread +{ + Q_OBJECT + + public: + + /** Constructor for QgsSvgSelectorLoader + * @param parent parent object + */ + QgsSvgSelectorLoader( QObject* parent = nullptr ); + + ~QgsSvgSelectorLoader(); + + /** Starts the loader finding and generating previews for SVG images. foundSvgs() will be + * emitted as the loader encounters SVG images. + * @brief run + */ + virtual void run() override; + + /** Cancels the current loading operation. Waits until the thread has finished operation + * before returning. + */ + virtual void stop(); + + /** Sets the root path containing SVG images to load. If no path is set, the default SVG + * search paths will be used instead. + */ + void setPath( const QString& path ) + { + mPath = path; + } + + signals: + + /** Emitted when the loader has found a block of SVG images. This signal is emitted with blocks + * of SVG images to prevent spamming any connected model. + * @param svgs list of SVGs and preview images found. + */ + void foundSvgs( QStringList svgs ); + + private: + + QString mPath; + bool mCancelled; + QStringList mQueuedSvgs; + + QElapsedTimer mTimer; + int mTimerThreshold; + + void loadPath( const QString& path ); + void loadImages( const QString& path ); + +}; + +/** \ingroup gui + * \class QgsSvgGroupLoader + * Recursively loads SVG paths in a background thread. + * \note added in QGIS 2.18 + */ +class GUI_EXPORT QgsSvgGroupLoader : public QThread +{ + Q_OBJECT + + public: + + /** Constructor for QgsSvgGroupLoader + * @param parent parent object + */ + QgsSvgGroupLoader( QObject* parent = nullptr ); + + ~QgsSvgGroupLoader(); + + /** Starts the loader finding folders for SVG images. + * @brief run + */ + virtual void run() override; + + /** Cancels the current loading operation. Waits until the thread has finished operation + * before returning. + */ + virtual void stop(); + + /** Sets the root path containing child paths to find. If no path is set, the default SVG + * search paths will be used instead. + */ + void setParentPaths( const QStringList& parentPaths ) + { + mParentPaths = parentPaths; + } + + signals: + + /** Emitted when the loader has found a block of SVG images. This signal is emitted with blocks + * of SVG images to prevent spamming any connected model. + * @param svgs list of SVGs and preview images found. + */ + void foundPath( const QString& parentPath, const QString& path ); + + private: + + QStringList mParentPaths; + bool mCancelled; + + void loadGroup( const QString& parentPath ); + +}; + +///@endcond +/// + /** \ingroup gui * \class QgsSvgSelectorListModel + * A model for displaying SVG files with a preview icon. Population of the model is performed in + * a background thread to ensure that initial creation of the model is responsive and does + * not block the GUI. */ class GUI_EXPORT QgsSvgSelectorListModel : public QAbstractListModel { Q_OBJECT public: + + /** Constructor for QgsSvgSelectorListModel. All SVGs in folders from the application SVG + * search paths will be shown. + * @param parent parent object + */ QgsSvgSelectorListModel( QObject* parent ); - // Constructor to create model for icons in a specific path + /** Constructor for creating a model for SVG files in a specific path. + * @param parent parent object + * @param path initial path, which is recursively searched + */ QgsSvgSelectorListModel( QObject* parent, const QString& path ); int rowCount( const QModelIndex & parent = QModelIndex() ) const override; - QVariant data( const QModelIndex & index, int role = Qt::DisplayRole ) const override; protected: QStringList mSvgFiles; + + private: + QPixmap createPreview( const QString& entry ) const; + QgsSvgSelectorLoader* mSvgLoader; + + private slots: + + /** Called to add SVG files to the model. + * @param svgs list of SVG files to add to model. + */ + void addSvgs( const QStringList& svgs ); + }; + /** \ingroup gui * \class QgsSvgSelectorGroupsModel + * A model for displaying SVG search paths. Population of the model is performed in + * a background thread to ensure that initial creation of the model is responsive and does + * not block the GUI. */ class GUI_EXPORT QgsSvgSelectorGroupsModel : public QStandardItemModel { @@ -65,9 +209,15 @@ class GUI_EXPORT QgsSvgSelectorGroupsModel : public QStandardItemModel public: QgsSvgSelectorGroupsModel( QObject* parent ); + ~QgsSvgSelectorGroupsModel(); private: - void createTree( QStandardItem* &parentGroup ); + QgsSvgGroupLoader* mLoader; + QHash< QString, QStandardItem* > mPathItemHash; + + private slots: + + void addPath( const QString& parentPath, const QString& path ); }; /** \ingroup gui @@ -114,6 +264,7 @@ class GUI_EXPORT QgsSvgSelectorWidget : public QWidget, private Ui::WidgetSvgSel private: QString mCurrentSvgPath; // always stored as absolute path + }; /** \ingroup gui diff --git a/src/gui/symbology-ng/qgssymbollayerwidget.cpp b/src/gui/symbology-ng/qgssymbollayerwidget.cpp index d42460459b0..9d614b0a660 100644 --- a/src/gui/symbology-ng/qgssymbollayerwidget.cpp +++ b/src/gui/symbology-ng/qgssymbollayerwidget.cpp @@ -34,6 +34,7 @@ #include "qgsmapcanvas.h" #include "qgsapplication.h" #include "qgsvectorlayer.h" +#include "qgssvgselectorwidget.h" #include "qgslogger.h" #include "qgssizescalewidget.h" @@ -1802,8 +1803,11 @@ QgsSvgMarkerSymbolLayerWidget::~QgsSvgMarkerSymbolLayerWidget() void QgsSvgMarkerSymbolLayerWidget::populateList() { - QgsSvgGroupsModel* g = new QgsSvgGroupsModel( viewGroups ); + QAbstractItemModel* oldModel = viewGroups->model(); + QgsSvgSelectorGroupsModel* g = new QgsSvgSelectorGroupsModel( viewGroups ); viewGroups->setModel( g ); + delete oldModel; + // Set the tree expanded at the first level int rows = g->rowCount( g->indexFromItem( g->invisibleRootItem() ) ); for ( int i = 0; i < rows; i++ ) @@ -1812,19 +1816,22 @@ void QgsSvgMarkerSymbolLayerWidget::populateList() } // Initally load the icons in the List view without any grouping - QgsSvgListModel* m = new QgsSvgListModel( viewImages ); + oldModel = viewImages->model(); + QgsSvgSelectorListModel* m = new QgsSvgSelectorListModel( viewImages ); viewImages->setModel( m ); + delete oldModel; } void QgsSvgMarkerSymbolLayerWidget::populateIcons( const QModelIndex& idx ) { QString path = idx.data( Qt::UserRole + 1 ).toString(); - QgsSvgListModel* m = new QgsSvgListModel( viewImages, path ); + QAbstractItemModel* oldModel = viewImages->model(); + QgsSvgSelectorListModel* m = new QgsSvgSelectorListModel( viewImages, path ); viewImages->setModel( m ); + delete oldModel; connect( viewImages->selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ), this, SLOT( setName( const QModelIndex& ) ) ); - emit changed(); } void QgsSvgMarkerSymbolLayerWidget::setGuiForSvg( const QgsSvgMarkerSymbolLayer* layer ) @@ -2285,8 +2292,11 @@ void QgsSVGFillSymbolLayerWidget::setFile( const QModelIndex& item ) void QgsSVGFillSymbolLayerWidget::insertIcons() { - QgsSvgGroupsModel* g = new QgsSvgGroupsModel( mSvgTreeView ); + QAbstractItemModel* oldModel = mSvgTreeView->model(); + QgsSvgSelectorGroupsModel* g = new QgsSvgSelectorGroupsModel( mSvgTreeView ); mSvgTreeView->setModel( g ); + delete oldModel; + // Set the tree expanded at the first level int rows = g->rowCount( g->indexFromItem( g->invisibleRootItem() ) ); for ( int i = 0; i < rows; i++ ) @@ -2294,19 +2304,22 @@ void QgsSVGFillSymbolLayerWidget::insertIcons() mSvgTreeView->setExpanded( g->indexFromItem( g->item( i ) ), true ); } - QgsSvgListModel* m = new QgsSvgListModel( mSvgListView ); + oldModel = mSvgListView->model(); + QgsSvgSelectorListModel* m = new QgsSvgSelectorListModel( mSvgListView ); mSvgListView->setModel( m ); + delete oldModel; } void QgsSVGFillSymbolLayerWidget::populateIcons( const QModelIndex& idx ) { QString path = idx.data( Qt::UserRole + 1 ).toString(); - QgsSvgListModel* m = new QgsSvgListModel( mSvgListView, path ); + QAbstractItemModel* oldModel = mSvgListView->model(); + QgsSvgSelectorListModel* m = new QgsSvgSelectorListModel( mSvgListView, path ); mSvgListView->setModel( m ); + delete oldModel; connect( mSvgListView->selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ), this, SLOT( setFile( const QModelIndex& ) ) ); - emit changed(); } @@ -3203,129 +3216,6 @@ void QgsRasterFillSymbolLayerWidget::updatePreviewImage() } -/// @cond PRIVATE - -QgsSvgListModel::QgsSvgListModel( QObject* parent ) : QAbstractListModel( parent ) -{ - mSvgFiles = QgsSymbolLayerUtils::listSvgFiles(); -} - -QgsSvgListModel::QgsSvgListModel( QObject* parent, const QString& path ) : QAbstractListModel( parent ) -{ - mSvgFiles = QgsSymbolLayerUtils::listSvgFilesAt( path ); -} - -int QgsSvgListModel::rowCount( const QModelIndex& parent ) const -{ - Q_UNUSED( parent ); - return mSvgFiles.count(); -} - -QVariant QgsSvgListModel::data( const QModelIndex& index, int role ) const -{ - QString entry = mSvgFiles.at( index.row() ); - - if ( role == Qt::DecorationRole ) // icon - { - QPixmap pixmap; - if ( !QPixmapCache::find( entry, pixmap ) ) - { - // render SVG file - QColor fill, outline; - double outlineWidth, fillOpacity, outlineOpacity; - bool fillParam, fillOpacityParam, outlineParam, outlineWidthParam, outlineOpacityParam; - bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultOutlineColor = false, - hasDefaultOutlineWidth = false, hasDefaultOutlineOpacity = false; - QgsSvgCache::instance()->containsParams( entry, fillParam, hasDefaultFillColor, fill, - fillOpacityParam, hasDefaultFillOpacity, fillOpacity, - outlineParam, hasDefaultOutlineColor, outline, - outlineWidthParam, hasDefaultOutlineWidth, outlineWidth, - outlineOpacityParam, hasDefaultOutlineOpacity, outlineOpacity ); - - //if defaults not set in symbol, use these values - if ( !hasDefaultFillColor ) - fill = QColor( 200, 200, 200 ); - fill.setAlphaF( hasDefaultFillOpacity ? fillOpacity : 1.0 ); - if ( !hasDefaultOutlineColor ) - outline = Qt::black; - outline.setAlphaF( hasDefaultOutlineOpacity ? outlineOpacity : 1.0 ); - if ( !hasDefaultOutlineWidth ) - outlineWidth = 0.6; - - bool fitsInCache; // should always fit in cache at these sizes (i.e. under 559 px ^ 2, or half cache size) - const QImage& img = QgsSvgCache::instance()->svgAsImage( entry, 30.0, fill, outline, outlineWidth, 3.5 /*appr. 88 dpi*/, 1.0, fitsInCache ); - pixmap = QPixmap::fromImage( img ); - QPixmapCache::insert( entry, pixmap ); - } - - return pixmap; - } - else if ( role == Qt::UserRole || role == Qt::ToolTipRole ) - { - return entry; - } - - return QVariant(); -} - - -QgsSvgGroupsModel::QgsSvgGroupsModel( QObject* parent ) : QStandardItemModel( parent ) -{ - QStringList svgPaths = QgsApplication::svgPaths(); - QStandardItem *parentItem = invisibleRootItem(); - - for ( int i = 0; i < svgPaths.size(); i++ ) - { - QDir dir( svgPaths[i] ); - QStandardItem *baseGroup; - - if ( dir.path().contains( QgsApplication::pkgDataPath() ) ) - { - baseGroup = new QStandardItem( QString( "App Symbols" ) ); - } - else if ( dir.path().contains( QgsApplication::qgisSettingsDirPath() ) ) - { - baseGroup = new QStandardItem( QString( "User Symbols" ) ); - } - else - { - baseGroup = new QStandardItem( dir.dirName() ); - } - baseGroup->setData( QVariant( svgPaths[i] ) ); - baseGroup->setEditable( false ); - baseGroup->setCheckable( false ); - baseGroup->setIcon( QgsApplication::style()->standardIcon( QStyle::SP_DirIcon ) ); - baseGroup->setToolTip( dir.path() ); - parentItem->appendRow( baseGroup ); - createTree( baseGroup ); - QgsDebugMsg( QString( "SVG base path %1: %2" ).arg( i ).arg( baseGroup->data().toString() ) ); - } -} - -void QgsSvgGroupsModel::createTree( QStandardItem*& parentGroup ) -{ - QDir parentDir( parentGroup->data().toString() ); - Q_FOREACH ( const QString& item, parentDir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) ) - { - QStandardItem* group = new QStandardItem( item ); - group->setData( QVariant( parentDir.path() + '/' + item ) ); - group->setEditable( false ); - group->setCheckable( false ); - group->setToolTip( parentDir.path() + '/' + item ); - group->setIcon( QgsApplication::style()->standardIcon( QStyle::SP_DirIcon ) ); - parentGroup->appendRow( group ); - createTree( group ); - } -} - - -/// @endcond - - - - - - QgsGeometryGeneratorSymbolLayerWidget::QgsGeometryGeneratorSymbolLayerWidget( const QgsVectorLayer* vl, QWidget* parent ) : QgsSymbolLayerWidget( parent, vl ) , mLayer( nullptr ) diff --git a/src/gui/symbology-ng/qgssymbollayerwidget.h b/src/gui/symbology-ng/qgssymbollayerwidget.h index 732f2926fcb..02059124187 100644 --- a/src/gui/symbology-ng/qgssymbollayerwidget.h +++ b/src/gui/symbology-ng/qgssymbollayerwidget.h @@ -707,39 +707,6 @@ class GUI_EXPORT QgsCentroidFillSymbolLayerWidget : public QgsSymbolLayerWidget, }; -///@cond PRIVATE - -class QgsSvgListModel : public QAbstractListModel -{ - Q_OBJECT - - public: - explicit QgsSvgListModel( QObject* parent ); - - // Constructor to create model for icons in a specific path - QgsSvgListModel( QObject* parent, const QString& path ); - - int rowCount( const QModelIndex & parent = QModelIndex() ) const override; - - QVariant data( const QModelIndex & index, int role = Qt::DisplayRole ) const override; - - protected: - QStringList mSvgFiles; -}; - -class QgsSvgGroupsModel : public QStandardItemModel -{ - Q_OBJECT - - public: - explicit QgsSvgGroupsModel( QObject* parent ); - - private: - void createTree( QStandardItem* &parentGroup ); -}; - -///@endcond - #include "ui_qgsgeometrygeneratorwidgetbase.h" class QgsGeometryGeneratorSymbolLayer;