QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgsstylemanagerdialog.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsstylemanagerdialog.cpp
3 ---------------------
4 begin : November 2009
5 copyright : (C) 2009 by Martin Dobias
6 email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include "qgs3dsymbolregistry.h"
19#include "qgs3dsymbolwidget.h"
20#include "qgsabstract3dsymbol.h"
21#include "qgsapplication.h"
23#include "qgscolorramp.h"
25#include "qgsfileutils.h"
26#include "qgsfillsymbol.h"
28#include "qgsiconutils.h"
29#include "qgslabelinggui.h"
32#include "qgslinesymbol.h"
33#include "qgslogger.h"
34#include "qgsmarkersymbol.h"
35#include "qgsmessagebar.h"
37#include "qgsproject.h"
39#include "qgssettings.h"
43#include "qgsstylemodel.h"
44#include "qgsstylesavedialog.h"
45#include "qgssymbol.h"
46#include "qgssymbollayerutils.h"
48#include "qgstextformatwidget.h"
49
50#include <QAction>
51#include <QClipboard>
52#include <QDesktopServices>
53#include <QFile>
54#include <QFileDialog>
55#include <QInputDialog>
56#include <QMenu>
57#include <QMessageBox>
58#include <QPushButton>
59#include <QShortcut>
60#include <QStandardItemModel>
61#include <QUrl>
62
63#include "moc_qgsstylemanagerdialog.cpp"
64
65const QgsSettingsEntryString *QgsStyleManagerDialog::settingLastStyleDatabaseFolder = new QgsSettingsEntryString( QStringLiteral( "last-style-database-folder" ), sTtreeStyleManager, QString(), QStringLiteral( "Last used folder for style databases" ) );
66
67//
68// QgsCheckableStyleModel
69//
70
72QgsCheckableStyleModel::QgsCheckableStyleModel( QgsStyleModel *sourceModel, QObject *parent, bool readOnly )
73 : QgsStyleProxyModel( sourceModel, parent )
74 , mStyle( sourceModel->style() )
75 , mReadOnly( readOnly )
76{
77}
78
79QgsCheckableStyleModel::QgsCheckableStyleModel( QgsStyle *style, QObject *parent, bool readOnly )
80 : QgsStyleProxyModel( style, parent )
81 , mStyle( style )
82 , mReadOnly( readOnly )
83{
84}
85
86void QgsCheckableStyleModel::setCheckable( bool checkable )
87{
88 if ( checkable == mCheckable )
89 return;
90
91 mCheckable = checkable;
92 emit dataChanged( index( 0, 0 ), index( rowCount() - 1, 0 ), QVector<int>() << Qt::CheckStateRole );
93}
94
95void QgsCheckableStyleModel::setCheckTag( const QString &tag )
96{
97 if ( tag == mCheckTag )
98 return;
99
100 mCheckTag = tag;
101 emit dataChanged( index( 0, 0 ), index( rowCount() - 1, 0 ), QVector<int>() << Qt::CheckStateRole );
102}
103
104Qt::ItemFlags QgsCheckableStyleModel::flags( const QModelIndex &index ) const
105{
106 Qt::ItemFlags f = QgsStyleProxyModel::flags( index );
107 if ( !mReadOnly && mCheckable && index.column() == 0 )
108 f |= Qt::ItemIsUserCheckable;
109
110 if ( mReadOnly )
111 f &= ~Qt::ItemIsEditable;
112
113 return f;
114}
115
116QVariant QgsCheckableStyleModel::data( const QModelIndex &index, int role ) const
117{
118 switch ( role )
119 {
120 case Qt::FontRole:
121 {
122 // drop font size to get reasonable amount of item name shown
123 QFont f = QgsStyleProxyModel::data( index, role ).value<QFont>();
124 f.setPointSize( 9 );
125 return f;
126 }
127
128 case Qt::CheckStateRole:
129 {
130 if ( !mCheckable || index.column() != 0 )
131 return QVariant();
132
133 const QStringList tags = data( index, static_cast<int>( QgsStyleModel::CustomRole::Tag ) ).toStringList();
134 return tags.contains( mCheckTag ) ? Qt::Checked : Qt::Unchecked;
135 }
136
137 default:
138 break;
139 }
140 return QgsStyleProxyModel::data( index, role );
141}
142
143bool QgsCheckableStyleModel::setData( const QModelIndex &i, const QVariant &value, int role )
144{
145 if ( i.row() < 0 || i.row() >= rowCount( QModelIndex() ) || ( role != Qt::EditRole && role != Qt::CheckStateRole ) )
146 return false;
147
148 if ( mReadOnly )
149 return false;
150
151 if ( role == Qt::CheckStateRole )
152 {
153 if ( !mCheckable || mCheckTag.isEmpty() )
154 return false;
155
156 const QString name = data( index( i.row(), QgsStyleModel::Name ), Qt::DisplayRole ).toString();
157 const QgsStyle::StyleEntity entity = static_cast<QgsStyle::StyleEntity>( data( i, static_cast<int>( QgsStyleModel::CustomRole::Type ) ).toInt() );
158
159 if ( value.toInt() == Qt::Checked )
160 return mStyle->tagSymbol( entity, name, QStringList() << mCheckTag );
161 else
162 return mStyle->detagSymbol( entity, name, QStringList() << mCheckTag );
163 }
164 return QgsStyleProxyModel::setData( i, value, role );
165}
167
168//
169// QgsStyleManagerDialog
170//
171
172#include "qgsgui.h"
173
174QString QgsStyleManagerDialog::sPreviousTag;
175
176QgsStyleManagerDialog::QgsStyleManagerDialog( QgsStyle *style, QWidget *parent, Qt::WindowFlags flags, bool readOnly )
177 : QDialog( parent, flags )
178 , mReadOnly( readOnly )
179{
180 init();
181 setCurrentStyle( style );
182 mStyleDatabaseWidget->hide();
183}
184
185QgsStyleManagerDialog::QgsStyleManagerDialog( QWidget *parent, Qt::WindowFlags flags )
186 : QDialog( parent, flags )
187{
188 init();
189
190 mProjectStyleModel = new QgsProjectStyleDatabaseModel( QgsProject::instance()->styleSettings(), this );
191 mProjectStyleModel->setShowDefaultStyle( true );
192 mComboBoxStyleDatabase->setModel( mProjectStyleModel );
193
194 setCurrentStyle( QgsStyle::defaultStyle() );
195
196 connect( mComboBoxStyleDatabase, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this]() {
197 if ( mBlockStyleDatabaseChanges )
198 return;
199
200 const QModelIndex index = mProjectStyleModel->index( mComboBoxStyleDatabase->currentIndex(), 0, QModelIndex() );
201 setCurrentStyle( mProjectStyleModel->styleFromIndex( index ) );
202 } );
203
204 connect( mButtonAddStyleDatabase, &QAbstractButton::clicked, this, [this] { addStyleDatabase( false ); } );
205 connect( mButtonNewStyleDatabase, &QAbstractButton::clicked, this, [this] { addStyleDatabase( true ); } );
206}
207
208void QgsStyleManagerDialog::init()
209{
210 setupUi( this );
212 connect( tabItemType, &QTabWidget::currentChanged, this, &QgsStyleManagerDialog::tabItemType_currentChanged );
213 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsStyleManagerDialog::showHelp );
214 connect( buttonBox, &QDialogButtonBox::rejected, this, &QgsStyleManagerDialog::onClose );
215
216 QPushButton *downloadButton = buttonBox->addButton( tr( "Browse Online Styles" ), QDialogButtonBox::ResetRole );
217 downloadButton->setToolTip( tr( "Download new styles from the online QGIS style repository" ) );
218 downloadButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFindReplace.svg" ) ) );
219 connect( downloadButton, &QPushButton::clicked, this, [] {
220 QDesktopServices::openUrl( QUrl( QStringLiteral( "https://hub.qgis.org/styles/" ) ) );
221 } );
222
223 mMessageBar = new QgsMessageBar();
224 mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
225 mVerticalLayout->insertWidget( 0, mMessageBar );
226
227#ifdef Q_OS_MAC
228 setWindowModality( Qt::WindowModal );
229#endif
230
231 QgsSettings settings;
232
233 mSplitter->setSizes( QList<int>() << 170 << 540 );
234 mSplitter->restoreState( settings.value( QStringLiteral( "Windows/StyleV2Manager/splitter" ) ).toByteArray() );
235
236 tabItemType->setDocumentMode( true );
237 searchBox->setShowSearchIcon( true );
238 searchBox->setPlaceholderText( tr( "Filter symbols…" ) );
239
240 connect( this, &QDialog::finished, this, &QgsStyleManagerDialog::onFinished );
241 connect( listItems, &QAbstractItemView::doubleClicked, this, &QgsStyleManagerDialog::editItem );
242 connect( btnEditItem, &QPushButton::clicked, this, [this]( bool ) { editItem(); } );
243 connect( actnEditItem, &QAction::triggered, this, [this]( bool ) { editItem(); } );
244
245
246 connect( btnAddItem, &QPushButton::clicked, this, [this]( bool ) {
247 // only show add item if the btn doesn't have a menu -- otherwise it should show the menu instead!
248 if ( !btnAddItem->menu() )
249 {
250 addItem();
251 }
252 } );
253
254 connect( btnRemoveItem, &QPushButton::clicked, this, [this]( bool ) { removeItem(); } );
255 connect( actnRemoveItem, &QAction::triggered, this, [this]( bool ) { removeItem(); } );
256
257 mShareMenu = new QMenu( tr( "Share Menu" ), this );
258 mExportAction = new QAction( tr( "Export Item(s)…" ), this );
259 mExportAction->setIcon( QIcon( QgsApplication::iconPath( "mActionFileSave.svg" ) ) );
260 mShareMenu->addAction( mExportAction );
261
262 connect( mCopyToDefaultButton, &QPushButton::clicked, this, &QgsStyleManagerDialog::copyItemsToDefault );
263
264 mActionCopyItem = new QAction( tr( "Copy Item" ), this );
265 connect( mActionCopyItem, &QAction::triggered, this, &QgsStyleManagerDialog::copyItem );
266 mActionPasteItem = new QAction( tr( "Paste Item…" ), this );
267 connect( mActionPasteItem, &QAction::triggered, this, &QgsStyleManagerDialog::pasteItem );
268
269 QShortcut *copyShortcut = new QShortcut( QKeySequence( QKeySequence::StandardKey::Copy ), this );
270 connect( copyShortcut, &QShortcut::activated, this, &QgsStyleManagerDialog::copyItem );
271 QShortcut *pasteShortcut = new QShortcut( QKeySequence( QKeySequence::StandardKey::Paste ), this );
272 connect( pasteShortcut, &QShortcut::activated, this, &QgsStyleManagerDialog::pasteItem );
273 QShortcut *removeShortcut = new QShortcut( QKeySequence( QKeySequence::StandardKey::Delete ), this );
274 connect( removeShortcut, &QShortcut::activated, this, &QgsStyleManagerDialog::removeItem );
275 QShortcut *editShortcut = new QShortcut( QKeySequence( Qt::Key_Return ), this );
276 connect( editShortcut, &QShortcut::activated, this, &QgsStyleManagerDialog::editItem );
277
278 mShareMenu->addSeparator();
279 mShareMenu->addAction( actnExportAsPNG );
280 mShareMenu->addAction( actnExportAsSVG );
281
282 connect( actnExportAsPNG, &QAction::triggered, this, &QgsStyleManagerDialog::exportItemsPNG );
283 connect( actnExportAsSVG, &QAction::triggered, this, &QgsStyleManagerDialog::exportItemsSVG );
284 connect( mExportAction, &QAction::triggered, this, &QgsStyleManagerDialog::exportItems );
285 btnShare->setMenu( mShareMenu );
286
287 listItems->setTextElideMode( Qt::TextElideMode::ElideRight );
288 double treeIconSize = Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 2;
289 mSymbolTreeView->setIconSize( QSize( static_cast<int>( treeIconSize ), static_cast<int>( treeIconSize ) ) );
290
291 listItems->setSelectionBehavior( QAbstractItemView::SelectRows );
292 listItems->setSelectionMode( QAbstractItemView::ExtendedSelection );
293 mSymbolTreeView->setSelectionMode( listItems->selectionMode() );
294
295 QStandardItemModel *groupModel = new QStandardItemModel( groupTree );
296 groupTree->setModel( groupModel );
297 groupTree->setHeaderHidden( true );
298
299 connect( groupTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsStyleManagerDialog::groupChanged );
300 connect( groupModel, &QStandardItemModel::itemChanged, this, &QgsStyleManagerDialog::groupRenamed );
301
302 QMenu *groupMenu = new QMenu( tr( "Group Actions" ), this );
303 connect( actnTagSymbols, &QAction::triggered, this, &QgsStyleManagerDialog::tagSymbolsAction );
304 groupMenu->addAction( actnTagSymbols );
305 connect( actnFinishTagging, &QAction::triggered, this, &QgsStyleManagerDialog::tagSymbolsAction );
306 actnFinishTagging->setVisible( false );
307 groupMenu->addAction( actnFinishTagging );
308 groupMenu->addAction( actnEditSmartGroup );
309 btnManageGroups->setMenu( groupMenu );
310
311 connect( searchBox, &QLineEdit::textChanged, this, &QgsStyleManagerDialog::filterSymbols );
312
313 // Context menu for groupTree
314 groupTree->setContextMenuPolicy( Qt::CustomContextMenu );
315 connect( groupTree, &QWidget::customContextMenuRequested, this, &QgsStyleManagerDialog::grouptreeContextMenu );
316
317 // Context menu for listItems
318 listItems->setContextMenuPolicy( Qt::CustomContextMenu );
319 connect( listItems, &QWidget::customContextMenuRequested, this, &QgsStyleManagerDialog::listitemsContextMenu );
320 mSymbolTreeView->setContextMenuPolicy( Qt::CustomContextMenu );
321 connect( mSymbolTreeView, &QWidget::customContextMenuRequested, this, &QgsStyleManagerDialog::listitemsContextMenu );
322
323 mMenuBtnAddItemAll = new QMenu( this );
324 mMenuBtnAddItemColorRamp = new QMenu( this );
325 mMenuBtnAddItemLabelSettings = new QMenu( this );
326 mMenuBtnAddItemLegendPatchShape = new QMenu( this );
327 mMenuBtnAddItemSymbol3D = new QMenu( this );
328
329 QAction *item = new QAction( QgsIconUtils::iconPoint(), tr( "Marker…" ), this );
330 connect( item, &QAction::triggered, this, [this]( bool ) { addSymbol( static_cast<int>( Qgis::SymbolType::Marker ) ); } );
331 mMenuBtnAddItemAll->addAction( item );
332 item = new QAction( QgsIconUtils::iconLine(), tr( "Line…" ), this );
333 connect( item, &QAction::triggered, this, [this]( bool ) { addSymbol( static_cast<int>( Qgis::SymbolType::Line ) ); } );
334 mMenuBtnAddItemAll->addAction( item );
335 item = new QAction( QgsIconUtils::iconPolygon(), tr( "Fill…" ), this );
336 connect( item, &QAction::triggered, this, [this]( bool ) { addSymbol( static_cast<int>( Qgis::SymbolType::Fill ) ); } );
337 mMenuBtnAddItemAll->addAction( item );
338 mMenuBtnAddItemAll->addSeparator();
339
340 const QList<QPair<QString, QString>> rampTypes = QgsColorRamp::rampTypes();
341 for ( const QPair<QString, QString> &rampType : rampTypes )
342 {
343 item = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "styleicons/color.svg" ) ), tr( "%1…" ).arg( rampType.second ), this );
344 connect( item, &QAction::triggered, this, [this, rampType]( bool ) { addColorRamp( rampType.first ); } );
345 mMenuBtnAddItemAll->addAction( item );
346 mMenuBtnAddItemColorRamp->addAction( item );
347 }
348 mMenuBtnAddItemAll->addSeparator();
349 item = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "mIconFieldText.svg" ) ), tr( "Text Format…" ), this );
350 connect( item, &QAction::triggered, this, [this]( bool ) { addTextFormat(); } );
351 mMenuBtnAddItemAll->addAction( item );
352 mMenuBtnAddItemAll->addSeparator();
353 item = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "labelingSingle.svg" ) ), tr( "Point Label Settings…" ), this );
354 connect( item, &QAction::triggered, this, [this]( bool ) { addLabelSettings( Qgis::GeometryType::Point ); } );
355 mMenuBtnAddItemAll->addAction( item );
356 mMenuBtnAddItemLabelSettings->addAction( item );
357 item = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "labelingSingle.svg" ) ), tr( "Line Label Settings…" ), this );
358 connect( item, &QAction::triggered, this, [this]( bool ) { addLabelSettings( Qgis::GeometryType::Line ); } );
359 mMenuBtnAddItemAll->addAction( item );
360 mMenuBtnAddItemLabelSettings->addAction( item );
361 item = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "labelingSingle.svg" ) ), tr( "Polygon Label Settings…" ), this );
362 connect( item, &QAction::triggered, this, [this]( bool ) { addLabelSettings( Qgis::GeometryType::Polygon ); } );
363 mMenuBtnAddItemAll->addAction( item );
364 mMenuBtnAddItemLabelSettings->addAction( item );
365
366 mMenuBtnAddItemAll->addSeparator();
367 item = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "legend.svg" ) ), tr( "Marker Legend Patch Shape…" ), this );
368 connect( item, &QAction::triggered, this, [this]( bool ) { addLegendPatchShape( Qgis::SymbolType::Marker ); } );
369 mMenuBtnAddItemAll->addAction( item );
370 mMenuBtnAddItemLegendPatchShape->addAction( item );
371 item = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "legend.svg" ) ), tr( "Line Legend Patch Shape…" ), this );
372 connect( item, &QAction::triggered, this, [this]( bool ) { addLegendPatchShape( Qgis::SymbolType::Line ); } );
373 mMenuBtnAddItemAll->addAction( item );
374 mMenuBtnAddItemLegendPatchShape->addAction( item );
375 item = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "legend.svg" ) ), tr( "Fill Legend Patch Shape…" ), this );
376 connect( item, &QAction::triggered, this, [this]( bool ) { addLegendPatchShape( Qgis::SymbolType::Fill ); } );
377 mMenuBtnAddItemAll->addAction( item );
378 mMenuBtnAddItemLegendPatchShape->addAction( item );
379
380 mMenuBtnAddItemAll->addSeparator();
381 item = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "3d.svg" ) ), tr( "3D Point Symbol…" ), this );
382 connect( item, &QAction::triggered, this, [this]( bool ) { addSymbol3D( QStringLiteral( "point" ) ); } );
383 mMenuBtnAddItemAll->addAction( item );
384 mMenuBtnAddItemSymbol3D->addAction( item );
385 item = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "3d.svg" ) ), tr( "3D Line Symbol…" ), this );
386 connect( item, &QAction::triggered, this, [this]( bool ) { addSymbol3D( QStringLiteral( "line" ) ); } );
387 mMenuBtnAddItemAll->addAction( item );
388 mMenuBtnAddItemSymbol3D->addAction( item );
389 item = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "3d.svg" ) ), tr( "3D Polygon Symbol…" ), this );
390 connect( item, &QAction::triggered, this, [this]( bool ) { addSymbol3D( QStringLiteral( "polygon" ) ); } );
391 mMenuBtnAddItemAll->addAction( item );
392 mMenuBtnAddItemSymbol3D->addAction( item );
393
394 // Context menu for symbols/colorramps. The menu entries for every group are created when displaying the menu.
395 mGroupMenu = new QMenu( this );
396 mGroupListMenu = new QMenu( mGroupMenu );
397 mGroupListMenu->setTitle( tr( "Add to Tag" ) );
398 mGroupListMenu->setEnabled( false );
399
400 connect( actnAddFavorite, &QAction::triggered, this, &QgsStyleManagerDialog::addFavoriteSelectedSymbols );
401 connect( actnRemoveFavorite, &QAction::triggered, this, &QgsStyleManagerDialog::removeFavoriteSelectedSymbols );
402 connect( actnDetag, &QAction::triggered, this, &QgsStyleManagerDialog::detagSelectedSymbols );
403
404 // Context menu for the group tree
405 mGroupTreeContextMenu = new QMenu( this );
406 connect( actnEditSmartGroup, &QAction::triggered, this, &QgsStyleManagerDialog::editSmartgroupAction );
407 connect( actnAddTag, &QAction::triggered, this, [this]( bool ) { addTag(); } );
408 connect( actnAddSmartgroup, &QAction::triggered, this, [this]( bool ) { addSmartgroup(); } );
409 connect( actnRemoveGroup, &QAction::triggered, this, &QgsStyleManagerDialog::removeGroup );
410
411 tabItemType_currentChanged( 0 );
412
413 connect( mButtonIconView, &QToolButton::toggled, this, [this]( bool active ) {
414 if ( active )
415 {
416 mSymbolViewStackedWidget->setCurrentIndex( 0 );
417 // note -- we have to save state here and not in destructor, as new symbol list widgets are created before the previous ones are destroyed
418 QgsSettings().setValue( QStringLiteral( "Windows/StyleV2Manager/lastIconView" ), 0, QgsSettings::Gui );
419 }
420 } );
421 connect( mButtonListView, &QToolButton::toggled, this, [this]( bool active ) {
422 if ( active )
423 {
424 QgsSettings().setValue( QStringLiteral( "Windows/StyleV2Manager/lastIconView" ), 1, QgsSettings::Gui );
425 mSymbolViewStackedWidget->setCurrentIndex( 1 );
426 }
427 } );
428 // restore previous view
429 const int currentView = settings.value( QStringLiteral( "Windows/StyleV2Manager/lastIconView" ), 0, QgsSettings::Gui ).toInt();
430 if ( currentView == 0 )
431 mButtonIconView->setChecked( true );
432 else
433 mButtonListView->setChecked( true );
434
435 mSymbolTreeView->header()->restoreState( settings.value( QStringLiteral( "Windows/StyleV2Manager/treeState" ), QByteArray(), QgsSettings::Gui ).toByteArray() );
436 connect( mSymbolTreeView->header(), &QHeaderView::sectionResized, this, [this] {
437 // note -- we have to save state here and not in destructor, as new symbol list widgets are created before the previous ones are destroyed
438 QgsSettings().setValue( QStringLiteral( "Windows/StyleV2Manager/treeState" ), mSymbolTreeView->header()->saveState(), QgsSettings::Gui );
439 } );
440
441 const int thumbnailSize = settings.value( QStringLiteral( "Windows/StyleV2Manager/thumbnailSize" ), 0, QgsSettings::Gui ).toInt();
442 mSliderIconSize->setValue( thumbnailSize );
443 connect( mSliderIconSize, &QSlider::valueChanged, this, &QgsStyleManagerDialog::setThumbnailSize );
444 setThumbnailSize( thumbnailSize );
445}
446
447void QgsStyleManagerDialog::setCurrentStyle( QgsStyle *style )
448{
449 if ( mStyle == style )
450 return;
451
452 if ( mStyle )
453 {
454 disconnect( mStyle, &QgsStyle::symbolSaved, this, &QgsStyleManagerDialog::populateList );
456 disconnect( mStyle, &QgsStyle::aboutToBeDestroyed, this, &QgsStyleManagerDialog::currentStyleAboutToBeDestroyed );
457 }
458
459 QgsCheckableStyleModel *oldModel = mModel;
460
461 mStyle = style;
462 const bool readOnly = isReadOnly();
463 if ( mStyle != QgsStyle::defaultStyle() )
464 {
465 if ( !mActionCopyToDefault )
466 {
467 mActionCopyToDefault = new QAction( tr( "Copy Selection to Default Style…" ), this );
468 mShareMenu->insertAction( mActionCopyItem, mActionCopyToDefault );
469 connect( mActionCopyToDefault, &QAction::triggered, this, &QgsStyleManagerDialog::copyItemsToDefault );
470 }
471 mCopyToDefaultButton->show();
472 mModel = new QgsCheckableStyleModel( mStyle, this, readOnly );
473 }
474 else
475 {
476 mCopyToDefaultButton->hide();
477 if ( mActionCopyToDefault )
478 {
479 mActionCopyToDefault->deleteLater();
480 mActionCopyToDefault = nullptr;
481 }
482 mModel = new QgsCheckableStyleModel( QgsApplication::defaultStyleModel(), this, readOnly );
483 }
484 mModel->addDesiredIconSize( mSymbolTreeView->iconSize() );
485 mModel->addDesiredIconSize( listItems->iconSize() );
486 mModel->addTargetScreenProperties( QgsScreenProperties( screen() ) );
487
488 mModel->setFilterString( searchBox->text() );
489
490 listItems->setModel( mModel );
491 mSymbolTreeView->setModel( mModel );
492
493 mSymbolTreeView->setSelectionModel( listItems->selectionModel() );
494
495 connect( listItems->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsStyleManagerDialog::symbolSelected );
496 connect( listItems->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsStyleManagerDialog::selectedSymbolsChanged );
497
498 if ( oldModel )
499 {
500 oldModel->deleteLater();
501 oldModel = nullptr;
502 }
503
506 connect( mStyle, &QgsStyle::aboutToBeDestroyed, this, &QgsStyleManagerDialog::currentStyleAboutToBeDestroyed );
507
508 if ( mProjectStyleModel )
509 {
510 const QModelIndex styleIndex = mProjectStyleModel->indexFromStyle( mStyle );
511 mBlockStyleDatabaseChanges++;
512 mComboBoxStyleDatabase->setCurrentIndex( styleIndex.row() );
513 mBlockStyleDatabaseChanges--;
514 }
515
516 if ( readOnly )
517 {
518 btnAddTag->setEnabled( false );
519 btnAddSmartgroup->setEnabled( false );
520 btnManageGroups->setEnabled( false );
521
522 btnAddItem->setVisible( false );
523 btnRemoveItem->setVisible( false );
524 btnEditItem->setVisible( false );
525 btnAddSmartgroup->setVisible( false );
526 btnAddTag->setVisible( false );
527 btnManageGroups->setVisible( false );
528
529 delete mImportAction;
530 mImportAction = nullptr;
531
532 mGroupTreeContextMenu->clear();
533 mGroupMenu->clear();
534 mGroupMenu->addAction( mActionCopyItem );
535 }
536 else
537 {
538 btnAddTag->setEnabled( true );
539 btnAddSmartgroup->setEnabled( true );
540 btnManageGroups->setEnabled( true );
541
542 btnAddItem->setVisible( true );
543 btnRemoveItem->setVisible( true );
544 btnEditItem->setVisible( true );
545 btnAddSmartgroup->setVisible( true );
546 btnAddTag->setVisible( true );
547 btnManageGroups->setVisible( true );
548
549 if ( !mImportAction )
550 {
551 mImportAction = new QAction( tr( "Import Item(s)…" ), this );
552 mImportAction->setIcon( QIcon( QgsApplication::iconPath( "mActionFileOpen.svg" ) ) );
553 mShareMenu->insertAction( mShareMenu->actions().at( mShareMenu->actions().indexOf( mExportAction ) + 1 ), mImportAction );
554 connect( mImportAction, &QAction::triggered, this, &QgsStyleManagerDialog::importItems );
555 }
556
557 mGroupTreeContextMenu->clear();
558 mGroupTreeContextMenu->addAction( actnEditSmartGroup );
559 mGroupTreeContextMenu->addAction( actnAddTag );
560 mGroupTreeContextMenu->addAction( actnAddSmartgroup );
561 mGroupTreeContextMenu->addAction( actnRemoveGroup );
562
563 mGroupMenu->clear();
564 mGroupMenu->addAction( actnAddFavorite );
565 mGroupMenu->addAction( actnRemoveFavorite );
566 mGroupMenu->addSeparator()->setParent( this );
567 mGroupMenu->addMenu( mGroupListMenu );
568 mGroupMenu->addAction( actnDetag );
569 mGroupMenu->addSeparator()->setParent( this );
570 mGroupMenu->addAction( actnRemoveItem );
571 mGroupMenu->addAction( actnEditItem );
572 mGroupMenu->addAction( mActionCopyItem );
573 mGroupMenu->addAction( mActionPasteItem );
574 mGroupMenu->addSeparator()->setParent( this );
575 }
576
577 if ( mActionCopyToDefault )
578 {
579 mGroupMenu->addAction( mActionCopyToDefault );
580 }
581 mGroupMenu->addAction( actnExportAsPNG );
582 mGroupMenu->addAction( actnExportAsSVG );
583
585 const QModelIndexList prevIndex = groupTree->model()->match( groupTree->model()->index( 0, 0 ), Qt::UserRole + 1, sPreviousTag, 1, Qt::MatchFixedString | Qt::MatchCaseSensitive | Qt::MatchRecursive );
586 groupTree->setCurrentIndex( !prevIndex.empty() ? prevIndex.at( 0 ) : groupTree->model()->index( 0, 0 ) );
587 populateList();
588
589 tabItemType_currentChanged( tabItemType->currentIndex() );
590
591 // set initial disabled state for actions requiring a selection
592 selectedSymbolsChanged( QItemSelection(), QItemSelection() );
593}
594
595void QgsStyleManagerDialog::currentStyleAboutToBeDestroyed()
596{
597 if ( mStyle != QgsStyle::defaultStyle() )
598 {
599 setCurrentStyle( QgsStyle::defaultStyle() );
600 }
601}
602
604{
605 QgsSettings settings;
606 settings.setValue( QStringLiteral( "Windows/StyleV2Manager/splitter" ), mSplitter->saveState() );
607}
608
612
613void QgsStyleManagerDialog::tabItemType_currentChanged( int )
614{
615 // when in Color Ramp tab, add menu to add item button and hide "Export symbols as PNG/SVG"
616 const bool isSymbol = currentItemType() != 3 && currentItemType() != 4 && currentItemType() != 5 && currentItemType() != 6 && currentItemType() != 7;
617 const bool isColorRamp = currentItemType() == 3;
618 const bool isTextFormat = currentItemType() == 4;
619 const bool isLabelSettings = currentItemType() == 5;
620 const bool isLegendPatchShape = currentItemType() == 6;
621 const bool isSymbol3D = currentItemType() == 7;
622 searchBox->setPlaceholderText( isSymbol ? tr( "Filter symbols…" ) : isColorRamp ? tr( "Filter color ramps…" )
623 : isTextFormat ? tr( "Filter text symbols…" )
624 : isLabelSettings ? tr( "Filter label settings…" )
625 : isLegendPatchShape ? tr( "Filter legend patch shapes…" )
626 : tr( "Filter 3D symbols…" ) );
627
628 const bool readOnly = isReadOnly();
629 if ( !readOnly && isColorRamp ) // color ramp tab
630 {
631 btnAddItem->setMenu( mMenuBtnAddItemColorRamp );
632 }
633 else if ( !readOnly && isLegendPatchShape ) // legend patch shape tab
634 {
635 btnAddItem->setMenu( mMenuBtnAddItemLegendPatchShape );
636 }
637 else if ( !readOnly && isSymbol3D ) // legend patch shape tab
638 {
639 btnAddItem->setMenu( mMenuBtnAddItemSymbol3D );
640 }
641 else if ( !readOnly && isLabelSettings ) // label settings tab
642 {
643 btnAddItem->setMenu( mMenuBtnAddItemLabelSettings );
644 }
645 else if ( !readOnly && !isSymbol && !isColorRamp ) // text format tab
646 {
647 btnAddItem->setMenu( nullptr );
648 }
649 else if ( !readOnly && tabItemType->currentIndex() == 0 ) // all symbols tab
650 {
651 btnAddItem->setMenu( mMenuBtnAddItemAll );
652 }
653 else
654 {
655 btnAddItem->setMenu( nullptr );
656 }
657
658 actnExportAsPNG->setVisible( isSymbol );
659 actnExportAsSVG->setVisible( isSymbol );
660
661 if ( mModel )
662 {
663 mModel->setEntityFilter( isSymbol ? QgsStyle::SymbolEntity : ( isColorRamp ? QgsStyle::ColorrampEntity : isTextFormat ? QgsStyle::TextFormatEntity
664 : isLabelSettings ? QgsStyle::LabelSettingsEntity
665 : isLegendPatchShape ? QgsStyle::LegendPatchShapeEntity
667 mModel->setEntityFilterEnabled( !allTypesSelected() );
668 mModel->setSymbolTypeFilterEnabled( isSymbol && !allTypesSelected() );
669 if ( isSymbol && !allTypesSelected() )
670 mModel->setSymbolType( static_cast<Qgis::SymbolType>( currentItemType() ) );
671 }
672
673 populateList();
674}
675
676void QgsStyleManagerDialog::copyItemsToDefault()
677{
678 const QList<ItemDetails> items = selectedItems();
679 if ( !items.empty() )
680 {
681 bool ok = false;
682 QStringList options;
683 if ( !mBaseName.isEmpty() )
684 options.append( mBaseName );
685
686 QStringList defaultTags = QgsStyle::defaultStyle()->tags();
687 defaultTags.sort( Qt::CaseInsensitive );
688 options.append( defaultTags );
689 const QString tags = QInputDialog::getItem( this, tr( "Import Items" ), tr( "Additional tags to add (comma separated)" ), options, mBaseName.isEmpty() ? -1 : 0, true, &ok );
690 if ( !ok )
691 return;
692
693 const QStringList parts = tags.split( ',', Qt::SkipEmptyParts );
694 QStringList additionalTags;
695 additionalTags.reserve( parts.count() );
696 for ( const QString &tag : parts )
697 additionalTags << tag.trimmed();
698
699 auto cursorOverride = std::make_unique<QgsTemporaryCursorOverride>( Qt::WaitCursor );
700 const int count = copyItems( items, mStyle, QgsStyle::defaultStyle(), this, cursorOverride, true, additionalTags, false, false );
701 cursorOverride.reset();
702 if ( count > 0 )
703 {
704 mMessageBar->pushSuccess( tr( "Import Items" ), count > 1 ? tr( "Successfully imported %n item(s).", nullptr, count ) : tr( "Successfully imported item." ) );
705 }
706 }
707}
708
709void QgsStyleManagerDialog::copyItem()
710{
711 const QList<ItemDetails> items = selectedItems();
712 if ( items.empty() )
713 return;
714
715 ItemDetails details = items.at( 0 );
716 switch ( details.entityType )
717 {
719 {
720 std::unique_ptr<QgsSymbol> symbol( mStyle->symbol( details.name ) );
721 if ( !symbol )
722 return;
723 QApplication::clipboard()->setMimeData( QgsSymbolLayerUtils::symbolToMimeData( symbol.get() ) );
724 break;
725 }
726
728 {
729 const QgsTextFormat format( mStyle->textFormat( details.name ) );
730 QApplication::clipboard()->setMimeData( format.toMimeData() );
731 break;
732 }
733
735 {
736 const QgsPalLayerSettings labelSettings( mStyle->labelSettings( details.name ) );
737 QApplication::clipboard()->setMimeData( labelSettings.toMimeData() );
738 break;
739 }
740
746 return;
747 }
748}
749
750void QgsStyleManagerDialog::pasteItem()
751{
752 const QString defaultTag = groupTree->currentIndex().isValid() ? groupTree->currentIndex().data( GroupModelRoles::TagName ).toString() : QString();
753 std::unique_ptr<QgsSymbol> tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
754 if ( tempSymbol )
755 {
756 QgsStyleSaveDialog saveDlg( this );
757 saveDlg.setWindowTitle( tr( "Paste Symbol" ) );
758 saveDlg.setDefaultTags( defaultTag );
759 if ( !saveDlg.exec() || saveDlg.name().isEmpty() )
760 return;
761
762 if ( mStyle->symbolNames().contains( saveDlg.name() ) )
763 {
764 int res = QMessageBox::warning( this, tr( "Paste Symbol" ), tr( "A symbol with the name '%1' already exists. Overwrite?" ).arg( saveDlg.name() ), QMessageBox::Yes | QMessageBox::No );
765 if ( res != QMessageBox::Yes )
766 {
767 return;
768 }
769 mStyle->removeSymbol( saveDlg.name() );
770 }
771
772 QStringList symbolTags = saveDlg.tags().split( ',' );
773 QgsSymbol *newSymbol = tempSymbol.get();
774 mStyle->addSymbol( saveDlg.name(), tempSymbol.release() );
775 // make sure the symbol is stored
776 mStyle->saveSymbol( saveDlg.name(), newSymbol, saveDlg.isFavorite(), symbolTags );
777 return;
778 }
779
780 bool ok = false;
781
782 const QgsPalLayerSettings labelSettings = QgsPalLayerSettings::fromMimeData( QApplication::clipboard()->mimeData(), &ok );
783 if ( ok )
784 {
785 QgsStyleSaveDialog saveDlg( this, QgsStyle::LabelSettingsEntity );
786 saveDlg.setDefaultTags( defaultTag );
787 saveDlg.setWindowTitle( tr( "Paste Label Settings" ) );
788 if ( !saveDlg.exec() || saveDlg.name().isEmpty() )
789 return;
790
791 if ( mStyle->labelSettingsNames().contains( saveDlg.name() ) )
792 {
793 int res = QMessageBox::warning( this, tr( "Paste Label Settings" ), tr( "A label setting with the name '%1' already exists. Overwrite?" ).arg( saveDlg.name() ), QMessageBox::Yes | QMessageBox::No );
794 if ( res != QMessageBox::Yes )
795 {
796 return;
797 }
798 mStyle->removeLabelSettings( saveDlg.name() );
799 }
800
801 QStringList symbolTags = saveDlg.tags().split( ',' );
802 mStyle->addLabelSettings( saveDlg.name(), labelSettings );
803 // make sure the settings are stored
804 mStyle->saveLabelSettings( saveDlg.name(), labelSettings, saveDlg.isFavorite(), symbolTags );
805 return;
806 }
807
808 ok = false;
809 const QgsTextFormat format = QgsTextFormat::fromMimeData( QApplication::clipboard()->mimeData(), &ok );
810 if ( ok )
811 {
812 QgsStyleSaveDialog saveDlg( this, QgsStyle::TextFormatEntity );
813 saveDlg.setDefaultTags( defaultTag );
814 saveDlg.setWindowTitle( tr( "Paste Text Format" ) );
815 if ( !saveDlg.exec() || saveDlg.name().isEmpty() )
816 return;
817
818 if ( mStyle->textFormatNames().contains( saveDlg.name() ) )
819 {
820 int res = QMessageBox::warning( this, tr( "Paste Text Format" ), tr( "A format with the name '%1' already exists. Overwrite?" ).arg( saveDlg.name() ), QMessageBox::Yes | QMessageBox::No );
821 if ( res != QMessageBox::Yes )
822 {
823 return;
824 }
825 mStyle->removeTextFormat( saveDlg.name() );
826 }
827
828 QStringList symbolTags = saveDlg.tags().split( ',' );
829 mStyle->addTextFormat( saveDlg.name(), format );
830 // make sure the foprmatis stored
831 mStyle->saveTextFormat( saveDlg.name(), format, saveDlg.isFavorite(), symbolTags );
832 return;
833 }
834}
835
836void QgsStyleManagerDialog::setThumbnailSize( int value )
837{
838 // value ranges from 0-10
839 const double iconSize = Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * ( value * 2.5 + 10 );
840 // set a grid size which allows sufficient vertical spacing to fit reasonably sized entity names
841 const double spacing = Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * ( value * 2.2 + 14 );
842 const double verticalSpacing = Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 7
843 + iconSize * 0.8;
844 listItems->setIconSize( QSize( static_cast<int>( iconSize ), static_cast<int>( iconSize * 0.9 ) ) );
845 listItems->setGridSize( QSize( static_cast<int>( spacing ), static_cast<int>( verticalSpacing ) ) );
846 if ( mModel )
847 {
848 mModel->addDesiredIconSize( listItems->iconSize() );
849 }
850
851 QgsSettings().setValue( QStringLiteral( "Windows/StyleV2Manager/thumbnailSize" ), value, QgsSettings::Gui );
852}
853
854int QgsStyleManagerDialog::selectedItemType()
855{
856 QModelIndex index = listItems->selectionModel()->currentIndex();
857 if ( !index.isValid() )
858 return 0;
859
860 const QgsStyle::StyleEntity entity = static_cast<QgsStyle::StyleEntity>( mModel->data( index, static_cast<int>( QgsStyleModel::CustomRole::Type ) ).toInt() );
861 if ( entity == QgsStyle::ColorrampEntity )
862 return 3;
863 else if ( entity == QgsStyle::TextFormatEntity )
864 return 4;
865 else if ( entity == QgsStyle::LabelSettingsEntity )
866 return 5;
867 else if ( entity == QgsStyle::LegendPatchShapeEntity )
868 return 6;
869 else if ( entity == QgsStyle::Symbol3DEntity )
870 return 7;
871
872 return mModel->data( index, static_cast<int>( QgsStyleModel::CustomRole::SymbolType ) ).toInt();
873}
874
875bool QgsStyleManagerDialog::allTypesSelected() const
876{
877 return tabItemType->currentIndex() == 0;
878}
879
880bool QgsStyleManagerDialog::isReadOnly() const
881{
882 return mReadOnly || ( mStyle && mStyle->isReadOnly() );
883}
884
885QList<QgsStyleManagerDialog::ItemDetails> QgsStyleManagerDialog::selectedItems()
886{
887 QList<QgsStyleManagerDialog::ItemDetails> res;
888 QModelIndexList indices = listItems->selectionModel()->selectedRows();
889 for ( const QModelIndex &index : indices )
890 {
891 if ( !index.isValid() )
892 continue;
893
894 ItemDetails details;
895 details.entityType = static_cast<QgsStyle::StyleEntity>( mModel->data( index, static_cast<int>( QgsStyleModel::CustomRole::Type ) ).toInt() );
896 if ( details.entityType == QgsStyle::SymbolEntity )
897 details.symbolType = static_cast<Qgis::SymbolType>( mModel->data( index, static_cast<int>( QgsStyleModel::CustomRole::SymbolType ) ).toInt() );
898 details.name = mModel->data( mModel->index( index.row(), QgsStyleModel::Name, index.parent() ), Qt::DisplayRole ).toString();
899
900 res << details;
901 }
902 return res;
903}
904
905int QgsStyleManagerDialog::copyItems( const QList<QgsStyleManagerDialog::ItemDetails> &items, QgsStyle *src, QgsStyle *dst, QWidget *parentWidget, std::unique_ptr<QgsTemporaryCursorOverride> &cursorOverride, bool isImport, const QStringList &importTags, bool addToFavorites, bool ignoreSourceTags )
906{
907 bool prompt = true;
908 bool overwriteAll = true;
909 int count = 0;
910
911 const QStringList favoriteSymbols = src->symbolsOfFavorite( QgsStyle::SymbolEntity );
912 const QStringList favoriteColorramps = src->symbolsOfFavorite( QgsStyle::ColorrampEntity );
913 const QStringList favoriteTextFormats = src->symbolsOfFavorite( QgsStyle::TextFormatEntity );
914 const QStringList favoriteLabelSettings = src->symbolsOfFavorite( QgsStyle::LabelSettingsEntity );
915 const QStringList favoriteLegendPatchShapes = src->symbolsOfFavorite( QgsStyle::LegendPatchShapeEntity );
916 const QStringList favorite3dSymbols = src->symbolsOfFavorite( QgsStyle::Symbol3DEntity );
917
918 for ( auto &details : items )
919 {
920 QStringList symbolTags;
921 if ( !ignoreSourceTags )
922 {
923 symbolTags = src->tagsOfSymbol( details.entityType, details.name );
924 }
925
926 bool addItemToFavorites = false;
927 if ( isImport )
928 {
929 symbolTags << importTags;
930 addItemToFavorites = addToFavorites;
931 }
932
933 switch ( details.entityType )
934 {
936 {
937 std::unique_ptr<QgsSymbol> symbol( src->symbol( details.name ) );
938 if ( !symbol )
939 continue;
940
941 const bool hasDuplicateName = dst->symbolNames().contains( details.name );
942 bool overwriteThis = false;
943 if ( isImport )
944 addItemToFavorites = favoriteSymbols.contains( details.name );
945
946 if ( hasDuplicateName && prompt )
947 {
948 cursorOverride.reset();
949 int res = QMessageBox::warning( parentWidget, isImport ? tr( "Import Symbol" ) : tr( "Export Symbol" ), tr( "A symbol with the name “%1” already exists.\nOverwrite?" ).arg( details.name ), QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll | QMessageBox::Cancel );
950 cursorOverride = std::make_unique<QgsTemporaryCursorOverride>( Qt::WaitCursor );
951 switch ( res )
952 {
953 case QMessageBox::Cancel:
954 return count;
955
956 case QMessageBox::No:
957 continue;
958
959 case QMessageBox::Yes:
960 overwriteThis = true;
961 break;
962
963 case QMessageBox::YesToAll:
964 prompt = false;
965 overwriteAll = true;
966 break;
967
968 case QMessageBox::NoToAll:
969 prompt = false;
970 overwriteAll = false;
971 break;
972 }
973 }
974
975 if ( !hasDuplicateName || overwriteAll || overwriteThis )
976 {
977 QgsSymbol *newSymbol = symbol.get();
978 dst->addSymbol( details.name, symbol.release() );
979 dst->saveSymbol( details.name, newSymbol, addItemToFavorites, symbolTags );
980 count++;
981 }
982 break;
983 }
984
986 {
987 std::unique_ptr<QgsColorRamp> ramp( src->colorRamp( details.name ) );
988 if ( !ramp )
989 continue;
990
991 const bool hasDuplicateName = dst->colorRampNames().contains( details.name );
992 bool overwriteThis = false;
993 if ( isImport )
994 addItemToFavorites = favoriteColorramps.contains( details.name );
995
996 if ( hasDuplicateName && prompt )
997 {
998 cursorOverride.reset();
999 int res = QMessageBox::warning( parentWidget, isImport ? tr( "Import Color Ramp" ) : tr( "Export Color Ramp" ), tr( "A color ramp with the name “%1” already exists.\nOverwrite?" ).arg( details.name ), QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll | QMessageBox::Cancel );
1000 cursorOverride = std::make_unique<QgsTemporaryCursorOverride>( Qt::WaitCursor );
1001 switch ( res )
1002 {
1003 case QMessageBox::Cancel:
1004 return count;
1005
1006 case QMessageBox::No:
1007 continue;
1008
1009 case QMessageBox::Yes:
1010 overwriteThis = true;
1011 break;
1012
1013 case QMessageBox::YesToAll:
1014 prompt = false;
1015 overwriteAll = true;
1016 break;
1017
1018 case QMessageBox::NoToAll:
1019 prompt = false;
1020 overwriteAll = false;
1021 break;
1022 }
1023 }
1024
1025 if ( !hasDuplicateName || overwriteAll || overwriteThis )
1026 {
1027 QgsColorRamp *newRamp = ramp.get();
1028 dst->addColorRamp( details.name, ramp.release() );
1029 dst->saveColorRamp( details.name, newRamp, addItemToFavorites, symbolTags );
1030 count++;
1031 }
1032 break;
1033 }
1034
1036 {
1037 const QgsTextFormat format( src->textFormat( details.name ) );
1038
1039 const bool hasDuplicateName = dst->textFormatNames().contains( details.name );
1040 bool overwriteThis = false;
1041 if ( isImport )
1042 addItemToFavorites = favoriteTextFormats.contains( details.name );
1043
1044 if ( hasDuplicateName && prompt )
1045 {
1046 cursorOverride.reset();
1047 int res = QMessageBox::warning( parentWidget, isImport ? tr( "Import Text Format" ) : tr( "Export Text Format" ), tr( "A text format with the name “%1” already exists.\nOverwrite?" ).arg( details.name ), QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll | QMessageBox::Cancel );
1048 cursorOverride = std::make_unique<QgsTemporaryCursorOverride>( Qt::WaitCursor );
1049 switch ( res )
1050 {
1051 case QMessageBox::Cancel:
1052 return count;
1053
1054 case QMessageBox::No:
1055 continue;
1056
1057 case QMessageBox::Yes:
1058 overwriteThis = true;
1059 break;
1060
1061 case QMessageBox::YesToAll:
1062 prompt = false;
1063 overwriteAll = true;
1064 break;
1065
1066 case QMessageBox::NoToAll:
1067 prompt = false;
1068 overwriteAll = false;
1069 break;
1070 }
1071 }
1072
1073 if ( !hasDuplicateName || overwriteAll || overwriteThis )
1074 {
1075 dst->addTextFormat( details.name, format );
1076 dst->saveTextFormat( details.name, format, addItemToFavorites, symbolTags );
1077 count++;
1078 }
1079 break;
1080 }
1081
1083 {
1084 const QgsPalLayerSettings settings( src->labelSettings( details.name ) );
1085
1086 const bool hasDuplicateName = dst->labelSettingsNames().contains( details.name );
1087 bool overwriteThis = false;
1088 if ( isImport )
1089 addItemToFavorites = favoriteLabelSettings.contains( details.name );
1090
1091 if ( hasDuplicateName && prompt )
1092 {
1093 cursorOverride.reset();
1094 int res = QMessageBox::warning( parentWidget, isImport ? tr( "Import Label Settings" ) : tr( "Export Label Settings" ), tr( "Label settings with the name “%1” already exist.\nOverwrite?" ).arg( details.name ), QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll | QMessageBox::Cancel );
1095 cursorOverride = std::make_unique<QgsTemporaryCursorOverride>( Qt::WaitCursor );
1096 switch ( res )
1097 {
1098 case QMessageBox::Cancel:
1099 return count;
1100
1101 case QMessageBox::No:
1102 continue;
1103
1104 case QMessageBox::Yes:
1105 overwriteThis = true;
1106 break;
1107
1108 case QMessageBox::YesToAll:
1109 prompt = false;
1110 overwriteAll = true;
1111 break;
1112
1113 case QMessageBox::NoToAll:
1114 prompt = false;
1115 overwriteAll = false;
1116 break;
1117 }
1118 }
1119
1120 if ( !hasDuplicateName || overwriteAll || overwriteThis )
1121 {
1122 dst->addLabelSettings( details.name, settings );
1123 dst->saveLabelSettings( details.name, settings, addItemToFavorites, symbolTags );
1124 count++;
1125 }
1126 break;
1127 }
1128
1130 {
1131 const QgsLegendPatchShape shape( src->legendPatchShape( details.name ) );
1132
1133 const bool hasDuplicateName = dst->legendPatchShapeNames().contains( details.name );
1134 bool overwriteThis = false;
1135 if ( isImport )
1136 addItemToFavorites = favoriteLegendPatchShapes.contains( details.name );
1137
1138 if ( hasDuplicateName && prompt )
1139 {
1140 cursorOverride.reset();
1141 int res = QMessageBox::warning( parentWidget, isImport ? tr( "Import Legend Patch Shape" ) : tr( "Export Legend Patch Shape" ), tr( "Legend patch shape with the name “%1” already exist.\nOverwrite?" ).arg( details.name ), QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll | QMessageBox::Cancel );
1142 cursorOverride = std::make_unique<QgsTemporaryCursorOverride>( Qt::WaitCursor );
1143 switch ( res )
1144 {
1145 case QMessageBox::Cancel:
1146 return count;
1147
1148 case QMessageBox::No:
1149 continue;
1150
1151 case QMessageBox::Yes:
1152 overwriteThis = true;
1153 break;
1154
1155 case QMessageBox::YesToAll:
1156 prompt = false;
1157 overwriteAll = true;
1158 break;
1159
1160 case QMessageBox::NoToAll:
1161 prompt = false;
1162 overwriteAll = false;
1163 break;
1164 }
1165 }
1166
1167 if ( !hasDuplicateName || overwriteAll || overwriteThis )
1168 {
1169 dst->addLegendPatchShape( details.name, shape );
1170 dst->saveLegendPatchShape( details.name, shape, addItemToFavorites, symbolTags );
1171 count++;
1172 }
1173 break;
1174 }
1175
1177 {
1178 std::unique_ptr<QgsAbstract3DSymbol> symbol( src->symbol3D( details.name ) );
1179 if ( !symbol )
1180 continue;
1181
1182 const bool hasDuplicateName = dst->symbol3DNames().contains( details.name );
1183 bool overwriteThis = false;
1184 if ( isImport )
1185 addItemToFavorites = favorite3dSymbols.contains( details.name );
1186
1187 if ( hasDuplicateName && prompt )
1188 {
1189 cursorOverride.reset();
1190 int res = QMessageBox::warning( parentWidget, isImport ? tr( "Import 3D Symbol" ) : tr( "Export 3D Symbol" ), tr( "A 3D symbol with the name “%1” already exists.\nOverwrite?" ).arg( details.name ), QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll | QMessageBox::Cancel );
1191 cursorOverride = std::make_unique<QgsTemporaryCursorOverride>( Qt::WaitCursor );
1192 switch ( res )
1193 {
1194 case QMessageBox::Cancel:
1195 return count;
1196
1197 case QMessageBox::No:
1198 continue;
1199
1200 case QMessageBox::Yes:
1201 overwriteThis = true;
1202 break;
1203
1204 case QMessageBox::YesToAll:
1205 prompt = false;
1206 overwriteAll = true;
1207 break;
1208
1209 case QMessageBox::NoToAll:
1210 prompt = false;
1211 overwriteAll = false;
1212 break;
1213 }
1214 }
1215
1216 if ( !hasDuplicateName || overwriteAll || overwriteThis )
1217 {
1218 QgsAbstract3DSymbol *newSymbol = symbol.get();
1219 dst->addSymbol3D( details.name, symbol.release() );
1220 dst->saveSymbol3D( details.name, newSymbol, addItemToFavorites, symbolTags );
1221 count++;
1222 }
1223 break;
1224 }
1225
1228 break;
1229 }
1230 }
1231 return count;
1232}
1233
1234bool QgsStyleManagerDialog::addTextFormat()
1235{
1236 QgsTextFormat format;
1237 QgsTextFormatDialog formatDlg( format, nullptr, this );
1238 formatDlg.setWindowTitle( tr( "New Text Format" ) );
1239 if ( !formatDlg.exec() )
1240 return false;
1241 format = formatDlg.format();
1242
1243 QgsStyleSaveDialog saveDlg( this, QgsStyle::TextFormatEntity );
1244 const QString defaultTag = groupTree->currentIndex().isValid() ? groupTree->currentIndex().data( GroupModelRoles::TagName ).toString() : QString();
1245 saveDlg.setDefaultTags( defaultTag );
1246 if ( !saveDlg.exec() )
1247 return false;
1248 QString name = saveDlg.name();
1249
1250 // request valid/unique name
1251 bool nameInvalid = true;
1252 while ( nameInvalid )
1253 {
1254 // validate name
1255 if ( name.isEmpty() )
1256 {
1257 QMessageBox::warning( this, tr( "Save Text Format" ), tr( "Cannot save text format without name. Enter a name." ) );
1258 }
1259 else if ( mStyle->textFormatNames().contains( name ) )
1260 {
1261 int res = QMessageBox::warning( this, tr( "Save Text Format" ), tr( "Text format with name '%1' already exists. Overwrite?" ).arg( name ), QMessageBox::Yes | QMessageBox::No );
1262 if ( res == QMessageBox::Yes )
1263 {
1264 mStyle->removeTextFormat( name );
1265 nameInvalid = false;
1266 }
1267 }
1268 else
1269 {
1270 // valid name
1271 nameInvalid = false;
1272 }
1273 if ( nameInvalid )
1274 {
1275 bool ok;
1276 name = QInputDialog::getText( this, tr( "Text Format Name" ), tr( "Please enter a name for new text format:" ), QLineEdit::Normal, name, &ok );
1277 if ( !ok )
1278 {
1279 return false;
1280 }
1281 }
1282 }
1283
1284 QStringList symbolTags = saveDlg.tags().split( ',' );
1285
1286 // add new format to style and re-populate the list
1287 mStyle->addTextFormat( name, format );
1288 mStyle->saveTextFormat( name, format, saveDlg.isFavorite(), symbolTags );
1289
1290 mModified = true;
1291 return true;
1292}
1293
1295{
1296 groupChanged( groupTree->selectionModel()->currentIndex() );
1297}
1298
1299void QgsStyleManagerDialog::populateSymbols( const QStringList &, bool )
1300{
1301}
1302
1303void QgsStyleManagerDialog::populateColorRamps( const QStringList &, bool )
1304{
1305}
1306
1308{
1309 switch ( tabItemType->currentIndex() )
1310 {
1311 case 1:
1312 return static_cast<int>( Qgis::SymbolType::Marker );
1313 case 2:
1314 return static_cast<int>( Qgis::SymbolType::Line );
1315 case 3:
1316 return static_cast<int>( Qgis::SymbolType::Fill );
1317 case 4:
1318 return 3;
1319 case 5:
1320 return 4;
1321 case 6:
1322 return 5;
1323 case 7:
1324 return 6;
1325 case 8:
1326 return 7;
1327 default:
1328 return 0;
1329 }
1330}
1331
1333{
1334 QModelIndex index = listItems->selectionModel()->currentIndex();
1335 if ( !index.isValid() )
1336 return QString();
1337
1338 return mModel->data( mModel->index( index.row(), QgsStyleModel::Name, index.parent() ), Qt::DisplayRole ).toString();
1339}
1340
1342{
1343 bool changed = false;
1344 if ( currentItemType() < 3 )
1345 {
1346 changed = addSymbol();
1347 }
1348 else if ( currentItemType() == 3 )
1349 {
1350 changed = addColorRamp();
1351 }
1352 else if ( currentItemType() == 4 )
1353 {
1354 changed = addTextFormat();
1355 }
1356 else if ( currentItemType() == 5 )
1357 {
1358 // actually never hit, because we present a submenu when adding label settings
1359 // changed = addLabelSettings();
1360 }
1361 else if ( currentItemType() == 6 )
1362 {
1363 // actually never hit, because we present a submenu when adding legend patches
1364 // changed = addLegendPatchShape();
1365 }
1366 else if ( currentItemType() == 7 )
1367 {
1368 // actually never hit, because we present a submenu when adding 3d symbols
1369 // changed = addSymbol3D();
1370 }
1371 else
1372 {
1373 Q_ASSERT( false && "not implemented" );
1374 }
1375
1376 if ( changed )
1377 {
1378 populateList();
1379 }
1380}
1381
1383{
1384 // create new symbol with current type
1385 QgsSymbol *symbol = nullptr;
1386 QString name = tr( "new symbol" );
1387 QString dialogTitle;
1388 switch ( symbolType == -1 ? currentItemType() : symbolType )
1389 {
1390 case static_cast<int>( Qgis::SymbolType::Marker ):
1391 symbol = new QgsMarkerSymbol();
1392 name = tr( "new marker" );
1393 dialogTitle = tr( "New Marker Symbol" );
1394 break;
1395 case static_cast<int>( Qgis::SymbolType::Line ):
1396 symbol = new QgsLineSymbol();
1397 name = tr( "new line" );
1398 dialogTitle = tr( "New Line Symbol" );
1399 break;
1400 case static_cast<int>( Qgis::SymbolType::Fill ):
1401 symbol = new QgsFillSymbol();
1402 name = tr( "new fill symbol" );
1403 dialogTitle = tr( "New Fill Symbol" );
1404 break;
1405 default:
1406 Q_ASSERT( false && "unknown symbol type" );
1407 return false;
1408 }
1409
1410 // get symbol design
1411 // NOTE : Set the parent widget as "this" to notify the Symbol selector
1412 // that, it is being called by Style Manager, so recursive calling
1413 // of style manager and symbol selector can be arrested
1414 // See also: editSymbol()
1415 QgsSymbolSelectorDialog dlg( symbol, mStyle, nullptr, this );
1416 dlg.setWindowTitle( dialogTitle );
1417 if ( dlg.exec() == 0 )
1418 {
1419 delete symbol;
1420 return false;
1421 }
1422
1423 QgsStyleSaveDialog saveDlg( this );
1424 const QString defaultTag = groupTree->currentIndex().isValid() ? groupTree->currentIndex().data( GroupModelRoles::TagName ).toString() : QString();
1425 saveDlg.setDefaultTags( defaultTag );
1426 if ( !saveDlg.exec() )
1427 {
1428 delete symbol;
1429 return false;
1430 }
1431
1432 name = saveDlg.name();
1433
1434 // request valid/unique name
1435 bool nameInvalid = true;
1436 while ( nameInvalid )
1437 {
1438 // validate name
1439 if ( name.isEmpty() )
1440 {
1441 QMessageBox::warning( this, tr( "Save Symbol" ), tr( "Cannot save symbol without name. Enter a name." ) );
1442 }
1443 else if ( mStyle->symbolNames().contains( name ) )
1444 {
1445 int res = QMessageBox::warning( this, tr( "Save Symbol" ), tr( "Symbol with name '%1' already exists. Overwrite?" ).arg( name ), QMessageBox::Yes | QMessageBox::No );
1446 if ( res == QMessageBox::Yes )
1447 {
1448 mStyle->removeSymbol( name );
1449 nameInvalid = false;
1450 }
1451 }
1452 else
1453 {
1454 // valid name
1455 nameInvalid = false;
1456 }
1457 if ( nameInvalid )
1458 {
1459 bool ok;
1460 name = QInputDialog::getText( this, tr( "Symbol Name" ), tr( "Please enter a name for new symbol:" ), QLineEdit::Normal, name, &ok );
1461 if ( !ok )
1462 {
1463 delete symbol;
1464 return false;
1465 }
1466 }
1467 }
1468
1469 QStringList symbolTags = saveDlg.tags().split( ',' );
1470
1471 // add new symbol to style and re-populate the list
1472 mStyle->addSymbol( name, symbol );
1473 mStyle->saveSymbol( name, symbol, saveDlg.isFavorite(), symbolTags );
1474
1475 mModified = true;
1476 return true;
1477}
1478
1479
1480QString QgsStyleManagerDialog::addColorRampStatic( QWidget *parent, QgsStyle *style, const QString &type )
1481{
1482 QString rampType = type;
1483
1484 if ( rampType.isEmpty() )
1485 {
1486 // let the user choose the color ramp type if rampType is not given
1487 bool ok = true;
1488 const QList<QPair<QString, QString>> rampTypes = QgsColorRamp::rampTypes();
1489 QStringList rampTypeNames;
1490 rampTypeNames.reserve( rampTypes.size() );
1491 for ( const QPair<QString, QString> &type : rampTypes )
1492 rampTypeNames << type.second;
1493 const QString selectedRampTypeName = QInputDialog::getItem( parent, tr( "Color Ramp Type" ), tr( "Please select color ramp type:" ), rampTypeNames, 0, false, &ok );
1494 if ( !ok || selectedRampTypeName.isEmpty() )
1495 return QString();
1496
1497 rampType = rampTypes.value( rampTypeNames.indexOf( selectedRampTypeName ) ).first;
1498 }
1499
1500 QString name = tr( "new ramp" );
1501
1502 std::unique_ptr<QgsColorRamp> ramp;
1503 if ( rampType == QgsGradientColorRamp::typeString() )
1504 {
1506 dlg.setWindowTitle( tr( "New Gradient Color Ramp" ) );
1507 if ( !dlg.exec() )
1508 {
1509 return QString();
1510 }
1511 ramp.reset( dlg.ramp().clone() );
1512 name = tr( "new gradient ramp" );
1513 }
1514 else if ( rampType == QgsLimitedRandomColorRamp::typeString() )
1515 {
1517 dlg.setWindowTitle( tr( "New Random Color Ramp" ) );
1518 if ( !dlg.exec() )
1519 {
1520 return QString();
1521 }
1522 ramp.reset( dlg.ramp().clone() );
1523 name = tr( "new random ramp" );
1524 }
1525 else if ( rampType == QgsColorBrewerColorRamp::typeString() )
1526 {
1528 dlg.setWindowTitle( tr( "New ColorBrewer Ramp" ) );
1529 if ( !dlg.exec() )
1530 {
1531 return QString();
1532 }
1533 ramp.reset( dlg.ramp().clone() );
1534 name = dlg.ramp().schemeName() + QString::number( dlg.ramp().colors() );
1535 }
1536 else if ( rampType == QgsPresetSchemeColorRamp::typeString() )
1537 {
1539 dlg.setWindowTitle( tr( "New Preset Color Ramp" ) );
1540 if ( !dlg.exec() )
1541 {
1542 return QString();
1543 }
1544 ramp.reset( dlg.ramp().clone() );
1545 name = tr( "new preset ramp" );
1546 }
1547 else if ( rampType == QgsCptCityColorRamp::typeString() )
1548 {
1549 QgsCptCityColorRampDialog dlg( QgsCptCityColorRamp( QString(), QString() ), parent );
1550 dlg.setWindowTitle( tr( "New cpt-city Color Ramp" ) );
1551 if ( !dlg.exec() )
1552 {
1553 return QString();
1554 }
1555 // name = dlg.selectedName();
1556 name = QFileInfo( dlg.ramp().schemeName() ).baseName() + dlg.ramp().variantName();
1557 if ( dlg.saveAsGradientRamp() )
1558 {
1559 ramp.reset( dlg.ramp().cloneGradientRamp() );
1560 }
1561 else
1562 {
1563 ramp.reset( dlg.ramp().clone() );
1564 }
1565 }
1566 else
1567 {
1568 // Q_ASSERT( 0 && "invalid ramp type" );
1569 // bailing out is rather harsh!
1570 QgsDebugError( QStringLiteral( "invalid ramp type %1" ).arg( rampType ) );
1571 return QString();
1572 }
1573
1575 if ( !saveDlg.exec() )
1576 {
1577 return QString();
1578 }
1579
1580 name = saveDlg.name();
1581
1582 // get valid/unique name
1583 bool nameInvalid = true;
1584 while ( nameInvalid )
1585 {
1586 // validate name
1587 if ( name.isEmpty() )
1588 {
1589 QMessageBox::warning( parent, tr( "Save Color Ramp" ), tr( "Cannot save color ramp without name. Enter a name." ) );
1590 }
1591 else if ( style->colorRampNames().contains( name ) )
1592 {
1593 int res = QMessageBox::warning( parent, tr( "Save Color Ramp" ), tr( "Color ramp with name '%1' already exists. Overwrite?" ).arg( name ), QMessageBox::Yes | QMessageBox::No );
1594 if ( res == QMessageBox::Yes )
1595 {
1596 nameInvalid = false;
1597 }
1598 }
1599 else
1600 {
1601 // valid name
1602 nameInvalid = false;
1603 }
1604 if ( nameInvalid )
1605 {
1606 bool ok;
1607 name = QInputDialog::getText( parent, tr( "Color Ramp Name" ), tr( "Please enter a name for new color ramp:" ), QLineEdit::Normal, name, &ok );
1608 if ( !ok )
1609 {
1610 return QString();
1611 }
1612 }
1613 }
1614
1615 QStringList colorRampTags = saveDlg.tags().split( ',' );
1616 QgsColorRamp *r = ramp.release();
1617
1618 // add new symbol to style and re-populate the list
1619 style->addColorRamp( name, r );
1620 style->saveColorRamp( name, r, saveDlg.isFavorite(), colorRampTags );
1621
1622 return name;
1623}
1624
1626{
1627 mFavoritesGroupVisible = show;
1629}
1630
1632{
1633 mSmartGroupVisible = show;
1635}
1636
1638{
1639 mBaseName = name;
1640}
1641
1643{
1644 raise();
1645 setWindowState( windowState() & ~Qt::WindowMinimized );
1646 activateWindow();
1647}
1648
1649bool QgsStyleManagerDialog::addColorRamp( const QString &type )
1650{
1651 // pass the action text, which is the color ramp type
1652 QString rampName = addColorRampStatic( this, mStyle, type );
1653 if ( !rampName.isEmpty() )
1654 {
1655 mModified = true;
1656 populateList();
1657 return true;
1658 }
1659
1660 return false;
1661}
1662
1664{
1665 if ( selectedItemType() < 3 )
1666 {
1667 editSymbol();
1668 }
1669 else if ( selectedItemType() == 3 )
1670 {
1671 editColorRamp();
1672 }
1673 else if ( selectedItemType() == 4 )
1674 {
1675 editTextFormat();
1676 }
1677 else if ( selectedItemType() == 5 )
1678 {
1679 editLabelSettings();
1680 }
1681 else if ( selectedItemType() == 6 )
1682 {
1683 editLegendPatchShape();
1684 }
1685 else if ( selectedItemType() == 7 )
1686 {
1687 editSymbol3D();
1688 }
1689 else
1690 {
1691 Q_ASSERT( false && "not implemented" );
1692 }
1693}
1694
1696{
1697 QString symbolName = currentItemName();
1698 if ( symbolName.isEmpty() )
1699 return false;
1700
1701 std::unique_ptr<QgsSymbol> symbol( mStyle->symbol( symbolName ) );
1702
1703 // let the user edit the symbol and update list when done
1704 QgsSymbolSelectorDialog dlg( symbol.get(), mStyle, nullptr, this );
1705 dlg.setWindowTitle( symbolName );
1706 if ( isReadOnly() )
1707 dlg.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
1708
1709 if ( !dlg.exec() )
1710 return false;
1711
1712 // by adding symbol to style with the same name the old effectively gets overwritten
1713 mStyle->addSymbol( symbolName, symbol.release(), true );
1714 mModified = true;
1715 return true;
1716}
1717
1719{
1720 QString name = currentItemName();
1721 if ( name.isEmpty() )
1722 return false;
1723
1724 std::unique_ptr<QgsColorRamp> ramp( mStyle->colorRamp( name ) );
1725
1726 if ( ramp->type() == QgsGradientColorRamp::typeString() )
1727 {
1728 QgsGradientColorRamp *gradRamp = static_cast<QgsGradientColorRamp *>( ramp.get() );
1729 QgsGradientColorRampDialog dlg( *gradRamp, this );
1730 dlg.setWindowTitle( name );
1731 if ( isReadOnly() )
1732 dlg.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
1733
1734 if ( !dlg.exec() )
1735 {
1736 return false;
1737 }
1738 ramp.reset( dlg.ramp().clone() );
1739 }
1740 else if ( ramp->type() == QgsLimitedRandomColorRamp::typeString() )
1741 {
1742 QgsLimitedRandomColorRamp *randRamp = static_cast<QgsLimitedRandomColorRamp *>( ramp.get() );
1743 QgsLimitedRandomColorRampDialog dlg( *randRamp, this );
1744 dlg.setWindowTitle( name );
1745 if ( isReadOnly() )
1746 dlg.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
1747
1748 if ( !dlg.exec() )
1749 {
1750 return false;
1751 }
1752 ramp.reset( dlg.ramp().clone() );
1753 }
1754 else if ( ramp->type() == QgsColorBrewerColorRamp::typeString() )
1755 {
1756 QgsColorBrewerColorRamp *brewerRamp = static_cast<QgsColorBrewerColorRamp *>( ramp.get() );
1757 QgsColorBrewerColorRampDialog dlg( *brewerRamp, this );
1758 dlg.setWindowTitle( name );
1759 if ( isReadOnly() )
1760 dlg.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
1761
1762 if ( !dlg.exec() )
1763 {
1764 return false;
1765 }
1766 ramp.reset( dlg.ramp().clone() );
1767 }
1768 else if ( ramp->type() == QgsPresetSchemeColorRamp::typeString() )
1769 {
1770 QgsPresetSchemeColorRamp *presetRamp = static_cast<QgsPresetSchemeColorRamp *>( ramp.get() );
1771 QgsPresetColorRampDialog dlg( *presetRamp, this );
1772 dlg.setWindowTitle( name );
1773 if ( isReadOnly() )
1774 dlg.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
1775
1776 if ( !dlg.exec() )
1777 {
1778 return false;
1779 }
1780 ramp.reset( dlg.ramp().clone() );
1781 }
1782 else if ( ramp->type() == QgsCptCityColorRamp::typeString() )
1783 {
1784 QgsCptCityColorRamp *cptCityRamp = static_cast<QgsCptCityColorRamp *>( ramp.get() );
1785 QgsCptCityColorRampDialog dlg( *cptCityRamp, this );
1786 dlg.setWindowTitle( name );
1787 if ( isReadOnly() )
1788 dlg.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
1789
1790 if ( !dlg.exec() )
1791 {
1792 return false;
1793 }
1794 if ( dlg.saveAsGradientRamp() )
1795 {
1796 ramp.reset( dlg.ramp().cloneGradientRamp() );
1797 }
1798 else
1799 {
1800 ramp.reset( dlg.ramp().clone() );
1801 }
1802 }
1803 else
1804 {
1805 Q_ASSERT( false && "invalid ramp type" );
1806 }
1807
1808 mStyle->addColorRamp( name, ramp.release(), true );
1809 mModified = true;
1810 return true;
1811}
1812
1813bool QgsStyleManagerDialog::editTextFormat()
1814{
1815 const QString formatName = currentItemName();
1816 if ( formatName.isEmpty() )
1817 return false;
1818
1819 QgsTextFormat format = mStyle->textFormat( formatName );
1820
1821 // let the user edit the format and update list when done
1822 QgsTextFormatDialog dlg( format, nullptr, this );
1823 dlg.setWindowTitle( formatName );
1824 if ( isReadOnly() )
1825 dlg.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
1826
1827 if ( !dlg.exec() )
1828 return false;
1829
1830 // by adding format to style with the same name the old effectively gets overwritten
1831 mStyle->addTextFormat( formatName, dlg.format(), true );
1832 mModified = true;
1833 return true;
1834}
1835
1836bool QgsStyleManagerDialog::addLabelSettings( Qgis::GeometryType type )
1837{
1838 QgsPalLayerSettings settings;
1839 QgsLabelSettingsDialog settingsDlg( settings, nullptr, nullptr, this, type );
1840 settingsDlg.setWindowTitle( tr( "New Label Settings" ) );
1841 if ( isReadOnly() )
1842 settingsDlg.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
1843
1844 if ( !settingsDlg.exec() )
1845 return false;
1846
1847 settings = settingsDlg.settings();
1848 settings.layerType = type;
1849
1850 QgsStyleSaveDialog saveDlg( this, QgsStyle::LabelSettingsEntity );
1851 const QString defaultTag = groupTree->currentIndex().isValid() ? groupTree->currentIndex().data( GroupModelRoles::TagName ).toString() : QString();
1852 saveDlg.setDefaultTags( defaultTag );
1853 if ( !saveDlg.exec() )
1854 return false;
1855 QString name = saveDlg.name();
1856
1857 // request valid/unique name
1858 bool nameInvalid = true;
1859 while ( nameInvalid )
1860 {
1861 // validate name
1862 if ( name.isEmpty() )
1863 {
1864 QMessageBox::warning( this, tr( "Save Label Settings" ), tr( "Cannot save label settings without a name. Enter a name." ) );
1865 }
1866 else if ( mStyle->labelSettingsNames().contains( name ) )
1867 {
1868 int res = QMessageBox::warning( this, tr( "Save Label Settings" ), tr( "Label settings with the name '%1' already exist. Overwrite?" ).arg( name ), QMessageBox::Yes | QMessageBox::No );
1869 if ( res == QMessageBox::Yes )
1870 {
1871 mStyle->removeLabelSettings( name );
1872 nameInvalid = false;
1873 }
1874 }
1875 else
1876 {
1877 // valid name
1878 nameInvalid = false;
1879 }
1880 if ( nameInvalid )
1881 {
1882 bool ok;
1883 name = QInputDialog::getText( this, tr( "Label Settings Name" ), tr( "Please enter a name for the new label settings:" ), QLineEdit::Normal, name, &ok );
1884 if ( !ok )
1885 {
1886 return false;
1887 }
1888 }
1889 }
1890
1891 QStringList symbolTags = saveDlg.tags().split( ',' );
1892
1893 // add new format to style and re-populate the list
1894 mStyle->addLabelSettings( name, settings );
1895 mStyle->saveLabelSettings( name, settings, saveDlg.isFavorite(), symbolTags );
1896
1897 mModified = true;
1898 return true;
1899}
1900
1901bool QgsStyleManagerDialog::editLabelSettings()
1902{
1903 const QString formatName = currentItemName();
1904 if ( formatName.isEmpty() )
1905 return false;
1906
1907 QgsPalLayerSettings settings = mStyle->labelSettings( formatName );
1908 Qgis::GeometryType geomType = settings.layerType;
1909
1910 // let the user edit the settings and update list when done
1911 QgsLabelSettingsDialog dlg( settings, nullptr, nullptr, this, geomType );
1912 dlg.setWindowTitle( formatName );
1913 if ( !dlg.exec() )
1914 return false;
1915
1916 settings = dlg.settings();
1917 settings.layerType = geomType;
1918
1919 // by adding format to style with the same name the old effectively gets overwritten
1920 mStyle->addLabelSettings( formatName, settings, true );
1921 mModified = true;
1922 return true;
1923}
1924
1925bool QgsStyleManagerDialog::addLegendPatchShape( Qgis::SymbolType type )
1926{
1927 QgsLegendPatchShape shape = mStyle->defaultPatch( type, QSizeF( 10, 5 ) );
1928 QgsLegendPatchShapeDialog dialog( shape, this );
1929 dialog.setWindowTitle( tr( "New Legend Patch Shape" ) );
1930 if ( isReadOnly() )
1931 dialog.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
1932
1933 if ( !dialog.exec() )
1934 return false;
1935
1936 shape = dialog.shape();
1937
1938 QgsStyleSaveDialog saveDlg( this, QgsStyle::LegendPatchShapeEntity );
1939 const QString defaultTag = groupTree->currentIndex().isValid() ? groupTree->currentIndex().data( GroupModelRoles::TagName ).toString() : QString();
1940 saveDlg.setDefaultTags( defaultTag );
1941 if ( !saveDlg.exec() )
1942 return false;
1943 QString name = saveDlg.name();
1944
1945 // request valid/unique name
1946 bool nameInvalid = true;
1947 while ( nameInvalid )
1948 {
1949 // validate name
1950 if ( name.isEmpty() )
1951 {
1952 QMessageBox::warning( this, tr( "Save Legend Patch Shape" ), tr( "Cannot save legend patch shapes without a name. Enter a name." ) );
1953 }
1954 else if ( mStyle->legendPatchShapeNames().contains( name ) )
1955 {
1956 int res = QMessageBox::warning( this, tr( "Save Legend Patch Shape" ), tr( "A legend patch shape with the name '%1' already exists. Overwrite?" ).arg( name ), QMessageBox::Yes | QMessageBox::No );
1957 if ( res == QMessageBox::Yes )
1958 {
1959 mStyle->removeEntityByName( QgsStyle::LegendPatchShapeEntity, name );
1960 nameInvalid = false;
1961 }
1962 }
1963 else
1964 {
1965 // valid name
1966 nameInvalid = false;
1967 }
1968 if ( nameInvalid )
1969 {
1970 bool ok;
1971 name = QInputDialog::getText( this, tr( "Legend Patch Shape Name" ), tr( "Please enter a name for the new legend patch shape:" ), QLineEdit::Normal, name, &ok );
1972 if ( !ok )
1973 {
1974 return false;
1975 }
1976 }
1977 }
1978
1979 QStringList symbolTags = saveDlg.tags().split( ',' );
1980
1981 // add new shape to style and re-populate the list
1982 mStyle->addLegendPatchShape( name, shape );
1983 mStyle->saveLegendPatchShape( name, shape, saveDlg.isFavorite(), symbolTags );
1984
1985 mModified = true;
1986 return true;
1987}
1988
1989bool QgsStyleManagerDialog::editLegendPatchShape()
1990{
1991 const QString shapeName = currentItemName();
1992 if ( shapeName.isEmpty() )
1993 return false;
1994
1995 QgsLegendPatchShape shape = mStyle->legendPatchShape( shapeName );
1996 if ( shape.isNull() )
1997 return false;
1998
1999 // let the user edit the shape and update list when done
2000 QgsLegendPatchShapeDialog dlg( shape, this );
2001 dlg.setWindowTitle( shapeName );
2002 if ( !dlg.exec() )
2003 return false;
2004
2005 shape = dlg.shape();
2006
2007 // by adding shape to style with the same name the old effectively gets overwritten
2008 mStyle->addLegendPatchShape( shapeName, shape, true );
2009 mModified = true;
2010 return true;
2011}
2012
2013bool QgsStyleManagerDialog::addSymbol3D( const QString &type )
2014{
2015 std::unique_ptr<QgsAbstract3DSymbol> symbol( QgsApplication::symbol3DRegistry()->createSymbol( type ) );
2016 if ( !symbol )
2017 return false;
2018
2019 Qgs3DSymbolDialog dialog( symbol.get(), this );
2020 dialog.setWindowTitle( tr( "New 3D Symbol" ) );
2021 if ( isReadOnly() )
2022 dialog.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
2023
2024 if ( !dialog.exec() )
2025 return false;
2026
2027 symbol.reset( dialog.symbol() );
2028 if ( !symbol )
2029 return false;
2030
2031 QgsStyleSaveDialog saveDlg( this, QgsStyle::Symbol3DEntity );
2032 const QString defaultTag = groupTree->currentIndex().isValid() ? groupTree->currentIndex().data( GroupModelRoles::TagName ).toString() : QString();
2033 saveDlg.setDefaultTags( defaultTag );
2034 if ( !saveDlg.exec() )
2035 return false;
2036 QString name = saveDlg.name();
2037
2038 // request valid/unique name
2039 bool nameInvalid = true;
2040 while ( nameInvalid )
2041 {
2042 // validate name
2043 if ( name.isEmpty() )
2044 {
2045 QMessageBox::warning( this, tr( "Save 3D Symbol" ), tr( "Cannot save 3D symbols without a name. Enter a name." ) );
2046 }
2047 else if ( mStyle->symbol3DNames().contains( name ) )
2048 {
2049 int res = QMessageBox::warning( this, tr( "Save 3D Symbol" ), tr( "A 3D symbol with the name '%1' already exists. Overwrite?" ).arg( name ), QMessageBox::Yes | QMessageBox::No );
2050 if ( res == QMessageBox::Yes )
2051 {
2052 mStyle->removeEntityByName( QgsStyle::Symbol3DEntity, name );
2053 nameInvalid = false;
2054 }
2055 }
2056 else
2057 {
2058 // valid name
2059 nameInvalid = false;
2060 }
2061 if ( nameInvalid )
2062 {
2063 bool ok;
2064 name = QInputDialog::getText( this, tr( "3D Symbol Name" ), tr( "Please enter a name for the new 3D symbol:" ), QLineEdit::Normal, name, &ok );
2065 if ( !ok )
2066 {
2067 return false;
2068 }
2069 }
2070 }
2071
2072 QStringList symbolTags = saveDlg.tags().split( ',' );
2073
2074 // add new shape to style and re-populate the list
2075 QgsAbstract3DSymbol *newSymbol = symbol.get();
2076 mStyle->addSymbol3D( name, symbol.release() );
2077 mStyle->saveSymbol3D( name, newSymbol, saveDlg.isFavorite(), symbolTags );
2078
2079 mModified = true;
2080 return true;
2081}
2082
2083bool QgsStyleManagerDialog::editSymbol3D()
2084{
2085 const QString symbolName = currentItemName();
2086 if ( symbolName.isEmpty() )
2087 return false;
2088
2089 std::unique_ptr<QgsAbstract3DSymbol> symbol( mStyle->symbol3D( symbolName ) );
2090 if ( !symbol )
2091 return false;
2092
2093 // let the user edit the symbol and update list when done
2094 Qgs3DSymbolDialog dlg( symbol.get(), this );
2095 dlg.setWindowTitle( symbolName );
2096 if ( !dlg.exec() )
2097 return false;
2098
2099 symbol.reset( dlg.symbol() );
2100 if ( !symbol )
2101 return false;
2102
2103 // by adding symbol to style with the same name the old effectively gets overwritten
2104 mStyle->addSymbol3D( symbolName, symbol.release(), true );
2105 mModified = true;
2106 return true;
2107}
2108
2109void QgsStyleManagerDialog::addStyleDatabase( bool createNew )
2110{
2112 if ( initialFolder.isEmpty() )
2113 initialFolder = QDir::homePath();
2114
2115 QString databasePath = createNew
2116 ? QFileDialog::getSaveFileName(
2117 this,
2118 tr( "Create Style Database" ),
2119 initialFolder,
2120 tr( "Style databases" ) + " (*.db)"
2121 )
2122 : QFileDialog::getOpenFileName(
2123 this,
2124 tr( "Add Style Database" ),
2125 initialFolder,
2126 tr( "Style databases" ) + " (*.db *.xml)"
2127 );
2128 // return dialog focus on Mac
2129 activateWindow();
2130 raise();
2131 if ( !databasePath.isEmpty() )
2132 {
2133 QgsStyleManagerDialog::settingLastStyleDatabaseFolder->setValue( QFileInfo( databasePath ).path() );
2134
2135 if ( createNew )
2136 {
2137 databasePath = QgsFileUtils::ensureFileNameHasExtension( databasePath, { QStringLiteral( "db" ) } );
2138 if ( QFile::exists( databasePath ) )
2139 {
2140 QFile::remove( databasePath );
2141 }
2142 QgsStyle s;
2143 if ( !s.createDatabase( databasePath ) )
2144 {
2145 QMessageBox::warning( this, tr( "Create Style Database" ), tr( "The style database could not be created" ) );
2146 return;
2147 }
2148 }
2149
2151 setCurrentStyle( QgsProject::instance()->styleSettings()->styleAtPath( databasePath ) );
2152 }
2153}
2154
2156{
2157 const QList<ItemDetails> items = selectedItems();
2158
2159 if ( allTypesSelected() )
2160 {
2161 if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Remove Items" ), QString( tr( "Do you really want to remove %n item(s)?", nullptr, items.count() ) ), QMessageBox::Yes, QMessageBox::No ) )
2162 return;
2163 }
2164 else
2165 {
2166 if ( currentItemType() < 3 )
2167 {
2168 if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Remove Symbol" ), QString( tr( "Do you really want to remove %n symbol(s)?", nullptr, items.count() ) ), QMessageBox::Yes, QMessageBox::No ) )
2169 return;
2170 }
2171 else if ( currentItemType() == 3 )
2172 {
2173 if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Remove Color Ramp" ), QString( tr( "Do you really want to remove %n ramp(s)?", nullptr, items.count() ) ), QMessageBox::Yes, QMessageBox::No ) )
2174 return;
2175 }
2176 else if ( currentItemType() == 4 )
2177 {
2178 if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Remove Text Formats" ), QString( tr( "Do you really want to remove %n text format(s)?", nullptr, items.count() ) ), QMessageBox::Yes, QMessageBox::No ) )
2179 return;
2180 }
2181 else if ( currentItemType() == 5 )
2182 {
2183 if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Remove Label Settings" ), QString( tr( "Do you really want to remove %n label setting(s)?", nullptr, items.count() ) ), QMessageBox::Yes, QMessageBox::No ) )
2184 return;
2185 }
2186 else if ( currentItemType() == 6 )
2187 {
2188 if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Remove Legend Patch Shapes" ), QString( tr( "Do you really want to remove %n legend patch shape(s)?", nullptr, items.count() ) ), QMessageBox::Yes, QMessageBox::No ) )
2189 return;
2190 }
2191 else if ( currentItemType() == 7 )
2192 {
2193 if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Remove 3D Symbols" ), QString( tr( "Do you really want to remove %n 3D symbol(s)?", nullptr, items.count() ) ), QMessageBox::Yes, QMessageBox::No ) )
2194 return;
2195 }
2196 }
2197
2198 QgsTemporaryCursorOverride override( Qt::WaitCursor );
2199
2200 for ( const ItemDetails &details : items )
2201 {
2202 if ( details.name.isEmpty() )
2203 continue;
2204
2205 mStyle->removeEntityByName( details.entityType, details.name );
2206 }
2207
2208 mModified = true;
2209}
2210
2212{
2213 return false;
2214}
2215
2217{
2218 return false;
2219}
2220
2222{
2223}
2224
2226{
2227 QString dir = QFileDialog::getExistingDirectory( this, tr( "Export Selected Symbols as PNG" ), QDir::home().absolutePath(), QFileDialog::DontResolveSymlinks );
2228 exportSelectedItemsImages( dir, QStringLiteral( "png" ), QSize( 32, 32 ) );
2229}
2230
2232{
2233 QString dir = QFileDialog::getExistingDirectory( this, tr( "Export Selected Symbols as SVG" ), QDir::home().absolutePath(), QFileDialog::DontResolveSymlinks );
2234 exportSelectedItemsImages( dir, QStringLiteral( "svg" ), QSize( 32, 32 ) );
2235}
2236
2237
2238void QgsStyleManagerDialog::exportSelectedItemsImages( const QString &dir, const QString &format, QSize size )
2239{
2240 if ( dir.isEmpty() )
2241 return;
2242
2243 const QList<ItemDetails> items = selectedItems();
2244 for ( const ItemDetails &details : items )
2245 {
2246 if ( details.entityType != QgsStyle::SymbolEntity )
2247 continue;
2248
2249 QString path = dir + '/' + details.name + '.' + format;
2250 std::unique_ptr<QgsSymbol> sym( mStyle->symbol( details.name ) );
2251 if ( sym )
2252 sym->exportImage( path, format, size );
2253 }
2254}
2255
2257{
2259 dlg.exec();
2260}
2261
2263{
2265 dlg.exec();
2266 populateList();
2268}
2269
2270void QgsStyleManagerDialog::setBold( QStandardItem *item )
2271{
2272 QFont font = item->font();
2273 font.setBold( true );
2274 item->setFont( font );
2275}
2276
2278{
2279 if ( mBlockGroupUpdates )
2280 return;
2281
2282 QStandardItemModel *model = qobject_cast<QStandardItemModel *>( groupTree->model() );
2283 model->clear();
2284
2285 const bool readOnly = isReadOnly();
2286
2287 if ( mFavoritesGroupVisible )
2288 {
2289 QStandardItem *favoriteSymbols = new QStandardItem( tr( "Favorites" ) );
2290 favoriteSymbols->setData( "favorite" );
2291 favoriteSymbols->setEditable( false );
2292 setBold( favoriteSymbols );
2293 model->appendRow( favoriteSymbols );
2294 }
2295
2296 QStandardItem *allSymbols = new QStandardItem( tr( "All" ) );
2297 allSymbols->setData( "all" );
2298 allSymbols->setEditable( false );
2299 setBold( allSymbols );
2300 model->appendRow( allSymbols );
2301
2302 QStandardItem *taggroup = new QStandardItem( QString() ); //require empty name to get first order groups
2303 taggroup->setData( "tags" );
2304 taggroup->setEditable( false );
2305 QStringList tags = mStyle->tags();
2306 tags.sort();
2307 for ( const QString &tag : std::as_const( tags ) )
2308 {
2309 QStandardItem *item = new QStandardItem( tag );
2310 item->setData( mStyle->tagId( tag ) );
2311 item->setData( tag, GroupModelRoles::TagName );
2312 item->setEditable( !readOnly );
2313 taggroup->appendRow( item );
2314 }
2315 taggroup->setText( tr( "Tags" ) ); //set title later
2316 setBold( taggroup );
2317 model->appendRow( taggroup );
2318
2319 if ( mSmartGroupVisible )
2320 {
2321 QStandardItem *smart = new QStandardItem( tr( "Smart Groups" ) );
2322 smart->setData( "smartgroups" );
2323 smart->setEditable( false );
2324 setBold( smart );
2325 QgsSymbolGroupMap sgMap = mStyle->smartgroupsListMap();
2326 QgsSymbolGroupMap::const_iterator i = sgMap.constBegin();
2327 while ( i != sgMap.constEnd() )
2328 {
2329 QStandardItem *item = new QStandardItem( i.value() );
2330 item->setData( i.key() );
2331 item->setEditable( !readOnly );
2332 smart->appendRow( item );
2333 ++i;
2334 }
2335 model->appendRow( smart );
2336 }
2337
2338 // expand things in the group tree
2339 int rows = model->rowCount( model->indexFromItem( model->invisibleRootItem() ) );
2340 for ( int i = 0; i < rows; i++ )
2341 {
2342 groupTree->setExpanded( model->indexFromItem( model->item( i ) ), true );
2343 }
2344}
2345
2346void QgsStyleManagerDialog::groupChanged( const QModelIndex &index )
2347{
2348 const QString category = index.data( Qt::UserRole + 1 ).toString();
2349 sPreviousTag = category;
2350
2351 const bool readOnly = isReadOnly();
2352
2353 if ( mGroupingMode && mModel )
2354 {
2355 mModel->setTagId( -1 );
2356 mModel->setSmartGroupId( -1 );
2357 mModel->setFavoritesOnly( false );
2358 mModel->setCheckTag( index.data( Qt::DisplayRole ).toString() );
2359 }
2360 else if ( category == QLatin1String( "all" ) || category == QLatin1String( "tags" ) || category == QLatin1String( "smartgroups" ) )
2361 {
2362 enableGroupInputs( false );
2363 if ( category == QLatin1String( "tags" ) )
2364 {
2365 actnAddTag->setEnabled( !readOnly );
2366 actnAddSmartgroup->setEnabled( false );
2367 }
2368 else if ( category == QLatin1String( "smartgroups" ) )
2369 {
2370 actnAddTag->setEnabled( false );
2371 actnAddSmartgroup->setEnabled( !readOnly );
2372 }
2373
2374 if ( mModel )
2375 {
2376 mModel->setTagId( -1 );
2377 mModel->setSmartGroupId( -1 );
2378 mModel->setFavoritesOnly( false );
2379 }
2380 }
2381 else if ( category == QLatin1String( "favorite" ) )
2382 {
2383 enableGroupInputs( false );
2384 if ( mModel )
2385 {
2386 mModel->setTagId( -1 );
2387 mModel->setSmartGroupId( -1 );
2388 mModel->setFavoritesOnly( true );
2389 }
2390 }
2391 else if ( index.parent().data( Qt::UserRole + 1 ) == "smartgroups" )
2392 {
2393 actnRemoveGroup->setEnabled( !readOnly );
2394 btnManageGroups->setEnabled( !readOnly );
2395 const int groupId = index.data( Qt::UserRole + 1 ).toInt();
2396 if ( mModel )
2397 {
2398 mModel->setTagId( -1 );
2399 mModel->setSmartGroupId( groupId );
2400 mModel->setFavoritesOnly( false );
2401 }
2402 }
2403 else // tags
2404 {
2405 enableGroupInputs( true );
2406 int tagId = index.data( Qt::UserRole + 1 ).toInt();
2407 if ( mModel )
2408 {
2409 mModel->setTagId( tagId );
2410 mModel->setSmartGroupId( -1 );
2411 mModel->setFavoritesOnly( false );
2412 }
2413 }
2414
2415 actnEditSmartGroup->setVisible( false );
2416 actnAddTag->setVisible( false );
2417 actnAddSmartgroup->setVisible( false );
2418 actnRemoveGroup->setVisible( false );
2419 actnTagSymbols->setVisible( false );
2420 actnFinishTagging->setVisible( false );
2421
2422 if ( index.parent().isValid() )
2423 {
2424 if ( index.parent().data( Qt::UserRole + 1 ).toString() == QLatin1String( "smartgroups" ) )
2425 {
2426 actnEditSmartGroup->setVisible( !mGroupingMode && !readOnly );
2427 }
2428 else if ( index.parent().data( Qt::UserRole + 1 ).toString() == QLatin1String( "tags" ) )
2429 {
2430 actnAddTag->setVisible( !mGroupingMode && !readOnly );
2431 actnTagSymbols->setVisible( !mGroupingMode && !readOnly );
2432 actnFinishTagging->setVisible( mGroupingMode && !readOnly );
2433 }
2434 actnRemoveGroup->setVisible( !readOnly );
2435 }
2436 else if ( index.data( Qt::UserRole + 1 ) == "smartgroups" )
2437 {
2438 actnAddSmartgroup->setVisible( !mGroupingMode && !readOnly );
2439 }
2440 else if ( index.data( Qt::UserRole + 1 ) == "tags" )
2441 {
2442 actnAddTag->setVisible( !mGroupingMode && !readOnly );
2443 }
2444}
2445
2447{
2448 if ( isReadOnly() )
2449 return 0;
2450
2451 QStandardItemModel *model = qobject_cast<QStandardItemModel *>( groupTree->model() );
2452 QModelIndex index;
2453 for ( int i = 0; i < groupTree->model()->rowCount(); i++ )
2454 {
2455 index = groupTree->model()->index( i, 0 );
2456 QString data = index.data( Qt::UserRole + 1 ).toString();
2457 if ( data == QLatin1String( "tags" ) )
2458 {
2459 break;
2460 }
2461 }
2462
2463 QString itemName;
2464 int id;
2465 bool ok;
2466 itemName = QInputDialog::getText( this, tr( "Add Tag" ), tr( "Please enter name for the new tag:" ), QLineEdit::Normal, tr( "New tag" ), &ok ).trimmed();
2467 if ( !ok || itemName.isEmpty() )
2468 return 0;
2469
2470 int check = mStyle->tagId( itemName );
2471 if ( check > 0 )
2472 {
2473 mMessageBar->pushCritical( tr( "Add Tag" ), tr( "The tag “%1” already exists." ).arg( itemName ) );
2474 return 0;
2475 }
2476
2477 // block the auto-repopulation of groups when the style emits groupsModified
2478 // instead, we manually update the model items for better state retention
2479 mBlockGroupUpdates++;
2480 id = mStyle->addTag( itemName );
2481 mBlockGroupUpdates--;
2482
2483 if ( !id )
2484 {
2485 mMessageBar->pushCritical( tr( "Add Tag" ), tr( "New tag could not be created — There was a problem with the symbol database." ) );
2486 return 0;
2487 }
2488
2489 QStandardItem *parentItem = model->itemFromIndex( index );
2490 QStandardItem *childItem = new QStandardItem( itemName );
2491 childItem->setData( id );
2492 childItem->setData( itemName, GroupModelRoles::TagName );
2493 parentItem->appendRow( childItem );
2494
2495 return id;
2496}
2497
2499{
2500 if ( isReadOnly() )
2501 return 0;
2502
2503 QStandardItemModel *model = qobject_cast<QStandardItemModel *>( groupTree->model() );
2504 QModelIndex index;
2505 for ( int i = 0; i < groupTree->model()->rowCount(); i++ )
2506 {
2507 index = groupTree->model()->index( i, 0 );
2508 QString data = index.data( Qt::UserRole + 1 ).toString();
2509 if ( data == QLatin1String( "smartgroups" ) )
2510 {
2511 break;
2512 }
2513 }
2514
2515 QString itemName;
2516 int id;
2517 QgsSmartGroupEditorDialog dlg( mStyle, this );
2518 if ( dlg.exec() == QDialog::Rejected )
2519 return 0;
2520
2521 // block the auto-repopulation of groups when the style emits groupsModified
2522 // instead, we manually update the model items for better state retention
2523 mBlockGroupUpdates++;
2524 id = mStyle->addSmartgroup( dlg.smartgroupName(), dlg.conditionOperator(), dlg.conditionMap() );
2525 mBlockGroupUpdates--;
2526
2527 if ( !id )
2528 return 0;
2529 itemName = dlg.smartgroupName();
2530
2531 QStandardItem *parentItem = model->itemFromIndex( index );
2532 QStandardItem *childItem = new QStandardItem( itemName );
2533 childItem->setData( id );
2534 parentItem->appendRow( childItem );
2535
2536 return id;
2537}
2538
2540{
2541 if ( isReadOnly() )
2542 return;
2543
2544 QStandardItemModel *model = qobject_cast<QStandardItemModel *>( groupTree->model() );
2545 QModelIndex index = groupTree->currentIndex();
2546
2547 // do not allow removal of system-defined groupings
2548 QString data = index.data( Qt::UserRole + 1 ).toString();
2549 if ( data == QLatin1String( "all" ) || data == QLatin1String( "favorite" ) || data == QLatin1String( "tags" ) || index.data() == "smartgroups" )
2550 {
2551 // should never appear -- blocked by GUI
2552 int err = QMessageBox::critical( this, tr( "Remove Group" ), tr( "Invalid selection. Cannot delete system defined categories.\n"
2553 "Kindly select a group or smart group you might want to delete." ) );
2554 if ( err )
2555 return;
2556 }
2557
2558 QStandardItem *parentItem = model->itemFromIndex( index.parent() );
2559
2560 // block the auto-repopulation of groups when the style emits groupsModified
2561 // instead, we manually update the model items for better state retention
2562 mBlockGroupUpdates++;
2563
2564 if ( parentItem->data( Qt::UserRole + 1 ).toString() == QLatin1String( "smartgroups" ) )
2565 {
2566 mStyle->remove( QgsStyle::SmartgroupEntity, index.data( Qt::UserRole + 1 ).toInt() );
2567 }
2568 else
2569 {
2570 mStyle->remove( QgsStyle::TagEntity, index.data( Qt::UserRole + 1 ).toInt() );
2571 }
2572
2573 mBlockGroupUpdates--;
2574 parentItem->removeRow( index.row() );
2575}
2576
2577void QgsStyleManagerDialog::groupRenamed( QStandardItem *item )
2578{
2579 if ( isReadOnly() )
2580 return;
2581
2582 QgsDebugMsgLevel( QStringLiteral( "Symbol group edited: data=%1 text=%2" ).arg( item->data( Qt::UserRole + 1 ).toString(), item->text() ), 2 );
2583 int id = item->data( Qt::UserRole + 1 ).toInt();
2584 QString name = item->text();
2585 mBlockGroupUpdates++;
2586 if ( item->parent()->data( Qt::UserRole + 1 ) == "smartgroups" )
2587 {
2588 mStyle->rename( QgsStyle::SmartgroupEntity, id, name );
2589 }
2590 else
2591 {
2592 mStyle->rename( QgsStyle::TagEntity, id, name );
2593 }
2594 mBlockGroupUpdates--;
2595}
2596
2598{
2599 if ( isReadOnly() )
2600 return;
2601
2602 QStandardItemModel *treeModel = qobject_cast<QStandardItemModel *>( groupTree->model() );
2603
2604 if ( mGroupingMode )
2605 {
2606 mGroupingMode = false;
2607 mModel->setCheckable( false );
2608 actnTagSymbols->setVisible( true );
2609 actnFinishTagging->setVisible( false );
2610 // disconnect slot which handles regrouping
2611
2612 // disable all items except groups in groupTree
2614 groupChanged( groupTree->currentIndex() );
2615
2616 // Finally: Reconnect all Symbol editing functionalities
2617 connect( treeModel, &QStandardItemModel::itemChanged, this, &QgsStyleManagerDialog::groupRenamed );
2618
2619 // Reset the selection mode
2620 listItems->setSelectionMode( QAbstractItemView::ExtendedSelection );
2621 mSymbolTreeView->setSelectionMode( QAbstractItemView::ExtendedSelection );
2622 }
2623 else
2624 {
2625 bool validGroup = false;
2626 // determine whether it is a valid group
2627 QModelIndex present = groupTree->currentIndex();
2628 while ( present.parent().isValid() )
2629 {
2630 if ( present.parent().data() == "Tags" )
2631 {
2632 validGroup = true;
2633 break;
2634 }
2635 present = present.parent();
2636 }
2637 if ( !validGroup )
2638 return;
2639
2640 mGroupingMode = true;
2641 // Change visibility of actions
2642 actnTagSymbols->setVisible( false );
2643 actnFinishTagging->setVisible( true );
2644 // Remove all Symbol editing functionalities
2645 disconnect( treeModel, &QStandardItemModel::itemChanged, this, &QgsStyleManagerDialog::groupRenamed );
2646
2647 // disable all items except groups in groupTree
2649 groupChanged( groupTree->currentIndex() );
2650 btnManageGroups->setEnabled( true );
2651
2652 mModel->setCheckable( true );
2653
2654 // No selection should be possible
2655 listItems->setSelectionMode( QAbstractItemView::NoSelection );
2656 mSymbolTreeView->setSelectionMode( QAbstractItemView::NoSelection );
2657 }
2658}
2659
2661{
2662}
2663
2665{
2666}
2667
2668void QgsStyleManagerDialog::filterSymbols( const QString &qword )
2669{
2670 mModel->setFilterString( qword );
2671}
2672
2673void QgsStyleManagerDialog::symbolSelected( const QModelIndex &index )
2674{
2675 actnEditItem->setEnabled( index.isValid() && !mGroupingMode && !isReadOnly() );
2676}
2677
2678void QgsStyleManagerDialog::selectedSymbolsChanged( const QItemSelection &selected, const QItemSelection &deselected )
2679{
2680 Q_UNUSED( selected )
2681 Q_UNUSED( deselected )
2682 const bool nothingSelected = listItems->selectionModel()->selectedIndexes().empty();
2683 const bool readOnly = isReadOnly();
2684 actnRemoveItem->setDisabled( nothingSelected || readOnly );
2685 actnAddFavorite->setDisabled( nothingSelected || readOnly );
2686 actnRemoveFavorite->setDisabled( nothingSelected || readOnly );
2687 mGroupListMenu->setDisabled( nothingSelected || readOnly );
2688 actnDetag->setDisabled( nothingSelected || readOnly );
2689 actnExportAsPNG->setDisabled( nothingSelected );
2690 actnExportAsSVG->setDisabled( nothingSelected );
2691 if ( mActionCopyToDefault )
2692 mActionCopyToDefault->setDisabled( nothingSelected );
2693 mCopyToDefaultButton->setDisabled( nothingSelected );
2694 actnEditItem->setDisabled( nothingSelected || readOnly );
2695}
2696
2698{
2699 const bool readOnly = isReadOnly();
2700 groupTree->setEnabled( enable );
2701 btnAddTag->setEnabled( enable && !readOnly );
2702 btnAddSmartgroup->setEnabled( enable && !readOnly );
2703 actnAddTag->setEnabled( enable && !readOnly );
2704 actnAddSmartgroup->setEnabled( enable && !readOnly );
2705 actnRemoveGroup->setEnabled( enable && !readOnly );
2706 btnManageGroups->setEnabled( !readOnly && ( enable || mGroupingMode ) ); // always enabled in grouping mode, as it is the only way to leave grouping mode
2707 searchBox->setEnabled( enable );
2708}
2709
2711{
2712 const bool readOnly = isReadOnly();
2713 actnRemoveGroup->setEnabled( enable && !readOnly );
2714 btnManageGroups->setEnabled( !readOnly && ( enable || mGroupingMode ) ); // always enabled in grouping mode, as it is the only way to leave grouping mode
2715}
2716
2718{
2719 QStandardItemModel *treeModel = qobject_cast<QStandardItemModel *>( groupTree->model() );
2720 for ( int i = 0; i < treeModel->rowCount(); i++ )
2721 {
2722 treeModel->item( i )->setEnabled( enable );
2723
2724 if ( treeModel->item( i )->data() == "smartgroups" )
2725 {
2726 for ( int j = 0; j < treeModel->item( i )->rowCount(); j++ )
2727 {
2728 treeModel->item( i )->child( j )->setEnabled( enable );
2729 }
2730 }
2731 }
2732
2733 // The buttons
2734 // NOTE: if you ever change the layout name in the .ui file edit here too
2735 for ( int i = 0; i < symbolBtnsLayout->count(); i++ )
2736 {
2737 QWidget *w = symbolBtnsLayout->itemAt( i )->widget();
2738 if ( w )
2739 w->setEnabled( enable );
2740 }
2741
2742 // The actions
2743 actnRemoveItem->setEnabled( enable );
2744 actnEditItem->setEnabled( enable );
2745 mActionCopyItem->setEnabled( enable );
2746 mActionPasteItem->setEnabled( enable );
2747}
2748
2750{
2751 QPoint globalPos = groupTree->viewport()->mapToGlobal( point );
2752
2753 QModelIndex index = groupTree->indexAt( point );
2754 if ( index.isValid() && !mGroupingMode )
2755 mGroupTreeContextMenu->popup( globalPos );
2756}
2757
2759{
2760 QPoint globalPos = mSymbolViewStackedWidget->currentIndex() == 0
2761 ? listItems->viewport()->mapToGlobal( point )
2762 : mSymbolTreeView->viewport()->mapToGlobal( point );
2763
2764 // Clear all actions and create new actions for every group
2765 mGroupListMenu->clear();
2766
2767 const QModelIndexList indices = listItems->selectionModel()->selectedRows();
2768
2769 if ( !isReadOnly() )
2770 {
2771 const QStringList currentTags = indices.count() == 1 ? indices.at( 0 ).data( static_cast<int>( QgsStyleModel::CustomRole::Tag ) ).toStringList() : QStringList();
2772 QAction *a = nullptr;
2773 QStringList tags = mStyle->tags();
2774 tags.sort();
2775 for ( const QString &tag : std::as_const( tags ) )
2776 {
2777 a = new QAction( tag, mGroupListMenu );
2778 a->setData( tag );
2779 if ( indices.count() == 1 )
2780 {
2781 a->setCheckable( true );
2782 a->setChecked( currentTags.contains( tag ) );
2783 }
2784 connect( a, &QAction::triggered, this, [this]( bool ) { tagSelectedSymbols(); } );
2785 mGroupListMenu->addAction( a );
2786 }
2787
2788 if ( tags.count() > 0 )
2789 {
2790 mGroupListMenu->addSeparator();
2791 }
2792 a = new QAction( tr( "Create New Tag…" ), mGroupListMenu );
2793 connect( a, &QAction::triggered, this, [this]( bool ) { tagSelectedSymbols( true ); } );
2794 mGroupListMenu->addAction( a );
2795 }
2796
2797 const QList<ItemDetails> items = selectedItems();
2798 mActionCopyItem->setEnabled( !items.isEmpty() && ( items.at( 0 ).entityType != QgsStyle::ColorrampEntity ) );
2799
2800 bool enablePaste = false;
2801 std::unique_ptr<QgsSymbol> tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
2802 if ( tempSymbol )
2803 enablePaste = true;
2804 else
2805 {
2806 const QMimeData *mimeData = QApplication::clipboard()->mimeData();
2807 if ( mimeData->hasFormat( QLatin1String( "application/qgis.labelsettings" ) ) )
2808 {
2809 ( void ) QgsPalLayerSettings::fromMimeData( mimeData, &enablePaste );
2810 }
2811 else
2812 {
2813 ( void ) QgsTextFormat::fromMimeData( mimeData, &enablePaste );
2814 }
2815 }
2816 mActionPasteItem->setEnabled( enablePaste );
2817
2818 mGroupMenu->popup( globalPos );
2819}
2820
2822{
2823 if ( isReadOnly() )
2824 return;
2825
2826 const QList<ItemDetails> items = selectedItems();
2827 for ( const ItemDetails &details : items )
2828 {
2829 mStyle->addFavorite( details.entityType, details.name );
2830 }
2831}
2832
2834{
2835 if ( isReadOnly() )
2836 return;
2837
2838 const QList<ItemDetails> items = selectedItems();
2839 for ( const ItemDetails &details : items )
2840 {
2841 mStyle->removeFavorite( details.entityType, details.name );
2842 }
2843}
2844
2846{
2847 QAction *selectedItem = qobject_cast<QAction *>( sender() );
2848 if ( selectedItem )
2849 {
2850 const QList<ItemDetails> items = selectedItems();
2851 QString tag;
2852 if ( newTag )
2853 {
2854 int id = addTag();
2855 if ( id == 0 )
2856 {
2857 return;
2858 }
2859
2860 tag = mStyle->tag( id );
2861 }
2862 else
2863 {
2864 tag = selectedItem->data().toString();
2865 }
2866
2867 for ( const ItemDetails &details : items )
2868 {
2869 mStyle->tagSymbol( details.entityType, details.name, QStringList( tag ) );
2870 }
2871 }
2872}
2873
2875{
2876 if ( isReadOnly() )
2877 return;
2878
2879 QAction *selectedItem = qobject_cast<QAction *>( sender() );
2880
2881 if ( selectedItem )
2882 {
2883 const QList<ItemDetails> items = selectedItems();
2884 for ( const ItemDetails &details : items )
2885 {
2886 mStyle->detagSymbol( details.entityType, details.name );
2887 }
2888 }
2889}
2890
2892{
2893 if ( isReadOnly() )
2894 return;
2895
2896 QStandardItemModel *treeModel = qobject_cast<QStandardItemModel *>( groupTree->model() );
2897
2898 // determine whether it is a valid group
2899 QModelIndex present = groupTree->currentIndex();
2900 if ( present.parent().data( Qt::UserRole + 1 ) != "smartgroups" )
2901 {
2902 // should never appear - blocked by GUI logic
2903 QMessageBox::critical( this, tr( "Edit Smart Group" ), tr( "You have not selected a Smart Group. Kindly select a Smart Group to edit." ) );
2904 return;
2905 }
2906 QStandardItem *item = treeModel->itemFromIndex( present );
2907
2908 QgsSmartGroupEditorDialog dlg( mStyle, this );
2909 QgsSmartConditionMap map = mStyle->smartgroup( present.data( Qt::UserRole + 1 ).toInt() );
2910 dlg.setSmartgroupName( item->text() );
2911 dlg.setOperator( mStyle->smartgroupOperator( item->data().toInt() ) );
2912 dlg.setConditionMap( map );
2913
2914 if ( dlg.exec() == QDialog::Rejected )
2915 return;
2916
2917 mBlockGroupUpdates++;
2918 mStyle->remove( QgsStyle::SmartgroupEntity, item->data().toInt() );
2919 int id = mStyle->addSmartgroup( dlg.smartgroupName(), dlg.conditionOperator(), dlg.conditionMap() );
2920 mBlockGroupUpdates--;
2921 if ( !id )
2922 {
2923 mMessageBar->pushCritical( tr( "Edit Smart Group" ), tr( "There was an error while editing the smart group." ) );
2924 return;
2925 }
2926 item->setText( dlg.smartgroupName() );
2927 item->setData( id );
2928
2929 groupChanged( present );
2930}
2931
2933{
2934 reject();
2935}
2936
2938{
2939 QgsHelp::openHelp( QStringLiteral( "style_library/style_manager.html" ) );
2940}
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:358
@ Point
Points.
Definition qgis.h:359
@ Line
Lines.
Definition qgis.h:360
@ Polygon
Polygons.
Definition qgis.h:361
SymbolType
Symbol types.
Definition qgis.h:610
@ Marker
Marker symbol.
Definition qgis.h:611
@ Line
Line symbol.
Definition qgis.h:612
@ Fill
Fill symbol.
Definition qgis.h:613
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition qgis.h:6222
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsStyleModel * defaultStyleModel()
Returns a shared QgsStyleModel containing the default style library (see QgsStyle::defaultStyle()).
static Qgs3DSymbolRegistry * symbol3DRegistry()
Returns registry of available 3D symbols.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
A dialog which allows users to modify the properties of a QgsColorBrewerColorRamp.
QDialogButtonBox * buttonBox() const
Returns a reference to the dialog's button box.
Color ramp utilising "Color Brewer" preset color schemes.
QgsColorBrewerColorRamp * clone() const override
Creates a clone of the color ramp.
static QString typeString()
Returns the string identifier for QgsColorBrewerColorRamp.
QString schemeName() const
Returns the name of the color brewer color scheme.
int colors() const
Returns the number of colors in the ramp.
Abstract base class for color ramps.
static QList< QPair< QString, QString > > rampTypes()
Returns a list of available ramp types, where the first value in each item is the QgsColorRamp::type(...
A dialog which allows users to modify the properties of a QgsCptCityColorRamp.
bool saveAsGradientRamp() const
Returns true if the ramp should be converted to a QgsGradientColorRamp.
QDialogButtonBox * buttonBox() const
Returns a reference to the dialog's button box.
A color ramp from the CPT City collection.
QgsCptCityColorRamp * clone() const override
Creates a clone of the color ramp.
static QString typeString()
Returns the string identifier for QgsCptCityColorRamp.
QgsGradientColorRamp * cloneGradientRamp() const
QString schemeName() const
QString variantName() const
static QString ensureFileNameHasExtension(const QString &fileName, const QStringList &extensions)
Ensures that a fileName ends with an extension from the provided list of extensions.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
A dialog which allows users to modify the properties of a QgsGradientColorRamp.
QDialogButtonBox * buttonBox() const
Returns a reference to the dialog's button box.
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
static QString typeString()
Returns the string identifier for QgsGradientColorRamp.
QgsGradientColorRamp * clone() const override
Creates a clone of the color ramp.
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition qgsgui.cpp:221
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:38
static QIcon iconLine()
Returns an icon representing line geometries.
static QIcon iconPolygon()
Returns an icon representing polygon geometries.
static QIcon iconPoint()
Returns an icon representing point geometries.
bool isNull() const
Returns true if the patch shape is a null QgsLegendPatchShape, which indicates that the default legen...
A dialog which allows users to modify the properties of a QgsLimitedRandomColorRamp.
QDialogButtonBox * buttonBox() const
Returns a reference to the dialog's button box.
Constrained random color ramp, which returns random colors based on preset parameters.
static QString typeString()
Returns the string identifier for QgsLimitedRandomColorRamp.
QgsLimitedRandomColorRamp * clone() const override
Creates a clone of the color ramp.
A line symbol type, for rendering LineString and MultiLineString geometries.
A marker symbol type, for rendering Point and MultiPoint geometries.
static QgsPalLayerSettings fromMimeData(const QMimeData *data, bool *ok=nullptr)
Attempts to parse the provided mime data as a QgsPalLayerSettings.
Qgis::GeometryType layerType
Geometry type of layers associated with these settings.
A dialog which allows users to modify the properties of a QgsPresetSchemeColorRamp.
QDialogButtonBox * buttonBox() const
Returns a reference to the dialog's button box.
QgsPresetSchemeColorRamp ramp
A scheme based color ramp consisting of a list of predefined colors.
static QString typeString()
Returns the string identifier for QgsPresetSchemeColorRamp.
QgsPresetSchemeColorRamp * clone() const override
Creates a clone of the color ramp.
List model representing the style databases associated with a QgsProject.
void addStyleDatabasePath(const QString &path)
Adds a style database path to the project.
static QgsProject * instance()
Returns the QgsProject singleton instance.
const QgsProjectStyleSettings * styleSettings() const
Returns the project's style settings, which contains settings and properties relating to how a QgsPro...
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
bool setValue(const T &value, const QString &dynamicKeyPart=QString()) const
Set settings value.
A string settings entry.
Stores settings for use within QGIS.
Definition qgssettings.h:65
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
A dialog for configuring a style smart group.
void setConditionMap(const QgsSmartConditionMap &)
sets up the GUI for the given conditionmap
QgsSmartConditionMap conditionMap()
returns the condition map
QString smartgroupName()
returns the value from mNameLineEdit
void setSmartgroupName(const QString &)
sets the smart group Name
void setOperator(const QString &)
sets the operator AND/OR
QString conditionOperator()
returns the AND/OR condition
@ Export
Export existing symbols mode.
Q_DECL_DEPRECATED bool removeSymbol()
void onClose()
Closes the dialog.
void groupRenamed(QStandardItem *item)
Triggered when a group item is renamed.
static const QgsSettingsEntryString * settingLastStyleDatabaseFolder
Last used folder for generic style database actions.
void addFavoriteSelectedSymbols()
Add selected symbols to favorites.
void selectedSymbolsChanged(const QItemSelection &selected, const QItemSelection &deselected)
Perform tasks when the selected symbols change.
void removeGroup()
Removes the selected tag or smartgroup.
void exportItems()
Triggers the dialog to export items.
void setFavoritesGroupVisible(bool show)
Sets whether the favorites group should be shown.
void grouptreeContextMenu(QPoint)
Context menu for the groupTree.
void setBold(QStandardItem *)
sets the text of the item with bold font
void filterSymbols(const QString &filter)
Sets the filter string to filter symbols by.
void addItem()
Triggers the dialog for adding a new item, based on the currently selected item type tab.
void tagSymbolsAction()
Toggles the interactive item tagging mode.
void editSmartgroupAction()
Triggers the dialog for editing the selected smart group.
void showHelp()
Opens the associated help.
void detagSelectedSymbols()
Remove all tags from selected symbols.
void enableSymbolInputs(bool)
Enables or disbables the symbol specific inputs.
bool addSymbol(int symbolType=-1)
add a new symbol to style
Q_DECL_DEPRECATED void populateTypes()
Populate combo box with known style items (symbols, color ramps).
void populateList()
Refreshes the list of items.
void removeItem()
Removes the current selected item.
void groupChanged(const QModelIndex &)
Triggered when the current group (or tag) is changed.
QgsStyleManagerDialog(QgsStyle *style, QWidget *parent=nullptr, Qt::WindowFlags flags=Qt::WindowFlags(), bool readOnly=false)
Constructor for QgsStyleManagerDialog, with the specified parent widget and window flags.
void enableGroupInputs(bool)
Enables or disables the groupTree specific inputs.
int addTag()
Triggers the dialog to add a new tag.
void exportItemsSVG()
Triggers the dialog to export selected items as SVG files.
Q_DECL_DEPRECATED void populateSymbols(const QStringList &symbolNames, bool checkable=false)
Populates the list view with symbols of the current type with the given names.
void populateGroups()
populate the groups
Q_DECL_DEPRECATED bool removeColorRamp()
void importItems()
Triggers the dialog to import items.
void setBaseStyleName(const QString &name)
Sets the base name for the style, which is used by the dialog to reflect the original style/XML file ...
Q_DECL_DEPRECATED void regrouped(QStandardItem *)
Q_DECL_DEPRECATED void itemChanged(QStandardItem *item)
void exportItemsPNG()
Triggers the dialog to export selected items as PNG files.
void activate()
Raises, unminimizes and activates this window.
bool addColorRamp(const QString &type=QString())
Triggers adding a new color ramp.
void exportSelectedItemsImages(const QString &dir, const QString &format, QSize size)
Triggers the dialog to export selected items as images of the specified format and size.
void enableItemsForGroupingMode(bool)
Enables or disables the groupTree items for grouping mode.
Q_DECL_DEPRECATED void setSymbolsChecked(const QStringList &)
void onFinished()
Called when the dialog is going to be closed.
void listitemsContextMenu(QPoint)
Context menu for the listItems ( symbols list ).
void setSmartGroupsVisible(bool show)
Sets whether smart groups should be shown.
static QString addColorRampStatic(QWidget *parent, QgsStyle *style, const QString &RampType=QString())
Opens the add color ramp dialog, returning the new color ramp's name if the ramp has been added.
void symbolSelected(const QModelIndex &)
Perform symbol specific tasks when selected.
void editItem()
Triggers the dialog for editing the current item.
void removeFavoriteSelectedSymbols()
Remove selected symbols from favorites.
int addSmartgroup()
Triggers the dialog to add a new smart group.
void tagSelectedSymbols(bool newTag=false)
Tag selected symbols using menu item selection.
Q_DECL_DEPRECATED void populateColorRamps(const QStringList &colorRamps, bool checkable=false)
Populates the list view with color ramps of the current type with the given names.
A QAbstractItemModel subclass for showing symbol and color ramp entities contained within a QgsStyle ...
@ SymbolType
Symbol type (for symbol or legend patch shape entities).
@ Type
Style entity type, see QgsStyle::StyleEntity.
@ Tag
String list of tags.
@ Name
Name column.
A QSortFilterProxyModel subclass for showing filtered symbol and color ramps entries from a QgsStyle ...
A dialog for setting properties of a newly saved style.
bool isFavorite() const
Returns true if the favorite is checked for the symbol.
QString name() const
Returns the entered name for the new symbol.
void setDefaultTags(const QString &tags)
Sets the default tags for the newly created item.
QString tags() const
Returns any tags entered for the new symbol (as a comma separated value list).
A database of saved style entities, including symbols, color ramps, text formats and others.
Definition qgsstyle.h:88
QgsTextFormat textFormat(const QString &name) const
Returns the text format with the specified name.
QStringList tags() const
Returns a list of all tags in the style database.
QStringList symbol3DNames() const
Returns a list of names of 3d symbols in the style.
bool saveLabelSettings(const QString &name, const QgsPalLayerSettings &settings, bool favorite, const QStringList &tags)
Adds label settings to the database.
void aboutToBeDestroyed()
Emitted just before the style object is destroyed.
bool createDatabase(const QString &filename)
Creates an on-disk database.
Definition qgsstyle.cpp:556
QStringList textFormatNames() const
Returns a list of names of text formats in the style.
bool addColorRamp(const QString &name, QgsColorRamp *colorRamp, bool update=false)
Adds a color ramp to the style.
Definition qgsstyle.cpp:348
QgsSymbol * symbol(const QString &name)
Returns a NEW copy of symbol.
Definition qgsstyle.cpp:320
StyleEntity
Enum for Entities involved in a style.
Definition qgsstyle.h:204
@ LabelSettingsEntity
Label settings.
Definition qgsstyle.h:210
@ TextFormatEntity
Text formats.
Definition qgsstyle.h:209
@ SmartgroupEntity
Smart groups.
Definition qgsstyle.h:208
@ Symbol3DEntity
3D symbol entity
Definition qgsstyle.h:212
@ SymbolEntity
Symbols.
Definition qgsstyle.h:205
@ TagEntity
Tags.
Definition qgsstyle.h:206
@ ColorrampEntity
Color ramps.
Definition qgsstyle.h:207
@ LegendPatchShapeEntity
Legend patch shape.
Definition qgsstyle.h:211
QStringList tagsOfSymbol(StyleEntity type, const QString &symbol)
Returns the tags associated with the symbol.
void groupsModified()
Emitted every time a tag or smartgroup has been added, removed, or renamed.
QStringList colorRampNames() const
Returns a list of names of color ramps.
Definition qgsstyle.cpp:518
bool addSymbol3D(const QString &name, QgsAbstract3DSymbol *symbol, bool update=false)
Adds a 3d symbol with the specified name to the style.
Definition qgsstyle.cpp:439
QStringList legendPatchShapeNames() const
Returns a list of names of legend patch shapes in the style.
void symbolSaved(const QString &name, const QgsSymbol *symbol)
Emitted every time a new symbol has been added to the database.
bool addLegendPatchShape(const QString &name, const QgsLegendPatchShape &shape, bool update=false)
Adds a legend patch shape with the specified name to the style.
Definition qgsstyle.cpp:417
QgsColorRamp * colorRamp(const QString &name) const
Returns a new copy of the specified color ramp.
Definition qgsstyle.cpp:502
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:147
bool saveSymbol(const QString &name, const QgsSymbol *symbol, bool favorite, const QStringList &tags)
Adds the symbol to the database with tags.
Definition qgsstyle.cpp:250
QStringList labelSettingsNames() const
Returns a list of names of label settings in the style.
bool saveLegendPatchShape(const QString &name, const QgsLegendPatchShape &shape, bool favorite, const QStringList &tags)
Adds a legend patch shape to the database.
bool addTextFormat(const QString &name, const QgsTextFormat &format, bool update=false)
Adds a text format with the specified name to the style.
Definition qgsstyle.cpp:373
bool saveColorRamp(const QString &name, const QgsColorRamp *ramp, bool favorite, const QStringList &tags)
Adds the colorramp to the database.
Definition qgsstyle.cpp:461
QStringList symbolsOfFavorite(StyleEntity type) const
Returns the symbol names which are flagged as favorite.
bool saveSymbol3D(const QString &name, QgsAbstract3DSymbol *symbol, bool favorite, const QStringList &tags)
Adds a 3d symbol to the database.
QgsPalLayerSettings labelSettings(const QString &name) const
Returns the label settings with the specified name.
QgsAbstract3DSymbol * symbol3D(const QString &name) const
Returns a new copy of the 3D symbol with the specified name.
bool addSymbol(const QString &name, QgsSymbol *symbol, bool update=false)
Adds a symbol to style and takes symbol's ownership.
Definition qgsstyle.cpp:225
QgsLegendPatchShape legendPatchShape(const QString &name) const
Returns the legend patch shape with the specified name.
QStringList symbolNames() const
Returns a list of names of symbols.
Definition qgsstyle.cpp:342
bool saveTextFormat(const QString &name, const QgsTextFormat &format, bool favorite, const QStringList &tags)
Adds a text format to the database.
Definition qgsstyle.cpp:981
bool addLabelSettings(const QString &name, const QgsPalLayerSettings &settings, bool update=false)
Adds label settings with the specified name to the style.
Definition qgsstyle.cpp:395
static std::unique_ptr< QgsSymbol > symbolFromMimeData(const QMimeData *data)
Attempts to parse mime data as a symbol.
static QMimeData * symbolToMimeData(const QgsSymbol *symbol)
Creates new mime data from a symbol.
A dialog that can be used to select and build a symbol.
QDialogButtonBox * buttonBox() const
Returns a reference to the dialog's button box.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:231
Temporarily sets a cursor override for the QApplication for the lifetime of the object.
A simple dialog for customizing text formatting settings.
Container for all settings relating to text rendering.
static QgsTextFormat fromMimeData(const QMimeData *data, bool *ok=nullptr)
Attempts to parse the provided mime data as a QgsTextFormat.
QMultiMap< QString, QString > QgsSmartConditionMap
A multimap to hold the smart group conditions as constraint and parameter pairs.
Definition qgsstyle.h:79
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61
#define QgsDebugError(str)
Definition qgslogger.h:57
QMap< int, QString > QgsSymbolGroupMap
Definition qgsstyle.h:42