28 #include <QAbstractListModel>
31 #include <QFileDialog>
32 #include <QModelIndex>
33 #include <QPixmapCache>
41 QgsSvgSelectorLoader::QgsSvgSelectorLoader( QObject *parent )
46 QgsSvgSelectorLoader::~QgsSvgSelectorLoader()
51 void QgsSvgSelectorLoader::run()
55 mTraversedPaths.clear();
63 if ( !mQueuedSvgs.isEmpty() )
66 emit foundSvgs( mQueuedSvgs );
71 void QgsSvgSelectorLoader::stop()
74 while ( isRunning() ) {}
77 void QgsSvgSelectorLoader::loadPath(
const QString &path )
87 const auto constSvgPaths = svgPaths;
88 for (
const QString &svgPath : constSvgPaths )
93 if ( !svgPath.isEmpty() )
104 QString canonicalPath = dir.canonicalPath();
105 if ( mTraversedPaths.contains( canonicalPath ) )
108 mTraversedPaths.insert( canonicalPath );
112 const auto constEntryList = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot );
113 for (
const QString &item : constEntryList )
118 QString newPath = dir.path() +
'/' + item;
125 void QgsSvgSelectorLoader::loadImages(
const QString &path )
128 const auto constEntryList = dir.entryList( QStringList(
"*.svg" ), QDir::Files );
129 for (
const QString &item : constEntryList )
135 QString svgPath = dir.path() +
'/' + item;
139 mQueuedSvgs << svgPath;
143 if ( mTimer.elapsed() > mTimerThreshold && !mQueuedSvgs.isEmpty() )
145 emit foundSvgs( mQueuedSvgs );
151 if ( mTimerThreshold < 1000 )
152 mTimerThreshold *= 2;
163 QgsSvgGroupLoader::QgsSvgGroupLoader( QObject *parent )
169 QgsSvgGroupLoader::~QgsSvgGroupLoader()
174 void QgsSvgGroupLoader::run()
177 mTraversedPaths.clear();
179 while ( !mCanceled && !mParentPaths.isEmpty() )
181 QString parentPath = mParentPaths.takeFirst();
182 loadGroup( parentPath );
186 void QgsSvgGroupLoader::stop()
189 while ( isRunning() ) {}
192 void QgsSvgGroupLoader::loadGroup(
const QString &parentPath )
194 QDir parentDir( parentPath );
197 QString canonicalPath = parentDir.canonicalPath();
198 if ( mTraversedPaths.contains( canonicalPath ) )
201 mTraversedPaths.insert( canonicalPath );
203 const auto constEntryList = parentDir.entryList( QDir::Dirs | QDir::NoDotAndDotDot );
204 for (
const QString &item : constEntryList )
209 emit foundPath( parentPath, item );
210 mParentPaths.append( parentDir.path() +
'/' + item );
221 : QAbstractListModel( parent )
222 , mSvgLoader( new QgsSvgSelectorLoader( this ) )
225 mSvgLoader->setPath( QString() );
226 connect( mSvgLoader, &QgsSvgSelectorLoader::foundSvgs,
this, &QgsSvgSelectorListModel::addSvgs );
231 : QAbstractListModel( parent )
232 , mSvgLoader( new QgsSvgSelectorLoader( this ) )
235 mSvgLoader->setPath( path );
236 connect( mSvgLoader, &QgsSvgSelectorLoader::foundSvgs,
this, &QgsSvgSelectorListModel::addSvgs );
246 QPixmap QgsSvgSelectorListModel::createPreview(
const QString &entry )
const
250 double strokeWidth, fillOpacity, strokeOpacity;
251 bool fillParam, fillOpacityParam, strokeParam, strokeWidthParam, strokeOpacityParam;
252 bool hasDefaultFillColor =
false, hasDefaultFillOpacity =
false, hasDefaultStrokeColor =
false,
253 hasDefaultStrokeWidth =
false, hasDefaultStrokeOpacity =
false;
255 fillOpacityParam, hasDefaultFillOpacity, fillOpacity,
256 strokeParam, hasDefaultStrokeColor, stroke,
257 strokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
258 strokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
261 if ( !hasDefaultFillColor )
262 fill = QColor( 200, 200, 200 );
263 fill.setAlphaF( hasDefaultFillOpacity ? fillOpacity : 1.0 );
264 if ( !hasDefaultStrokeColor )
266 stroke.setAlphaF( hasDefaultStrokeOpacity ? strokeOpacity : 1.0 );
267 if ( !hasDefaultStrokeWidth )
272 return QPixmap::fromImage( img );
277 QString entry =
mSvgFiles.at( index.row() );
279 if ( role == Qt::DecorationRole )
281 QPixmap *pixmap =
nullptr;
282 if ( !QPixmapCache::find( entry, pixmap ) || !pixmap )
284 QPixmap newPixmap = createPreview( entry );
285 QPixmapCache::insert( entry, newPixmap );
293 else if ( role == Qt::UserRole || role == Qt::ToolTipRole )
301 void QgsSvgSelectorListModel::addSvgs(
const QStringList &svgs )
303 beginInsertRows( QModelIndex(),
mSvgFiles.count(),
mSvgFiles.count() + svgs.size() - 1 );
315 : QStandardItemModel( parent )
316 , mLoader( new QgsSvgGroupLoader( this ) )
319 QStandardItem *parentItem = invisibleRootItem();
320 QStringList parentPaths;
321 parentPaths.reserve( svgPaths.size() );
323 for (
int i = 0; i < svgPaths.size(); i++ )
325 QDir dir( svgPaths.at( i ) );
326 QStandardItem *baseGroup =
nullptr;
330 baseGroup =
new QStandardItem( tr(
"App Symbols" ) );
334 baseGroup =
new QStandardItem( tr(
"User Symbols" ) );
338 baseGroup =
new QStandardItem( dir.dirName() );
340 baseGroup->setData( QVariant( svgPaths.at( i ) ) );
341 baseGroup->setEditable(
false );
342 baseGroup->setCheckable(
false );
344 baseGroup->setToolTip( dir.path() );
345 parentItem->appendRow( baseGroup );
346 parentPaths << svgPaths.at( i );
347 mPathItemHash.insert( svgPaths.at( i ), baseGroup );
348 QgsDebugMsg( QStringLiteral(
"SVG base path %1: %2" ).arg( i ).arg( baseGroup->data().toString() ) );
350 mLoader->setParentPaths( parentPaths );
351 connect( mLoader, &QgsSvgGroupLoader::foundPath,
this, &QgsSvgSelectorGroupsModel::addPath );
360 void QgsSvgSelectorGroupsModel::addPath(
const QString &parentPath,
const QString &item )
362 QStandardItem *parentGroup = mPathItemHash.value( parentPath );
366 QString fullPath = parentPath +
'/' + item;
367 QStandardItem *group =
new QStandardItem( item );
368 group->setData( QVariant( fullPath ) );
369 group->setEditable(
false );
370 group->setCheckable(
false );
371 group->setToolTip( fullPath );
373 parentGroup->appendRow( group );
374 mPathItemHash.insert( fullPath, group );
388 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
389 mIconSize = std::max( 30,
static_cast< int >( std::round(
Qgis::UI_SCALE_FACTOR * fontMetrics().width(
'X' ) * 3 ) ) );
391 mIconSize = std::max( 30,
static_cast< int >( std::round(
Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance(
'X' ) * 3 ) ) );
393 mImagesListView->setGridSize( QSize( mIconSize * 1.2, mIconSize * 1.2 ) );
395 mGroupsTreeView->setHeaderHidden(
true );
398 connect( mImagesListView->selectionModel(), &QItemSelectionModel::currentChanged,
399 this, &QgsSvgSelectorWidget::svgSelectionChanged );
400 connect( mGroupsTreeView->selectionModel(), &QItemSelectionModel::currentChanged,
401 this, &QgsSvgSelectorWidget::populateIcons );
406 mCurrentSvgPath = svgPath;
410 mImagesListView->selectionModel()->blockSignals(
true );
411 QAbstractItemModel *m = mImagesListView->model();
412 QItemSelectionModel *selModel = mImagesListView->selectionModel();
413 for (
int i = 0; i < m->rowCount(); i++ )
415 QModelIndex idx( m->index( i, 0 ) );
416 if ( m->data( idx ).toString() == svgPath )
418 selModel->select( idx, QItemSelectionModel::SelectCurrent );
419 selModel->setCurrentIndex( idx, QItemSelectionModel::SelectCurrent );
420 mImagesListView->scrollTo( idx );
424 mImagesListView->selectionModel()->blockSignals(
false );
429 return mCurrentSvgPath;
432 void QgsSvgSelectorWidget::updateCurrentSvgPath(
const QString &svgPath )
434 mCurrentSvgPath = svgPath;
438 void QgsSvgSelectorWidget::svgSelectionChanged(
const QModelIndex &idx )
440 QString filePath = idx.data( Qt::UserRole ).toString();
442 updateCurrentSvgPath( filePath );
445 void QgsSvgSelectorWidget::populateIcons(
const QModelIndex &idx )
447 QString path = idx.data( Qt::UserRole + 1 ).toString();
449 QAbstractItemModel *oldModel = mImagesListView->model();
451 mImagesListView->setModel( m );
454 connect( mImagesListView->selectionModel(), &QItemSelectionModel::currentChanged,
455 this, &QgsSvgSelectorWidget::svgSelectionChanged );
458 void QgsSvgSelectorWidget::svgSourceChanged(
const QString &text )
461 bool validSVG = !resolvedPath.isNull();
463 updateCurrentSvgPath( validSVG ? resolvedPath : text );
469 mGroupsTreeView->setModel( g );
471 int rows = g->rowCount( g->indexFromItem( g->invisibleRootItem() ) );
472 for (
int i = 0; i < rows; i++ )
474 mGroupsTreeView->setExpanded( g->indexFromItem( g->item( i ) ),
true );
478 QAbstractItemModel *oldModel = mImagesListView->model();
480 mImagesListView->setModel( m );
487 QDialogButtonBox::StandardButtons buttons,
488 Qt::Orientation orientation )
489 : QDialog( parent, fl )
492 Q_UNUSED( orientation )
495 mButtonBox =
new QDialogButtonBox( buttons, orientation,
this );
496 connect(
mButtonBox, &QDialogButtonBox::accepted,
this, &QDialog::accept );
497 connect(
mButtonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject );
499 setMinimumSize( 480, 320 );