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