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