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