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