QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
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->setFilterString( searchBox->text() );
506
507 listItems->setModel( mModel );
508 mSymbolTreeView->setModel( mModel );
509
510 mSymbolTreeView->setSelectionModel( listItems->selectionModel() );
511
512 connect( listItems->selectionModel(), &QItemSelectionModel::currentChanged,
514 connect( listItems->selectionModel(), &QItemSelectionModel::selectionChanged,
516
517 if ( oldModel )
518 {
519 oldModel->deleteLater();
520 oldModel = nullptr;
521 }
522
525 connect( mStyle, &QgsStyle::aboutToBeDestroyed, this, &QgsStyleManagerDialog::currentStyleAboutToBeDestroyed );
526
527 if ( mProjectStyleModel )
528 {
529 const QModelIndex styleIndex = mProjectStyleModel->indexFromStyle( mStyle );
530 mBlockStyleDatabaseChanges++;
531 mComboBoxStyleDatabase->setCurrentIndex( styleIndex.row() );
532 mBlockStyleDatabaseChanges--;
533 }
534
535 if ( readOnly )
536 {
537 btnAddTag->setEnabled( false );
538 btnAddSmartgroup->setEnabled( false );
539 btnManageGroups->setEnabled( false );
540
541 btnAddItem->setVisible( false );
542 btnRemoveItem->setVisible( false );
543 btnEditItem->setVisible( false );
544 btnAddSmartgroup->setVisible( false );
545 btnAddTag->setVisible( false );
546 btnManageGroups->setVisible( false );
547
548 delete mImportAction;
549 mImportAction = nullptr;
550
551 mGroupTreeContextMenu->clear();
552 mGroupMenu->clear();
553 mGroupMenu->addAction( mActionCopyItem );
554 }
555 else
556 {
557 btnAddTag->setEnabled( true );
558 btnAddSmartgroup->setEnabled( true );
559 btnManageGroups->setEnabled( true );
560
561 btnAddItem->setVisible( true );
562 btnRemoveItem->setVisible( true );
563 btnEditItem->setVisible( true );
564 btnAddSmartgroup->setVisible( true );
565 btnAddTag->setVisible( true );
566 btnManageGroups->setVisible( true );
567
568 if ( !mImportAction )
569 {
570 mImportAction = new QAction( tr( "Import Item(s)…" ), this );
571 mImportAction->setIcon( QIcon( QgsApplication::iconPath( "mActionFileOpen.svg" ) ) );
572 mShareMenu->insertAction( mShareMenu->actions().at( mShareMenu->actions().indexOf( mExportAction ) + 1 ), mImportAction );
573 connect( mImportAction, &QAction::triggered, this, &QgsStyleManagerDialog::importItems );
574 }
575
576 mGroupTreeContextMenu->clear();
577 mGroupTreeContextMenu->addAction( actnEditSmartGroup );
578 mGroupTreeContextMenu->addAction( actnAddTag );
579 mGroupTreeContextMenu->addAction( actnAddSmartgroup );
580 mGroupTreeContextMenu->addAction( actnRemoveGroup );
581
582 mGroupMenu->clear();
583 mGroupMenu->addAction( actnAddFavorite );
584 mGroupMenu->addAction( actnRemoveFavorite );
585 mGroupMenu->addSeparator()->setParent( this );
586 mGroupMenu->addMenu( mGroupListMenu );
587 mGroupMenu->addAction( actnDetag );
588 mGroupMenu->addSeparator()->setParent( this );
589 mGroupMenu->addAction( actnRemoveItem );
590 mGroupMenu->addAction( actnEditItem );
591 mGroupMenu->addAction( mActionCopyItem );
592 mGroupMenu->addAction( mActionPasteItem );
593 mGroupMenu->addSeparator()->setParent( this );
594 }
595
596 if ( mActionCopyToDefault )
597 {
598 mGroupMenu->addAction( mActionCopyToDefault );
599 }
600 mGroupMenu->addAction( actnExportAsPNG );
601 mGroupMenu->addAction( actnExportAsSVG );
602
604 const QModelIndexList prevIndex = groupTree->model()->match( groupTree->model()->index( 0, 0 ), Qt::UserRole + 1, sPreviousTag, 1, Qt::MatchFixedString | Qt::MatchCaseSensitive | Qt::MatchRecursive );
605 groupTree->setCurrentIndex( !prevIndex.empty() ? prevIndex.at( 0 ) : groupTree->model()->index( 0, 0 ) );
606 populateList();
607
608 tabItemType_currentChanged( tabItemType->currentIndex() );
609
610 // set initial disabled state for actions requiring a selection
611 selectedSymbolsChanged( QItemSelection(), QItemSelection() );
612}
613
614void QgsStyleManagerDialog::currentStyleAboutToBeDestroyed()
615{
616 if ( mStyle != QgsStyle::defaultStyle() )
617 {
618 setCurrentStyle( QgsStyle::defaultStyle() );
619 }
620}
621
623{
624 QgsSettings settings;
625 settings.setValue( QStringLiteral( "Windows/StyleV2Manager/splitter" ), mSplitter->saveState() );
626}
627
629{
630}
631
632void QgsStyleManagerDialog::tabItemType_currentChanged( int )
633{
634 // when in Color Ramp tab, add menu to add item button and hide "Export symbols as PNG/SVG"
635 const bool isSymbol = currentItemType() != 3 && currentItemType() != 4 && currentItemType() != 5 && currentItemType() != 6 && currentItemType() != 7;
636 const bool isColorRamp = currentItemType() == 3;
637 const bool isTextFormat = currentItemType() == 4;
638 const bool isLabelSettings = currentItemType() == 5;
639 const bool isLegendPatchShape = currentItemType() == 6;
640 const bool isSymbol3D = currentItemType() == 7;
641 searchBox->setPlaceholderText( isSymbol ? tr( "Filter symbols…" ) :
642 isColorRamp ? tr( "Filter color ramps…" ) :
643 isTextFormat ? tr( "Filter text symbols…" ) :
644 isLabelSettings ? tr( "Filter label settings…" ) :
645 isLegendPatchShape ? tr( "Filter legend patch shapes…" ) : tr( "Filter 3D symbols…" ) );
646
647 const bool readOnly = isReadOnly();
648 if ( !readOnly && isColorRamp ) // color ramp tab
649 {
650 btnAddItem->setMenu( mMenuBtnAddItemColorRamp );
651 }
652 else if ( !readOnly && isLegendPatchShape ) // legend patch shape tab
653 {
654 btnAddItem->setMenu( mMenuBtnAddItemLegendPatchShape );
655 }
656 else if ( !readOnly && isSymbol3D ) // legend patch shape tab
657 {
658 btnAddItem->setMenu( mMenuBtnAddItemSymbol3D );
659 }
660 else if ( !readOnly && isLabelSettings ) // label settings tab
661 {
662 btnAddItem->setMenu( mMenuBtnAddItemLabelSettings );
663 }
664 else if ( !readOnly && !isSymbol && !isColorRamp ) // text format tab
665 {
666 btnAddItem->setMenu( nullptr );
667 }
668 else if ( !readOnly && tabItemType->currentIndex() == 0 ) // all symbols tab
669 {
670 btnAddItem->setMenu( mMenuBtnAddItemAll );
671 }
672 else
673 {
674 btnAddItem->setMenu( nullptr );
675 }
676
677 actnExportAsPNG->setVisible( isSymbol );
678 actnExportAsSVG->setVisible( isSymbol );
679
680 if ( mModel )
681 {
682 mModel->setEntityFilter( isSymbol ? QgsStyle::SymbolEntity : ( isColorRamp ? QgsStyle::ColorrampEntity : isTextFormat ? QgsStyle::TextFormatEntity : isLabelSettings ? QgsStyle::LabelSettingsEntity : isLegendPatchShape ? QgsStyle::LegendPatchShapeEntity : QgsStyle::Symbol3DEntity ) );
683 mModel->setEntityFilterEnabled( !allTypesSelected() );
684 mModel->setSymbolTypeFilterEnabled( isSymbol && !allTypesSelected() );
685 if ( isSymbol && !allTypesSelected() )
686 mModel->setSymbolType( static_cast< Qgis::SymbolType >( currentItemType() ) );
687 }
688
689 populateList();
690}
691
692void QgsStyleManagerDialog::copyItemsToDefault()
693{
694 const QList< ItemDetails > items = selectedItems();
695 if ( !items.empty() )
696 {
697 bool ok = false;
698 QStringList options;
699 if ( !mBaseName.isEmpty() )
700 options.append( mBaseName );
701
702 QStringList defaultTags = QgsStyle::defaultStyle()->tags();
703 defaultTags.sort( Qt::CaseInsensitive );
704 options.append( defaultTags );
705 const QString tags = QInputDialog::getItem( this, tr( "Import Items" ),
706 tr( "Additional tags to add (comma separated)" ), options, mBaseName.isEmpty() ? -1 : 0, true, &ok );
707 if ( !ok )
708 return;
709
710#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
711 const QStringList parts = tags.split( ',', QString::SkipEmptyParts );
712#else
713 const QStringList parts = tags.split( ',', Qt::SkipEmptyParts );
714#endif
715 QStringList additionalTags;
716 additionalTags.reserve( parts.count() );
717 for ( const QString &tag : parts )
718 additionalTags << tag.trimmed();
719
720 auto cursorOverride = std::make_unique< QgsTemporaryCursorOverride >( Qt::WaitCursor );
721 const int count = copyItems( items, mStyle, QgsStyle::defaultStyle(), this, cursorOverride, true, additionalTags, false, false );
722 cursorOverride.reset();
723 if ( count > 0 )
724 {
725 mMessageBar->pushSuccess( tr( "Import Items" ), count > 1 ? tr( "Successfully imported %n item(s).", nullptr, count ) : tr( "Successfully imported item." ) );
726 }
727 }
728}
729
730void QgsStyleManagerDialog::copyItem()
731{
732 const QList< ItemDetails > items = selectedItems();
733 if ( items.empty() )
734 return;
735
736 ItemDetails details = items.at( 0 );
737 switch ( details.entityType )
738 {
740 {
741 std::unique_ptr< QgsSymbol > symbol( mStyle->symbol( details.name ) );
742 if ( !symbol )
743 return;
744 QApplication::clipboard()->setMimeData( QgsSymbolLayerUtils::symbolToMimeData( symbol.get() ) );
745 break;
746 }
747
749 {
750 const QgsTextFormat format( mStyle->textFormat( details.name ) );
751 QApplication::clipboard()->setMimeData( format.toMimeData() );
752 break;
753 }
754
756 {
757 const QgsTextFormat format( mStyle->labelSettings( details.name ).format() );
758 QApplication::clipboard()->setMimeData( format.toMimeData() );
759 break;
760 }
761
767 return;
768
769 }
770}
771
772void QgsStyleManagerDialog::pasteItem()
773{
774 const QString defaultTag = groupTree->currentIndex().isValid() ? groupTree->currentIndex().data( GroupModelRoles::TagName ).toString() : QString();
775 std::unique_ptr< QgsSymbol > tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
776 if ( tempSymbol )
777 {
778 QgsStyleSaveDialog saveDlg( this );
779 saveDlg.setWindowTitle( tr( "Paste Symbol" ) );
780 saveDlg.setDefaultTags( defaultTag );
781 if ( !saveDlg.exec() || saveDlg.name().isEmpty() )
782 return;
783
784 if ( mStyle->symbolNames().contains( saveDlg.name() ) )
785 {
786 int res = QMessageBox::warning( this, tr( "Paste Symbol" ),
787 tr( "A symbol with the name '%1' already exists. Overwrite?" )
788 .arg( saveDlg.name() ),
789 QMessageBox::Yes | QMessageBox::No );
790 if ( res != QMessageBox::Yes )
791 {
792 return;
793 }
794 mStyle->removeSymbol( saveDlg.name() );
795 }
796
797 QStringList symbolTags = saveDlg.tags().split( ',' );
798 QgsSymbol *newSymbol = tempSymbol.get();
799 mStyle->addSymbol( saveDlg.name(), tempSymbol.release() );
800 // make sure the symbol is stored
801 mStyle->saveSymbol( saveDlg.name(), newSymbol, saveDlg.isFavorite(), symbolTags );
802 return;
803 }
804
805 bool ok = false;
806 const QgsTextFormat format = QgsTextFormat::fromMimeData( QApplication::clipboard()->mimeData(), &ok );
807 if ( ok )
808 {
810 saveDlg.setDefaultTags( defaultTag );
811 saveDlg.setWindowTitle( tr( "Paste Text Format" ) );
812 if ( !saveDlg.exec() || saveDlg.name().isEmpty() )
813 return;
814
815 if ( mStyle->textFormatNames().contains( saveDlg.name() ) )
816 {
817 int res = QMessageBox::warning( this, tr( "Paste Text Format" ),
818 tr( "A format with the name '%1' already exists. Overwrite?" )
819 .arg( saveDlg.name() ),
820 QMessageBox::Yes | QMessageBox::No );
821 if ( res != QMessageBox::Yes )
822 {
823 return;
824 }
825 mStyle->removeTextFormat( saveDlg.name() );
826 }
827
828 QStringList symbolTags = saveDlg.tags().split( ',' );
829 mStyle->addTextFormat( saveDlg.name(), format );
830 // make sure the foprmatis stored
831 mStyle->saveTextFormat( saveDlg.name(), format, saveDlg.isFavorite(), symbolTags );
832 return;
833 }
834}
835
836void QgsStyleManagerDialog::setThumbnailSize( int value )
837{
838 // value ranges from 0-10
839 const double iconSize = Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * ( value * 2.5 + 10 );
840 // set a grid size which allows sufficient vertical spacing to fit reasonably sized entity names
841 const double spacing = Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * ( value * 2.2 + 14 );
842 const double verticalSpacing = Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 7
843 + iconSize * 0.8;
844 listItems->setIconSize( QSize( static_cast< int >( iconSize ), static_cast< int >( iconSize * 0.9 ) ) );
845 listItems->setGridSize( QSize( static_cast< int >( spacing ), static_cast< int >( verticalSpacing ) ) );
846 if ( mModel )
847 {
848 mModel->addDesiredIconSize( listItems->iconSize() );
849 }
850
851 QgsSettings().setValue( QStringLiteral( "Windows/StyleV2Manager/thumbnailSize" ), value, QgsSettings::Gui );
852}
853
854int QgsStyleManagerDialog::selectedItemType()
855{
856 QModelIndex index = listItems->selectionModel()->currentIndex();
857 if ( !index.isValid() )
858 return 0;
859
860 const QgsStyle::StyleEntity entity = static_cast< QgsStyle::StyleEntity >( mModel->data( index, QgsStyleModel::TypeRole ).toInt() );
861 if ( entity == QgsStyle::ColorrampEntity )
862 return 3;
863 else if ( entity == QgsStyle::TextFormatEntity )
864 return 4;
865 else if ( entity == QgsStyle::LabelSettingsEntity )
866 return 5;
867 else if ( entity == QgsStyle::LegendPatchShapeEntity )
868 return 6;
869 else if ( entity == QgsStyle::Symbol3DEntity )
870 return 7;
871
872 return mModel->data( index, QgsStyleModel::SymbolTypeRole ).toInt();
873}
874
875bool QgsStyleManagerDialog::allTypesSelected() const
876{
877 return tabItemType->currentIndex() == 0;
878}
879
880bool QgsStyleManagerDialog::isReadOnly() const
881{
882 return mReadOnly || ( mStyle && mStyle->isReadOnly() );
883}
884
885QList< QgsStyleManagerDialog::ItemDetails > QgsStyleManagerDialog::selectedItems()
886{
887 QList<QgsStyleManagerDialog::ItemDetails > res;
888 QModelIndexList indices = listItems->selectionModel()->selectedRows();
889 for ( const QModelIndex &index : indices )
890 {
891 if ( !index.isValid() )
892 continue;
893
894 ItemDetails details;
895 details.entityType = static_cast< QgsStyle::StyleEntity >( mModel->data( index, QgsStyleModel::TypeRole ).toInt() );
896 if ( details.entityType == QgsStyle::SymbolEntity )
897 details.symbolType = static_cast< Qgis::SymbolType >( mModel->data( index, QgsStyleModel::SymbolTypeRole ).toInt() );
898 details.name = mModel->data( mModel->index( index.row(), QgsStyleModel::Name, index.parent() ), Qt::DisplayRole ).toString();
899
900 res << details;
901 }
902 return res;
903}
904
905int QgsStyleManagerDialog::copyItems( const QList<QgsStyleManagerDialog::ItemDetails> &items, QgsStyle *src, QgsStyle *dst, QWidget *parentWidget,
906 std::unique_ptr< QgsTemporaryCursorOverride > &cursorOverride, bool isImport, const QStringList &importTags, bool addToFavorites, bool ignoreSourceTags )
907{
908 bool prompt = true;
909 bool overwriteAll = true;
910 int count = 0;
911
912 const QStringList favoriteSymbols = src->symbolsOfFavorite( QgsStyle::SymbolEntity );
913 const QStringList favoriteColorramps = src->symbolsOfFavorite( QgsStyle::ColorrampEntity );
914 const QStringList favoriteTextFormats = src->symbolsOfFavorite( QgsStyle::TextFormatEntity );
915 const QStringList favoriteLabelSettings = src->symbolsOfFavorite( QgsStyle::LabelSettingsEntity );
916 const QStringList favoriteLegendPatchShapes = src->symbolsOfFavorite( QgsStyle::LegendPatchShapeEntity );
917 const QStringList favorite3dSymbols = src->symbolsOfFavorite( QgsStyle::Symbol3DEntity );
918
919 for ( auto &details : items )
920 {
921 QStringList symbolTags;
922 if ( !ignoreSourceTags )
923 {
924 symbolTags = src->tagsOfSymbol( details.entityType, details.name );
925 }
926
927 bool addItemToFavorites = false;
928 if ( isImport )
929 {
930 symbolTags << importTags;
931 addItemToFavorites = addToFavorites;
932 }
933
934 switch ( details.entityType )
935 {
937 {
938 std::unique_ptr< QgsSymbol > symbol( src->symbol( details.name ) );
939 if ( !symbol )
940 continue;
941
942 const bool hasDuplicateName = dst->symbolNames().contains( details.name );
943 bool overwriteThis = false;
944 if ( isImport )
945 addItemToFavorites = favoriteSymbols.contains( details.name );
946
947 if ( hasDuplicateName && prompt )
948 {
949 cursorOverride.reset();
950 int res = QMessageBox::warning( parentWidget, isImport ? tr( "Import Symbol" ) : tr( "Export Symbol" ),
951 tr( "A symbol with the name “%1” already exists.\nOverwrite?" )
952 .arg( details.name ),
953 QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll | QMessageBox::Cancel );
954 cursorOverride = std::make_unique< QgsTemporaryCursorOverride >( Qt::WaitCursor );
955 switch ( res )
956 {
957 case QMessageBox::Cancel:
958 return count;
959
960 case QMessageBox::No:
961 continue;
962
963 case QMessageBox::Yes:
964 overwriteThis = true;
965 break;
966
967 case QMessageBox::YesToAll:
968 prompt = false;
969 overwriteAll = true;
970 break;
971
972 case QMessageBox::NoToAll:
973 prompt = false;
974 overwriteAll = false;
975 break;
976 }
977 }
978
979 if ( !hasDuplicateName || overwriteAll || overwriteThis )
980 {
981 QgsSymbol *newSymbol = symbol.get();
982 dst->addSymbol( details.name, symbol.release() );
983 dst->saveSymbol( details.name, newSymbol, addItemToFavorites, symbolTags );
984 count++;
985 }
986 break;
987 }
988
990 {
991 std::unique_ptr< QgsColorRamp > ramp( src->colorRamp( details.name ) );
992 if ( !ramp )
993 continue;
994
995 const bool hasDuplicateName = dst->colorRampNames().contains( details.name );
996 bool overwriteThis = false;
997 if ( isImport )
998 addItemToFavorites = favoriteColorramps.contains( details.name );
999
1000 if ( hasDuplicateName && prompt )
1001 {
1002 cursorOverride.reset();
1003 int res = QMessageBox::warning( parentWidget, isImport ? tr( "Import Color Ramp" ) : tr( "Export Color Ramp" ),
1004 tr( "A color ramp with the name “%1” already exists.\nOverwrite?" )
1005 .arg( details.name ),
1006 QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll | QMessageBox::Cancel );
1007 cursorOverride = std::make_unique< QgsTemporaryCursorOverride >( Qt::WaitCursor );
1008 switch ( res )
1009 {
1010 case QMessageBox::Cancel:
1011 return count;
1012
1013 case QMessageBox::No:
1014 continue;
1015
1016 case QMessageBox::Yes:
1017 overwriteThis = true;
1018 break;
1019
1020 case QMessageBox::YesToAll:
1021 prompt = false;
1022 overwriteAll = true;
1023 break;
1024
1025 case QMessageBox::NoToAll:
1026 prompt = false;
1027 overwriteAll = false;
1028 break;
1029 }
1030 }
1031
1032 if ( !hasDuplicateName || overwriteAll || overwriteThis )
1033 {
1034 QgsColorRamp *newRamp = ramp.get();
1035 dst->addColorRamp( details.name, ramp.release() );
1036 dst->saveColorRamp( details.name, newRamp, addItemToFavorites, symbolTags );
1037 count++;
1038 }
1039 break;
1040 }
1041
1043 {
1044 const QgsTextFormat format( src->textFormat( details.name ) );
1045
1046 const bool hasDuplicateName = dst->textFormatNames().contains( details.name );
1047 bool overwriteThis = false;
1048 if ( isImport )
1049 addItemToFavorites = favoriteTextFormats.contains( details.name );
1050
1051 if ( hasDuplicateName && prompt )
1052 {
1053 cursorOverride.reset();
1054 int res = QMessageBox::warning( parentWidget, isImport ? tr( "Import Text Format" ) : tr( "Export Text Format" ),
1055 tr( "A text format with the name “%1” already exists.\nOverwrite?" )
1056 .arg( details.name ),
1057 QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll | QMessageBox::Cancel );
1058 cursorOverride = std::make_unique< QgsTemporaryCursorOverride >( Qt::WaitCursor );
1059 switch ( res )
1060 {
1061 case QMessageBox::Cancel:
1062 return count;
1063
1064 case QMessageBox::No:
1065 continue;
1066
1067 case QMessageBox::Yes:
1068 overwriteThis = true;
1069 break;
1070
1071 case QMessageBox::YesToAll:
1072 prompt = false;
1073 overwriteAll = true;
1074 break;
1075
1076 case QMessageBox::NoToAll:
1077 prompt = false;
1078 overwriteAll = false;
1079 break;
1080 }
1081 }
1082
1083 if ( !hasDuplicateName || overwriteAll || overwriteThis )
1084 {
1085 dst->addTextFormat( details.name, format );
1086 dst->saveTextFormat( details.name, format, addItemToFavorites, symbolTags );
1087 count++;
1088 }
1089 break;
1090 }
1091
1093 {
1094 const QgsPalLayerSettings settings( src->labelSettings( details.name ) );
1095
1096 const bool hasDuplicateName = dst->labelSettingsNames().contains( details.name );
1097 bool overwriteThis = false;
1098 if ( isImport )
1099 addItemToFavorites = favoriteLabelSettings.contains( details.name );
1100
1101 if ( hasDuplicateName && prompt )
1102 {
1103 cursorOverride.reset();
1104 int res = QMessageBox::warning( parentWidget, isImport ? tr( "Import Label Settings" ) : tr( "Export Label Settings" ),
1105 tr( "Label settings with the name “%1” already exist.\nOverwrite?" )
1106 .arg( details.name ),
1107 QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll | QMessageBox::Cancel );
1108 cursorOverride = std::make_unique< QgsTemporaryCursorOverride >( Qt::WaitCursor );
1109 switch ( res )
1110 {
1111 case QMessageBox::Cancel:
1112 return count;
1113
1114 case QMessageBox::No:
1115 continue;
1116
1117 case QMessageBox::Yes:
1118 overwriteThis = true;
1119 break;
1120
1121 case QMessageBox::YesToAll:
1122 prompt = false;
1123 overwriteAll = true;
1124 break;
1125
1126 case QMessageBox::NoToAll:
1127 prompt = false;
1128 overwriteAll = false;
1129 break;
1130 }
1131 }
1132
1133 if ( !hasDuplicateName || overwriteAll || overwriteThis )
1134 {
1135 dst->addLabelSettings( details.name, settings );
1136 dst->saveLabelSettings( details.name, settings, addItemToFavorites, symbolTags );
1137 count++;
1138 }
1139 break;
1140 }
1141
1143 {
1144 const QgsLegendPatchShape shape( src->legendPatchShape( details.name ) );
1145
1146 const bool hasDuplicateName = dst->legendPatchShapeNames().contains( details.name );
1147 bool overwriteThis = false;
1148 if ( isImport )
1149 addItemToFavorites = favoriteLegendPatchShapes.contains( details.name );
1150
1151 if ( hasDuplicateName && prompt )
1152 {
1153 cursorOverride.reset();
1154 int res = QMessageBox::warning( parentWidget, isImport ? tr( "Import Legend Patch Shape" ) : tr( "Export Legend Patch Shape" ),
1155 tr( "Legend patch shape with the name “%1” already exist.\nOverwrite?" )
1156 .arg( details.name ),
1157 QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll | QMessageBox::Cancel );
1158 cursorOverride = std::make_unique< QgsTemporaryCursorOverride >( Qt::WaitCursor );
1159 switch ( res )
1160 {
1161 case QMessageBox::Cancel:
1162 return count;
1163
1164 case QMessageBox::No:
1165 continue;
1166
1167 case QMessageBox::Yes:
1168 overwriteThis = true;
1169 break;
1170
1171 case QMessageBox::YesToAll:
1172 prompt = false;
1173 overwriteAll = true;
1174 break;
1175
1176 case QMessageBox::NoToAll:
1177 prompt = false;
1178 overwriteAll = false;
1179 break;
1180 }
1181 }
1182
1183 if ( !hasDuplicateName || overwriteAll || overwriteThis )
1184 {
1185 dst->addLegendPatchShape( details.name, shape );
1186 dst->saveLegendPatchShape( details.name, shape, addItemToFavorites, symbolTags );
1187 count++;
1188 }
1189 break;
1190 }
1191
1193 {
1194 std::unique_ptr< QgsAbstract3DSymbol > symbol( src->symbol3D( details.name ) );
1195 if ( !symbol )
1196 continue;
1197
1198 const bool hasDuplicateName = dst->symbol3DNames().contains( details.name );
1199 bool overwriteThis = false;
1200 if ( isImport )
1201 addItemToFavorites = favorite3dSymbols.contains( details.name );
1202
1203 if ( hasDuplicateName && prompt )
1204 {
1205 cursorOverride.reset();
1206 int res = QMessageBox::warning( parentWidget, isImport ? tr( "Import 3D Symbol" ) : tr( "Export 3D Symbol" ),
1207 tr( "A 3D symbol with the name “%1” already exists.\nOverwrite?" )
1208 .arg( details.name ),
1209 QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll | QMessageBox::Cancel );
1210 cursorOverride = std::make_unique< QgsTemporaryCursorOverride >( Qt::WaitCursor );
1211 switch ( res )
1212 {
1213 case QMessageBox::Cancel:
1214 return count;
1215
1216 case QMessageBox::No:
1217 continue;
1218
1219 case QMessageBox::Yes:
1220 overwriteThis = true;
1221 break;
1222
1223 case QMessageBox::YesToAll:
1224 prompt = false;
1225 overwriteAll = true;
1226 break;
1227
1228 case QMessageBox::NoToAll:
1229 prompt = false;
1230 overwriteAll = false;
1231 break;
1232 }
1233 }
1234
1235 if ( !hasDuplicateName || overwriteAll || overwriteThis )
1236 {
1237 QgsAbstract3DSymbol *newSymbol = symbol.get();
1238 dst->addSymbol3D( details.name, symbol.release() );
1239 dst->saveSymbol3D( details.name, newSymbol, addItemToFavorites, symbolTags );
1240 count++;
1241 }
1242 break;
1243 }
1244
1247 break;
1248
1249 }
1250 }
1251 return count;
1252}
1253
1254bool QgsStyleManagerDialog::addTextFormat()
1255{
1256 QgsTextFormat format;
1257 QgsTextFormatDialog formatDlg( format, nullptr, this );
1258 formatDlg.setWindowTitle( tr( "New Text Format" ) );
1259 if ( !formatDlg.exec() )
1260 return false;
1261 format = formatDlg.format();
1262
1264 const QString defaultTag = groupTree->currentIndex().isValid() ? groupTree->currentIndex().data( GroupModelRoles::TagName ).toString() : QString();
1265 saveDlg.setDefaultTags( defaultTag );
1266 if ( !saveDlg.exec() )
1267 return false;
1268 QString name = saveDlg.name();
1269
1270 // request valid/unique name
1271 bool nameInvalid = true;
1272 while ( nameInvalid )
1273 {
1274 // validate name
1275 if ( name.isEmpty() )
1276 {
1277 QMessageBox::warning( this, tr( "Save Text Format" ),
1278 tr( "Cannot save text format without name. Enter a name." ) );
1279 }
1280 else if ( mStyle->textFormatNames().contains( name ) )
1281 {
1282 int res = QMessageBox::warning( this, tr( "Save Text Format" ),
1283 tr( "Text format with name '%1' already exists. Overwrite?" )
1284 .arg( name ),
1285 QMessageBox::Yes | QMessageBox::No );
1286 if ( res == QMessageBox::Yes )
1287 {
1288 mStyle->removeTextFormat( name );
1289 nameInvalid = false;
1290 }
1291 }
1292 else
1293 {
1294 // valid name
1295 nameInvalid = false;
1296 }
1297 if ( nameInvalid )
1298 {
1299 bool ok;
1300 name = QInputDialog::getText( this, tr( "Text Format Name" ),
1301 tr( "Please enter a name for new text format:" ),
1302 QLineEdit::Normal, name, &ok );
1303 if ( !ok )
1304 {
1305 return false;
1306 }
1307 }
1308 }
1309
1310 QStringList symbolTags = saveDlg.tags().split( ',' );
1311
1312 // add new format to style and re-populate the list
1313 mStyle->addTextFormat( name, format );
1314 mStyle->saveTextFormat( name, format, saveDlg.isFavorite(), symbolTags );
1315
1316 mModified = true;
1317 return true;
1318}
1319
1321{
1322 groupChanged( groupTree->selectionModel()->currentIndex() );
1323}
1324
1325void QgsStyleManagerDialog::populateSymbols( const QStringList &, bool )
1326{
1327}
1328
1329void QgsStyleManagerDialog::populateColorRamps( const QStringList &, bool )
1330{
1331}
1332
1334{
1335 switch ( tabItemType->currentIndex() )
1336 {
1337 case 1:
1338 return static_cast< int >( Qgis::SymbolType::Marker );
1339 case 2:
1340 return static_cast< int >( Qgis::SymbolType::Line );
1341 case 3:
1342 return static_cast< int >( Qgis::SymbolType::Fill );
1343 case 4:
1344 return 3;
1345 case 5:
1346 return 4;
1347 case 6:
1348 return 5;
1349 case 7:
1350 return 6;
1351 case 8:
1352 return 7;
1353 default:
1354 return 0;
1355 }
1356}
1357
1359{
1360 QModelIndex index = listItems->selectionModel()->currentIndex();
1361 if ( !index.isValid() )
1362 return QString();
1363
1364 return mModel->data( mModel->index( index.row(), QgsStyleModel::Name, index.parent() ), Qt::DisplayRole ).toString();
1365}
1366
1368{
1369 bool changed = false;
1370 if ( currentItemType() < 3 )
1371 {
1372 changed = addSymbol();
1373 }
1374 else if ( currentItemType() == 3 )
1375 {
1376 changed = addColorRamp();
1377 }
1378 else if ( currentItemType() == 4 )
1379 {
1380 changed = addTextFormat();
1381 }
1382 else if ( currentItemType() == 5 )
1383 {
1384 // actually never hit, because we present a submenu when adding label settings
1385 // changed = addLabelSettings();
1386 }
1387 else if ( currentItemType() == 6 )
1388 {
1389 // actually never hit, because we present a submenu when adding legend patches
1390 // changed = addLegendPatchShape();
1391 }
1392 else if ( currentItemType() == 7 )
1393 {
1394 // actually never hit, because we present a submenu when adding 3d symbols
1395 // changed = addSymbol3D();
1396 }
1397 else
1398 {
1399 Q_ASSERT( false && "not implemented" );
1400 }
1401
1402 if ( changed )
1403 {
1404 populateList();
1405 }
1406}
1407
1409{
1410 // create new symbol with current type
1411 QgsSymbol *symbol = nullptr;
1412 QString name = tr( "new symbol" );
1413 QString dialogTitle;
1414 switch ( symbolType == -1 ? currentItemType() : symbolType )
1415 {
1416 case static_cast< int >( Qgis::SymbolType::Marker ):
1417 symbol = new QgsMarkerSymbol();
1418 name = tr( "new marker" );
1419 dialogTitle = tr( "New Marker Symbol" );
1420 break;
1421 case static_cast< int>( Qgis::SymbolType::Line ):
1422 symbol = new QgsLineSymbol();
1423 name = tr( "new line" );
1424 dialogTitle = tr( "New Line Symbol" );
1425 break;
1426 case static_cast< int >( Qgis::SymbolType::Fill ):
1427 symbol = new QgsFillSymbol();
1428 name = tr( "new fill symbol" );
1429 dialogTitle = tr( "New Fill Symbol" );
1430 break;
1431 default:
1432 Q_ASSERT( false && "unknown symbol type" );
1433 return false;
1434 }
1435
1436 // get symbol design
1437 // NOTE : Set the parent widget as "this" to notify the Symbol selector
1438 // that, it is being called by Style Manager, so recursive calling
1439 // of style manager and symbol selector can be arrested
1440 // See also: editSymbol()
1441 QgsSymbolSelectorDialog dlg( symbol, mStyle, nullptr, this );
1442 dlg.setWindowTitle( dialogTitle );
1443 if ( dlg.exec() == 0 )
1444 {
1445 delete symbol;
1446 return false;
1447 }
1448
1449 QgsStyleSaveDialog saveDlg( this );
1450 const QString defaultTag = groupTree->currentIndex().isValid() ? groupTree->currentIndex().data( GroupModelRoles::TagName ).toString() : QString();
1451 saveDlg.setDefaultTags( defaultTag );
1452 if ( !saveDlg.exec() )
1453 {
1454 delete symbol;
1455 return false;
1456 }
1457
1458 name = saveDlg.name();
1459
1460 // request valid/unique name
1461 bool nameInvalid = true;
1462 while ( nameInvalid )
1463 {
1464 // validate name
1465 if ( name.isEmpty() )
1466 {
1467 QMessageBox::warning( this, tr( "Save Symbol" ),
1468 tr( "Cannot save symbol without name. Enter a name." ) );
1469 }
1470 else if ( mStyle->symbolNames().contains( name ) )
1471 {
1472 int res = QMessageBox::warning( this, tr( "Save Symbol" ),
1473 tr( "Symbol with name '%1' already exists. Overwrite?" )
1474 .arg( name ),
1475 QMessageBox::Yes | QMessageBox::No );
1476 if ( res == QMessageBox::Yes )
1477 {
1478 mStyle->removeSymbol( name );
1479 nameInvalid = false;
1480 }
1481 }
1482 else
1483 {
1484 // valid name
1485 nameInvalid = false;
1486 }
1487 if ( nameInvalid )
1488 {
1489 bool ok;
1490 name = QInputDialog::getText( this, tr( "Symbol Name" ),
1491 tr( "Please enter a name for new symbol:" ),
1492 QLineEdit::Normal, name, &ok );
1493 if ( !ok )
1494 {
1495 delete symbol;
1496 return false;
1497 }
1498 }
1499 }
1500
1501 QStringList symbolTags = saveDlg.tags().split( ',' );
1502
1503 // add new symbol to style and re-populate the list
1504 mStyle->addSymbol( name, symbol );
1505 mStyle->saveSymbol( name, symbol, saveDlg.isFavorite(), symbolTags );
1506
1507 mModified = true;
1508 return true;
1509}
1510
1511
1512QString QgsStyleManagerDialog::addColorRampStatic( QWidget *parent, QgsStyle *style, const QString &type )
1513{
1514 QString rampType = type;
1515
1516 if ( rampType.isEmpty() )
1517 {
1518 // let the user choose the color ramp type if rampType is not given
1519 bool ok = true;
1520 const QList< QPair< QString, QString > > rampTypes = QgsColorRamp::rampTypes();
1521 QStringList rampTypeNames;
1522 rampTypeNames.reserve( rampTypes.size() );
1523 for ( const QPair< QString, QString > &type : rampTypes )
1524 rampTypeNames << type.second;
1525 const QString selectedRampTypeName = QInputDialog::getItem( parent, tr( "Color Ramp Type" ),
1526 tr( "Please select color ramp type:" ), rampTypeNames, 0, false, &ok );
1527 if ( !ok || selectedRampTypeName.isEmpty() )
1528 return QString();
1529
1530 rampType = rampTypes.value( rampTypeNames.indexOf( selectedRampTypeName ) ).first;
1531 }
1532
1533 QString name = tr( "new ramp" );
1534
1535 std::unique_ptr< QgsColorRamp > ramp;
1536 if ( rampType == QgsGradientColorRamp::typeString() )
1537 {
1539 dlg.setWindowTitle( tr( "New Gradient Color Ramp" ) );
1540 if ( !dlg.exec() )
1541 {
1542 return QString();
1543 }
1544 ramp.reset( dlg.ramp().clone() );
1545 name = tr( "new gradient ramp" );
1546 }
1547 else if ( rampType == QgsLimitedRandomColorRamp::typeString() )
1548 {
1550 dlg.setWindowTitle( tr( "New Random Color Ramp" ) );
1551 if ( !dlg.exec() )
1552 {
1553 return QString();
1554 }
1555 ramp.reset( dlg.ramp().clone() );
1556 name = tr( "new random ramp" );
1557 }
1558 else if ( rampType == QgsColorBrewerColorRamp::typeString() )
1559 {
1561 dlg.setWindowTitle( tr( "New ColorBrewer Ramp" ) );
1562 if ( !dlg.exec() )
1563 {
1564 return QString();
1565 }
1566 ramp.reset( dlg.ramp().clone() );
1567 name = dlg.ramp().schemeName() + QString::number( dlg.ramp().colors() );
1568 }
1569 else if ( rampType == QgsPresetSchemeColorRamp::typeString() )
1570 {
1572 dlg.setWindowTitle( tr( "New Preset Color Ramp" ) );
1573 if ( !dlg.exec() )
1574 {
1575 return QString();
1576 }
1577 ramp.reset( dlg.ramp().clone() );
1578 name = tr( "new preset ramp" );
1579 }
1580 else if ( rampType == QgsCptCityColorRamp::typeString() )
1581 {
1582 QgsCptCityColorRampDialog dlg( QgsCptCityColorRamp( QString(), QString() ), parent );
1583 dlg.setWindowTitle( tr( "New cpt-city Color Ramp" ) );
1584 if ( !dlg.exec() )
1585 {
1586 return QString();
1587 }
1588 // name = dlg.selectedName();
1589 name = QFileInfo( dlg.ramp().schemeName() ).baseName() + dlg.ramp().variantName();
1590 if ( dlg.saveAsGradientRamp() )
1591 {
1592 ramp.reset( dlg.ramp().cloneGradientRamp() );
1593 }
1594 else
1595 {
1596 ramp.reset( dlg.ramp().clone() );
1597 }
1598 }
1599 else
1600 {
1601 // Q_ASSERT( 0 && "invalid ramp type" );
1602 // bailing out is rather harsh!
1603 QgsDebugMsg( QStringLiteral( "invalid ramp type %1" ).arg( rampType ) );
1604 return QString();
1605 }
1606
1608 if ( !saveDlg.exec() )
1609 {
1610 return QString();
1611 }
1612
1613 name = saveDlg.name();
1614
1615 // get valid/unique name
1616 bool nameInvalid = true;
1617 while ( nameInvalid )
1618 {
1619 // validate name
1620 if ( name.isEmpty() )
1621 {
1622 QMessageBox::warning( parent, tr( "Save Color Ramp" ),
1623 tr( "Cannot save color ramp without name. Enter a name." ) );
1624 }
1625 else if ( style->colorRampNames().contains( name ) )
1626 {
1627 int res = QMessageBox::warning( parent, tr( "Save Color Ramp" ),
1628 tr( "Color ramp with name '%1' already exists. Overwrite?" )
1629 .arg( name ),
1630 QMessageBox::Yes | QMessageBox::No );
1631 if ( res == QMessageBox::Yes )
1632 {
1633 nameInvalid = false;
1634 }
1635 }
1636 else
1637 {
1638 // valid name
1639 nameInvalid = false;
1640 }
1641 if ( nameInvalid )
1642 {
1643 bool ok;
1644 name = QInputDialog::getText( parent, tr( "Color Ramp Name" ),
1645 tr( "Please enter a name for new color ramp:" ),
1646 QLineEdit::Normal, name, &ok );
1647 if ( !ok )
1648 {
1649 return QString();
1650 }
1651 }
1652 }
1653
1654 QStringList colorRampTags = saveDlg.tags().split( ',' );
1655 QgsColorRamp *r = ramp.release();
1656
1657 // add new symbol to style and re-populate the list
1658 style->addColorRamp( name, r );
1659 style->saveColorRamp( name, r, saveDlg.isFavorite(), colorRampTags );
1660
1661 return name;
1662}
1663
1665{
1666 mFavoritesGroupVisible = show;
1668}
1669
1671{
1672 mSmartGroupVisible = show;
1674}
1675
1677{
1678 mBaseName = name;
1679}
1680
1682{
1683 raise();
1684 setWindowState( windowState() & ~Qt::WindowMinimized );
1685 activateWindow();
1686}
1687
1688bool QgsStyleManagerDialog::addColorRamp( const QString &type )
1689{
1690 // pass the action text, which is the color ramp type
1691 QString rampName = addColorRampStatic( this, mStyle, type );
1692 if ( !rampName.isEmpty() )
1693 {
1694 mModified = true;
1695 populateList();
1696 return true;
1697 }
1698
1699 return false;
1700}
1701
1703{
1704 if ( selectedItemType() < 3 )
1705 {
1706 editSymbol();
1707 }
1708 else if ( selectedItemType() == 3 )
1709 {
1710 editColorRamp();
1711 }
1712 else if ( selectedItemType() == 4 )
1713 {
1714 editTextFormat();
1715 }
1716 else if ( selectedItemType() == 5 )
1717 {
1718 editLabelSettings();
1719 }
1720 else if ( selectedItemType() == 6 )
1721 {
1722 editLegendPatchShape();
1723 }
1724 else if ( selectedItemType() == 7 )
1725 {
1726 editSymbol3D();
1727 }
1728 else
1729 {
1730 Q_ASSERT( false && "not implemented" );
1731 }
1732}
1733
1735{
1736 QString symbolName = currentItemName();
1737 if ( symbolName.isEmpty() )
1738 return false;
1739
1740 std::unique_ptr< QgsSymbol > symbol( mStyle->symbol( symbolName ) );
1741
1742 // let the user edit the symbol and update list when done
1743 QgsSymbolSelectorDialog dlg( symbol.get(), mStyle, nullptr, this );
1744 dlg.setWindowTitle( symbolName );
1745 if ( isReadOnly() )
1746 dlg.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
1747
1748 if ( !dlg.exec() )
1749 return false;
1750
1751 // by adding symbol to style with the same name the old effectively gets overwritten
1752 mStyle->addSymbol( symbolName, symbol.release(), true );
1753 mModified = true;
1754 return true;
1755}
1756
1758{
1759 QString name = currentItemName();
1760 if ( name.isEmpty() )
1761 return false;
1762
1763 std::unique_ptr< QgsColorRamp > ramp( mStyle->colorRamp( name ) );
1764
1765 if ( ramp->type() == QgsGradientColorRamp::typeString() )
1766 {
1767 QgsGradientColorRamp *gradRamp = static_cast<QgsGradientColorRamp *>( ramp.get() );
1768 QgsGradientColorRampDialog dlg( *gradRamp, this );
1769 dlg.setWindowTitle( name );
1770 if ( isReadOnly() )
1771 dlg.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
1772
1773 if ( !dlg.exec() )
1774 {
1775 return false;
1776 }
1777 ramp.reset( dlg.ramp().clone() );
1778 }
1779 else if ( ramp->type() == QgsLimitedRandomColorRamp::typeString() )
1780 {
1781 QgsLimitedRandomColorRamp *randRamp = static_cast<QgsLimitedRandomColorRamp *>( ramp.get() );
1782 QgsLimitedRandomColorRampDialog dlg( *randRamp, this );
1783 dlg.setWindowTitle( name );
1784 if ( isReadOnly() )
1785 dlg.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
1786
1787 if ( !dlg.exec() )
1788 {
1789 return false;
1790 }
1791 ramp.reset( dlg.ramp().clone() );
1792 }
1793 else if ( ramp->type() == QgsColorBrewerColorRamp::typeString() )
1794 {
1795 QgsColorBrewerColorRamp *brewerRamp = static_cast<QgsColorBrewerColorRamp *>( ramp.get() );
1796 QgsColorBrewerColorRampDialog dlg( *brewerRamp, this );
1797 dlg.setWindowTitle( name );
1798 if ( isReadOnly() )
1799 dlg.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
1800
1801 if ( !dlg.exec() )
1802 {
1803 return false;
1804 }
1805 ramp.reset( dlg.ramp().clone() );
1806 }
1807 else if ( ramp->type() == QgsPresetSchemeColorRamp::typeString() )
1808 {
1809 QgsPresetSchemeColorRamp *presetRamp = static_cast<QgsPresetSchemeColorRamp *>( ramp.get() );
1810 QgsPresetColorRampDialog dlg( *presetRamp, this );
1811 dlg.setWindowTitle( name );
1812 if ( isReadOnly() )
1813 dlg.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
1814
1815 if ( !dlg.exec() )
1816 {
1817 return false;
1818 }
1819 ramp.reset( dlg.ramp().clone() );
1820 }
1821 else if ( ramp->type() == QgsCptCityColorRamp::typeString() )
1822 {
1823 QgsCptCityColorRamp *cptCityRamp = static_cast<QgsCptCityColorRamp *>( ramp.get() );
1824 QgsCptCityColorRampDialog dlg( *cptCityRamp, this );
1825 dlg.setWindowTitle( name );
1826 if ( isReadOnly() )
1827 dlg.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
1828
1829 if ( !dlg.exec() )
1830 {
1831 return false;
1832 }
1833 if ( dlg.saveAsGradientRamp() )
1834 {
1835 ramp.reset( dlg.ramp().cloneGradientRamp() );
1836 }
1837 else
1838 {
1839 ramp.reset( dlg.ramp().clone() );
1840 }
1841 }
1842 else
1843 {
1844 Q_ASSERT( false && "invalid ramp type" );
1845 }
1846
1847 mStyle->addColorRamp( name, ramp.release(), true );
1848 mModified = true;
1849 return true;
1850}
1851
1852bool QgsStyleManagerDialog::editTextFormat()
1853{
1854 const QString formatName = currentItemName();
1855 if ( formatName.isEmpty() )
1856 return false;
1857
1858 QgsTextFormat format = mStyle->textFormat( formatName );
1859
1860 // let the user edit the format and update list when done
1861 QgsTextFormatDialog dlg( format, nullptr, this );
1862 dlg.setWindowTitle( formatName );
1863 if ( isReadOnly() )
1864 dlg.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
1865
1866 if ( !dlg.exec() )
1867 return false;
1868
1869 // by adding format to style with the same name the old effectively gets overwritten
1870 mStyle->addTextFormat( formatName, dlg.format(), true );
1871 mModified = true;
1872 return true;
1873}
1874
1875bool QgsStyleManagerDialog::addLabelSettings( Qgis::GeometryType type )
1876{
1877 QgsPalLayerSettings settings;
1878 QgsLabelSettingsDialog settingsDlg( settings, nullptr, nullptr, this, type );
1879 settingsDlg.setWindowTitle( tr( "New Label Settings" ) );
1880 if ( isReadOnly() )
1881 settingsDlg.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
1882
1883 if ( !settingsDlg.exec() )
1884 return false;
1885
1886 settings = settingsDlg.settings();
1887 settings.layerType = type;
1888
1890 const QString defaultTag = groupTree->currentIndex().isValid() ? groupTree->currentIndex().data( GroupModelRoles::TagName ).toString() : QString();
1891 saveDlg.setDefaultTags( defaultTag );
1892 if ( !saveDlg.exec() )
1893 return false;
1894 QString name = saveDlg.name();
1895
1896 // request valid/unique name
1897 bool nameInvalid = true;
1898 while ( nameInvalid )
1899 {
1900 // validate name
1901 if ( name.isEmpty() )
1902 {
1903 QMessageBox::warning( this, tr( "Save Label Settings" ),
1904 tr( "Cannot save label settings without a name. Enter a name." ) );
1905 }
1906 else if ( mStyle->labelSettingsNames().contains( name ) )
1907 {
1908 int res = QMessageBox::warning( this, tr( "Save Label Settings" ),
1909 tr( "Label settings with the name '%1' already exist. Overwrite?" )
1910 .arg( name ),
1911 QMessageBox::Yes | QMessageBox::No );
1912 if ( res == QMessageBox::Yes )
1913 {
1914 mStyle->removeLabelSettings( name );
1915 nameInvalid = false;
1916 }
1917 }
1918 else
1919 {
1920 // valid name
1921 nameInvalid = false;
1922 }
1923 if ( nameInvalid )
1924 {
1925 bool ok;
1926 name = QInputDialog::getText( this, tr( "Label Settings Name" ),
1927 tr( "Please enter a name for the new label settings:" ),
1928 QLineEdit::Normal, name, &ok );
1929 if ( !ok )
1930 {
1931 return false;
1932 }
1933 }
1934 }
1935
1936 QStringList symbolTags = saveDlg.tags().split( ',' );
1937
1938 // add new format to style and re-populate the list
1939 mStyle->addLabelSettings( name, settings );
1940 mStyle->saveLabelSettings( name, settings, saveDlg.isFavorite(), symbolTags );
1941
1942 mModified = true;
1943 return true;
1944}
1945
1946bool QgsStyleManagerDialog::editLabelSettings()
1947{
1948 const QString formatName = currentItemName();
1949 if ( formatName.isEmpty() )
1950 return false;
1951
1952 QgsPalLayerSettings settings = mStyle->labelSettings( formatName );
1953 Qgis::GeometryType geomType = settings.layerType;
1954
1955 // let the user edit the settings and update list when done
1956 QgsLabelSettingsDialog dlg( settings, nullptr, nullptr, this, geomType );
1957 dlg.setWindowTitle( formatName );
1958 if ( !dlg.exec() )
1959 return false;
1960
1961 settings = dlg.settings();
1962 settings.layerType = geomType;
1963
1964 // by adding format to style with the same name the old effectively gets overwritten
1965 mStyle->addLabelSettings( formatName, settings, true );
1966 mModified = true;
1967 return true;
1968}
1969
1970bool QgsStyleManagerDialog::addLegendPatchShape( Qgis::SymbolType type )
1971{
1972 QgsLegendPatchShape shape = mStyle->defaultPatch( type, QSizeF( 10, 5 ) );
1973 QgsLegendPatchShapeDialog dialog( shape, this );
1974 dialog.setWindowTitle( tr( "New Legend Patch Shape" ) );
1975 if ( isReadOnly() )
1976 dialog.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
1977
1978 if ( !dialog.exec() )
1979 return false;
1980
1981 shape = dialog.shape();
1982
1984 const QString defaultTag = groupTree->currentIndex().isValid() ? groupTree->currentIndex().data( GroupModelRoles::TagName ).toString() : QString();
1985 saveDlg.setDefaultTags( defaultTag );
1986 if ( !saveDlg.exec() )
1987 return false;
1988 QString name = saveDlg.name();
1989
1990 // request valid/unique name
1991 bool nameInvalid = true;
1992 while ( nameInvalid )
1993 {
1994 // validate name
1995 if ( name.isEmpty() )
1996 {
1997 QMessageBox::warning( this, tr( "Save Legend Patch Shape" ),
1998 tr( "Cannot save legend patch shapes without a name. Enter a name." ) );
1999 }
2000 else if ( mStyle->legendPatchShapeNames().contains( name ) )
2001 {
2002 int res = QMessageBox::warning( this, tr( "Save Legend Patch Shape" ),
2003 tr( "A legend patch shape with the name '%1' already exists. Overwrite?" )
2004 .arg( name ),
2005 QMessageBox::Yes | QMessageBox::No );
2006 if ( res == QMessageBox::Yes )
2007 {
2009 nameInvalid = false;
2010 }
2011 }
2012 else
2013 {
2014 // valid name
2015 nameInvalid = false;
2016 }
2017 if ( nameInvalid )
2018 {
2019 bool ok;
2020 name = QInputDialog::getText( this, tr( "Legend Patch Shape Name" ),
2021 tr( "Please enter a name for the new legend patch shape:" ),
2022 QLineEdit::Normal, name, &ok );
2023 if ( !ok )
2024 {
2025 return false;
2026 }
2027 }
2028 }
2029
2030 QStringList symbolTags = saveDlg.tags().split( ',' );
2031
2032 // add new shape to style and re-populate the list
2033 mStyle->addLegendPatchShape( name, shape );
2034 mStyle->saveLegendPatchShape( name, shape, saveDlg.isFavorite(), symbolTags );
2035
2036 mModified = true;
2037 return true;
2038}
2039
2040bool QgsStyleManagerDialog::editLegendPatchShape()
2041{
2042 const QString shapeName = currentItemName();
2043 if ( shapeName.isEmpty() )
2044 return false;
2045
2046 QgsLegendPatchShape shape = mStyle->legendPatchShape( shapeName );
2047 if ( shape.isNull() )
2048 return false;
2049
2050 // let the user edit the shape and update list when done
2051 QgsLegendPatchShapeDialog dlg( shape, this );
2052 dlg.setWindowTitle( shapeName );
2053 if ( !dlg.exec() )
2054 return false;
2055
2056 shape = dlg.shape();
2057
2058 // by adding shape to style with the same name the old effectively gets overwritten
2059 mStyle->addLegendPatchShape( shapeName, shape, true );
2060 mModified = true;
2061 return true;
2062}
2063
2064bool QgsStyleManagerDialog::addSymbol3D( const QString &type )
2065{
2066 std::unique_ptr< QgsAbstract3DSymbol > symbol( QgsApplication::symbol3DRegistry()->createSymbol( type ) );
2067 if ( !symbol )
2068 return false;
2069
2070 Qgs3DSymbolDialog dialog( symbol.get(), this );
2071 dialog.setWindowTitle( tr( "New 3D Symbol" ) );
2072 if ( isReadOnly() )
2073 dialog.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
2074
2075 if ( !dialog.exec() )
2076 return false;
2077
2078 symbol.reset( dialog.symbol() );
2079 if ( !symbol )
2080 return false;
2081
2083 const QString defaultTag = groupTree->currentIndex().isValid() ? groupTree->currentIndex().data( GroupModelRoles::TagName ).toString() : QString();
2084 saveDlg.setDefaultTags( defaultTag );
2085 if ( !saveDlg.exec() )
2086 return false;
2087 QString name = saveDlg.name();
2088
2089 // request valid/unique name
2090 bool nameInvalid = true;
2091 while ( nameInvalid )
2092 {
2093 // validate name
2094 if ( name.isEmpty() )
2095 {
2096 QMessageBox::warning( this, tr( "Save 3D Symbol" ),
2097 tr( "Cannot save 3D symbols without a name. Enter a name." ) );
2098 }
2099 else if ( mStyle->symbol3DNames().contains( name ) )
2100 {
2101 int res = QMessageBox::warning( this, tr( "Save 3D Symbol" ),
2102 tr( "A 3D symbol with the name '%1' already exists. Overwrite?" )
2103 .arg( name ),
2104 QMessageBox::Yes | QMessageBox::No );
2105 if ( res == QMessageBox::Yes )
2106 {
2108 nameInvalid = false;
2109 }
2110 }
2111 else
2112 {
2113 // valid name
2114 nameInvalid = false;
2115 }
2116 if ( nameInvalid )
2117 {
2118 bool ok;
2119 name = QInputDialog::getText( this, tr( "3D Symbol Name" ),
2120 tr( "Please enter a name for the new 3D symbol:" ),
2121 QLineEdit::Normal, name, &ok );
2122 if ( !ok )
2123 {
2124 return false;
2125 }
2126 }
2127 }
2128
2129 QStringList symbolTags = saveDlg.tags().split( ',' );
2130
2131 // add new shape to style and re-populate the list
2132 QgsAbstract3DSymbol *newSymbol = symbol.get();
2133 mStyle->addSymbol3D( name, symbol.release() );
2134 mStyle->saveSymbol3D( name, newSymbol, saveDlg.isFavorite(), symbolTags );
2135
2136 mModified = true;
2137 return true;
2138}
2139
2140bool QgsStyleManagerDialog::editSymbol3D()
2141{
2142 const QString symbolName = currentItemName();
2143 if ( symbolName.isEmpty() )
2144 return false;
2145
2146 std::unique_ptr< QgsAbstract3DSymbol > symbol( mStyle->symbol3D( symbolName ) );
2147 if ( !symbol )
2148 return false;
2149
2150 // let the user edit the symbol and update list when done
2151 Qgs3DSymbolDialog dlg( symbol.get(), this );
2152 dlg.setWindowTitle( symbolName );
2153 if ( !dlg.exec() )
2154 return false;
2155
2156 symbol.reset( dlg.symbol() );
2157 if ( !symbol )
2158 return false;
2159
2160 // by adding symbol to style with the same name the old effectively gets overwritten
2161 mStyle->addSymbol3D( symbolName, symbol.release(), true );
2162 mModified = true;
2163 return true;
2164}
2165
2166void QgsStyleManagerDialog::addStyleDatabase( bool createNew )
2167{
2169 if ( initialFolder.isEmpty() )
2170 initialFolder = QDir::homePath();
2171
2172 QString databasePath = createNew
2173 ? QFileDialog::getSaveFileName(
2174 this,
2175 tr( "Create Style Database" ),
2176 initialFolder,
2177 tr( "Style databases" ) + " (*.db)" )
2178 : QFileDialog::getOpenFileName(
2179 this,
2180 tr( "Add Style Database" ),
2181 initialFolder,
2182 tr( "Style databases" ) + " (*.db *.xml)" );
2183 if ( ! databasePath.isEmpty() )
2184 {
2185 QgsStyleManagerDialog::settingLastStyleDatabaseFolder->setValue( QFileInfo( databasePath ).path() );
2186
2187 if ( createNew )
2188 {
2189 databasePath = QgsFileUtils::ensureFileNameHasExtension( databasePath, { QStringLiteral( "db" )} );
2190 if ( QFile::exists( databasePath ) )
2191 {
2192 QFile::remove( databasePath );
2193 }
2194 QgsStyle s;
2195 if ( !s.createDatabase( databasePath ) )
2196 {
2197 QMessageBox::warning( this, tr( "Create Style Database" ), tr( "The style database could not be created" ) );
2198 return;
2199 }
2200 }
2201
2203 setCurrentStyle( QgsProject::instance()->styleSettings()->styleAtPath( databasePath ) );
2204 }
2205}
2206
2208{
2209 const QList< ItemDetails > items = selectedItems();
2210
2211 if ( allTypesSelected() )
2212 {
2213 if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Remove Items" ),
2214 QString( tr( "Do you really want to remove %n item(s)?", nullptr, items.count() ) ),
2215 QMessageBox::Yes,
2216 QMessageBox::No ) )
2217 return;
2218 }
2219 else
2220 {
2221 if ( currentItemType() < 3 )
2222 {
2223 if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Remove Symbol" ),
2224 QString( tr( "Do you really want to remove %n symbol(s)?", nullptr, items.count() ) ),
2225 QMessageBox::Yes,
2226 QMessageBox::No ) )
2227 return;
2228 }
2229 else if ( currentItemType() == 3 )
2230 {
2231 if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Remove Color Ramp" ),
2232 QString( tr( "Do you really want to remove %n ramp(s)?", nullptr, items.count() ) ),
2233 QMessageBox::Yes,
2234 QMessageBox::No ) )
2235 return;
2236 }
2237 else if ( currentItemType() == 4 )
2238 {
2239 if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Remove Text Formats" ),
2240 QString( tr( "Do you really want to remove %n text format(s)?", nullptr, items.count() ) ),
2241 QMessageBox::Yes,
2242 QMessageBox::No ) )
2243 return;
2244 }
2245 else if ( currentItemType() == 5 )
2246 {
2247 if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Remove Label Settings" ),
2248 QString( tr( "Do you really want to remove %n label setting(s)?", nullptr, items.count() ) ),
2249 QMessageBox::Yes,
2250 QMessageBox::No ) )
2251 return;
2252 }
2253 else if ( currentItemType() == 6 )
2254 {
2255 if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Remove Legend Patch Shapes" ),
2256 QString( tr( "Do you really want to remove %n legend patch shape(s)?", nullptr, items.count() ) ),
2257 QMessageBox::Yes,
2258 QMessageBox::No ) )
2259 return;
2260 }
2261 else if ( currentItemType() == 7 )
2262 {
2263 if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Remove 3D Symbols" ),
2264 QString( tr( "Do you really want to remove %n 3D symbol(s)?", nullptr, items.count() ) ),
2265 QMessageBox::Yes,
2266 QMessageBox::No ) )
2267 return;
2268 }
2269 }
2270
2271 QgsTemporaryCursorOverride override( Qt::WaitCursor );
2272
2273 for ( const ItemDetails &details : items )
2274 {
2275 if ( details.name.isEmpty() )
2276 continue;
2277
2278 mStyle->removeEntityByName( details.entityType, details.name );
2279 }
2280
2281 mModified = true;
2282}
2283
2285{
2286 return false;
2287}
2288
2290{
2291 return false;
2292}
2293
2295{
2296}
2297
2299{
2300 QString dir = QFileDialog::getExistingDirectory( this, tr( "Export Selected Symbols as PNG" ),
2301 QDir::home().absolutePath(),
2302 QFileDialog::DontResolveSymlinks );
2303 exportSelectedItemsImages( dir, QStringLiteral( "png" ), QSize( 32, 32 ) );
2304}
2305
2307{
2308 QString dir = QFileDialog::getExistingDirectory( this, tr( "Export Selected Symbols as SVG" ),
2309 QDir::home().absolutePath(),
2310 QFileDialog::DontResolveSymlinks );
2311 exportSelectedItemsImages( dir, QStringLiteral( "svg" ), QSize( 32, 32 ) );
2312}
2313
2314
2315void QgsStyleManagerDialog::exportSelectedItemsImages( const QString &dir, const QString &format, QSize size )
2316{
2317 if ( dir.isEmpty() )
2318 return;
2319
2320 const QList< ItemDetails > items = selectedItems();
2321 for ( const ItemDetails &details : items )
2322 {
2323 if ( details.entityType != QgsStyle::SymbolEntity )
2324 continue;
2325
2326 QString path = dir + '/' + details.name + '.' + format;
2327 std::unique_ptr< QgsSymbol > sym( mStyle->symbol( details.name ) );
2328 if ( sym )
2329 sym->exportImage( path, format, size );
2330 }
2331}
2332
2334{
2336 dlg.exec();
2337}
2338
2340{
2342 dlg.exec();
2343 populateList();
2345}
2346
2347void QgsStyleManagerDialog::setBold( QStandardItem *item )
2348{
2349 QFont font = item->font();
2350 font.setBold( true );
2351 item->setFont( font );
2352}
2353
2355{
2356 if ( mBlockGroupUpdates )
2357 return;
2358
2359 QStandardItemModel *model = qobject_cast<QStandardItemModel *>( groupTree->model() );
2360 model->clear();
2361
2362 const bool readOnly = isReadOnly();
2363
2364 if ( mFavoritesGroupVisible )
2365 {
2366 QStandardItem *favoriteSymbols = new QStandardItem( tr( "Favorites" ) );
2367 favoriteSymbols->setData( "favorite" );
2368 favoriteSymbols->setEditable( false );
2369 setBold( favoriteSymbols );
2370 model->appendRow( favoriteSymbols );
2371 }
2372
2373 QStandardItem *allSymbols = new QStandardItem( tr( "All" ) );
2374 allSymbols->setData( "all" );
2375 allSymbols->setEditable( false );
2376 setBold( allSymbols );
2377 model->appendRow( allSymbols );
2378
2379 QStandardItem *taggroup = new QStandardItem( QString() ); //require empty name to get first order groups
2380 taggroup->setData( "tags" );
2381 taggroup->setEditable( false );
2382 QStringList tags = mStyle->tags();
2383 tags.sort();
2384 for ( const QString &tag : std::as_const( tags ) )
2385 {
2386 QStandardItem *item = new QStandardItem( tag );
2387 item->setData( mStyle->tagId( tag ) );
2388 item->setData( tag, GroupModelRoles::TagName );
2389 item->setEditable( !readOnly );
2390 taggroup->appendRow( item );
2391 }
2392 taggroup->setText( tr( "Tags" ) );//set title later
2393 setBold( taggroup );
2394 model->appendRow( taggroup );
2395
2396 if ( mSmartGroupVisible )
2397 {
2398 QStandardItem *smart = new QStandardItem( tr( "Smart Groups" ) );
2399 smart->setData( "smartgroups" );
2400 smart->setEditable( false );
2401 setBold( smart );
2402 QgsSymbolGroupMap sgMap = mStyle->smartgroupsListMap();
2403 QgsSymbolGroupMap::const_iterator i = sgMap.constBegin();
2404 while ( i != sgMap.constEnd() )
2405 {
2406 QStandardItem *item = new QStandardItem( i.value() );
2407 item->setData( i.key() );
2408 item->setEditable( !readOnly );
2409 smart->appendRow( item );
2410 ++i;
2411 }
2412 model->appendRow( smart );
2413 }
2414
2415 // expand things in the group tree
2416 int rows = model->rowCount( model->indexFromItem( model->invisibleRootItem() ) );
2417 for ( int i = 0; i < rows; i++ )
2418 {
2419 groupTree->setExpanded( model->indexFromItem( model->item( i ) ), true );
2420 }
2421}
2422
2423void QgsStyleManagerDialog::groupChanged( const QModelIndex &index )
2424{
2425 const QString category = index.data( Qt::UserRole + 1 ).toString();
2426 sPreviousTag = category;
2427
2428 const bool readOnly = isReadOnly();
2429
2430 if ( mGroupingMode && mModel )
2431 {
2432 mModel->setTagId( -1 );
2433 mModel->setSmartGroupId( -1 );
2434 mModel->setFavoritesOnly( false );
2435 mModel->setCheckTag( index.data( Qt::DisplayRole ).toString() );
2436 }
2437 else if ( category == QLatin1String( "all" ) || category == QLatin1String( "tags" ) || category == QLatin1String( "smartgroups" ) )
2438 {
2439 enableGroupInputs( false );
2440 if ( category == QLatin1String( "tags" ) )
2441 {
2442 actnAddTag->setEnabled( !readOnly );
2443 actnAddSmartgroup->setEnabled( false );
2444 }
2445 else if ( category == QLatin1String( "smartgroups" ) )
2446 {
2447 actnAddTag->setEnabled( false );
2448 actnAddSmartgroup->setEnabled( !readOnly );
2449 }
2450
2451 if ( mModel )
2452 {
2453 mModel->setTagId( -1 );
2454 mModel->setSmartGroupId( -1 );
2455 mModel->setFavoritesOnly( false );
2456 }
2457 }
2458 else if ( category == QLatin1String( "favorite" ) )
2459 {
2460 enableGroupInputs( false );
2461 mModel->setTagId( -1 );
2462 mModel->setSmartGroupId( -1 );
2463 mModel->setFavoritesOnly( true );
2464 }
2465 else if ( index.parent().data( Qt::UserRole + 1 ) == "smartgroups" )
2466 {
2467 actnRemoveGroup->setEnabled( !readOnly );
2468 btnManageGroups->setEnabled( !readOnly );
2469 const int groupId = index.data( Qt::UserRole + 1 ).toInt();
2470 if ( mModel )
2471 {
2472 mModel->setTagId( -1 );
2473 mModel->setSmartGroupId( groupId );
2474 mModel->setFavoritesOnly( false );
2475 }
2476 }
2477 else // tags
2478 {
2479 enableGroupInputs( true );
2480 int tagId = index.data( Qt::UserRole + 1 ).toInt();
2481 if ( mModel )
2482 {
2483 mModel->setTagId( tagId );
2484 mModel->setSmartGroupId( -1 );
2485 mModel->setFavoritesOnly( false );
2486 }
2487 }
2488
2489 actnEditSmartGroup->setVisible( false );
2490 actnAddTag->setVisible( false );
2491 actnAddSmartgroup->setVisible( false );
2492 actnRemoveGroup->setVisible( false );
2493 actnTagSymbols->setVisible( false );
2494 actnFinishTagging->setVisible( false );
2495
2496 if ( index.parent().isValid() )
2497 {
2498 if ( index.parent().data( Qt::UserRole + 1 ).toString() == QLatin1String( "smartgroups" ) )
2499 {
2500 actnEditSmartGroup->setVisible( !mGroupingMode && !readOnly );
2501 }
2502 else if ( index.parent().data( Qt::UserRole + 1 ).toString() == QLatin1String( "tags" ) )
2503 {
2504 actnAddTag->setVisible( !mGroupingMode && !readOnly );
2505 actnTagSymbols->setVisible( !mGroupingMode && !readOnly );
2506 actnFinishTagging->setVisible( mGroupingMode && !readOnly );
2507 }
2508 actnRemoveGroup->setVisible( !readOnly );
2509 }
2510 else if ( index.data( Qt::UserRole + 1 ) == "smartgroups" )
2511 {
2512 actnAddSmartgroup->setVisible( !mGroupingMode && !readOnly );
2513 }
2514 else if ( index.data( Qt::UserRole + 1 ) == "tags" )
2515 {
2516 actnAddTag->setVisible( !mGroupingMode && !readOnly );
2517 }
2518}
2519
2521{
2522 if ( isReadOnly() )
2523 return 0;
2524
2525 QStandardItemModel *model = qobject_cast<QStandardItemModel *>( groupTree->model() );
2526 QModelIndex index;
2527 for ( int i = 0; i < groupTree->model()->rowCount(); i++ )
2528 {
2529 index = groupTree->model()->index( i, 0 );
2530 QString data = index.data( Qt::UserRole + 1 ).toString();
2531 if ( data == QLatin1String( "tags" ) )
2532 {
2533 break;
2534 }
2535 }
2536
2537 QString itemName;
2538 int id;
2539 bool ok;
2540 itemName = QInputDialog::getText( this, tr( "Add Tag" ),
2541 tr( "Please enter name for the new tag:" ), QLineEdit::Normal, tr( "New tag" ), &ok ).trimmed();
2542 if ( !ok || itemName.isEmpty() )
2543 return 0;
2544
2545 int check = mStyle->tagId( itemName );
2546 if ( check > 0 )
2547 {
2548 mMessageBar->pushCritical( tr( "Add Tag" ), tr( "The tag “%1” already exists." ).arg( itemName ) );
2549 return 0;
2550 }
2551
2552 // block the auto-repopulation of groups when the style emits groupsModified
2553 // instead, we manually update the model items for better state retention
2554 mBlockGroupUpdates++;
2555 id = mStyle->addTag( itemName );
2556 mBlockGroupUpdates--;
2557
2558 if ( !id )
2559 {
2560 mMessageBar->pushCritical( tr( "Add Tag" ), tr( "New tag could not be created — There was a problem with the symbol database." ) );
2561 return 0;
2562 }
2563
2564 QStandardItem *parentItem = model->itemFromIndex( index );
2565 QStandardItem *childItem = new QStandardItem( itemName );
2566 childItem->setData( id );
2567 childItem->setData( itemName, GroupModelRoles::TagName );
2568 parentItem->appendRow( childItem );
2569
2570 return id;
2571}
2572
2574{
2575 if ( isReadOnly() )
2576 return 0;
2577
2578 QStandardItemModel *model = qobject_cast<QStandardItemModel *>( groupTree->model() );
2579 QModelIndex index;
2580 for ( int i = 0; i < groupTree->model()->rowCount(); i++ )
2581 {
2582 index = groupTree->model()->index( i, 0 );
2583 QString data = index.data( Qt::UserRole + 1 ).toString();
2584 if ( data == QLatin1String( "smartgroups" ) )
2585 {
2586 break;
2587 }
2588 }
2589
2590 QString itemName;
2591 int id;
2592 QgsSmartGroupEditorDialog dlg( mStyle, this );
2593 if ( dlg.exec() == QDialog::Rejected )
2594 return 0;
2595
2596 // block the auto-repopulation of groups when the style emits groupsModified
2597 // instead, we manually update the model items for better state retention
2598 mBlockGroupUpdates++;
2599 id = mStyle->addSmartgroup( dlg.smartgroupName(), dlg.conditionOperator(), dlg.conditionMap() );
2600 mBlockGroupUpdates--;
2601
2602 if ( !id )
2603 return 0;
2604 itemName = dlg.smartgroupName();
2605
2606 QStandardItem *parentItem = model->itemFromIndex( index );
2607 QStandardItem *childItem = new QStandardItem( itemName );
2608 childItem->setData( id );
2609 parentItem->appendRow( childItem );
2610
2611 return id;
2612}
2613
2615{
2616 if ( isReadOnly() )
2617 return;
2618
2619 QStandardItemModel *model = qobject_cast<QStandardItemModel *>( groupTree->model() );
2620 QModelIndex index = groupTree->currentIndex();
2621
2622 // do not allow removal of system-defined groupings
2623 QString data = index.data( Qt::UserRole + 1 ).toString();
2624 if ( data == QLatin1String( "all" ) || data == QLatin1String( "favorite" ) || data == QLatin1String( "tags" ) || index.data() == "smartgroups" )
2625 {
2626 // should never appear -- blocked by GUI
2627 int err = QMessageBox::critical( this, tr( "Remove Group" ),
2628 tr( "Invalid selection. Cannot delete system defined categories.\n"
2629 "Kindly select a group or smart group you might want to delete." ) );
2630 if ( err )
2631 return;
2632 }
2633
2634 QStandardItem *parentItem = model->itemFromIndex( index.parent() );
2635
2636 // block the auto-repopulation of groups when the style emits groupsModified
2637 // instead, we manually update the model items for better state retention
2638 mBlockGroupUpdates++;
2639
2640 if ( parentItem->data( Qt::UserRole + 1 ).toString() == QLatin1String( "smartgroups" ) )
2641 {
2642 mStyle->remove( QgsStyle::SmartgroupEntity, index.data( Qt::UserRole + 1 ).toInt() );
2643 }
2644 else
2645 {
2646 mStyle->remove( QgsStyle::TagEntity, index.data( Qt::UserRole + 1 ).toInt() );
2647 }
2648
2649 mBlockGroupUpdates--;
2650 parentItem->removeRow( index.row() );
2651}
2652
2653void QgsStyleManagerDialog::groupRenamed( QStandardItem *item )
2654{
2655 if ( isReadOnly() )
2656 return;
2657
2658 QgsDebugMsg( QStringLiteral( "Symbol group edited: data=%1 text=%2" ).arg( item->data( Qt::UserRole + 1 ).toString(), item->text() ) );
2659 int id = item->data( Qt::UserRole + 1 ).toInt();
2660 QString name = item->text();
2661 mBlockGroupUpdates++;
2662 if ( item->parent()->data( Qt::UserRole + 1 ) == "smartgroups" )
2663 {
2664 mStyle->rename( QgsStyle::SmartgroupEntity, id, name );
2665 }
2666 else
2667 {
2668 mStyle->rename( QgsStyle::TagEntity, id, name );
2669 }
2670 mBlockGroupUpdates--;
2671}
2672
2674{
2675 if ( isReadOnly() )
2676 return;
2677
2678 QStandardItemModel *treeModel = qobject_cast<QStandardItemModel *>( groupTree->model() );
2679
2680 if ( mGroupingMode )
2681 {
2682 mGroupingMode = false;
2683 mModel->setCheckable( false );
2684 actnTagSymbols->setVisible( true );
2685 actnFinishTagging->setVisible( false );
2686 // disconnect slot which handles regrouping
2687
2688 // disable all items except groups in groupTree
2690 groupChanged( groupTree->currentIndex() );
2691
2692 // Finally: Reconnect all Symbol editing functionalities
2693 connect( treeModel, &QStandardItemModel::itemChanged,
2695
2696 // Reset the selection mode
2697 listItems->setSelectionMode( QAbstractItemView::ExtendedSelection );
2698 mSymbolTreeView->setSelectionMode( QAbstractItemView::ExtendedSelection );
2699 }
2700 else
2701 {
2702 bool validGroup = false;
2703 // determine whether it is a valid group
2704 QModelIndex present = groupTree->currentIndex();
2705 while ( present.parent().isValid() )
2706 {
2707 if ( present.parent().data() == "Tags" )
2708 {
2709 validGroup = true;
2710 break;
2711 }
2712 present = present.parent();
2713 }
2714 if ( !validGroup )
2715 return;
2716
2717 mGroupingMode = true;
2718 // Change visibility of actions
2719 actnTagSymbols->setVisible( false );
2720 actnFinishTagging->setVisible( true );
2721 // Remove all Symbol editing functionalities
2722 disconnect( treeModel, &QStandardItemModel::itemChanged,
2724
2725 // disable all items except groups in groupTree
2727 groupChanged( groupTree->currentIndex() );
2728 btnManageGroups->setEnabled( true );
2729
2730 mModel->setCheckable( true );
2731
2732 // No selection should be possible
2733 listItems->setSelectionMode( QAbstractItemView::NoSelection );
2734 mSymbolTreeView->setSelectionMode( QAbstractItemView::NoSelection );
2735 }
2736}
2737
2739{
2740}
2741
2743{
2744}
2745
2746void QgsStyleManagerDialog::filterSymbols( const QString &qword )
2747{
2748 mModel->setFilterString( qword );
2749}
2750
2751void QgsStyleManagerDialog::symbolSelected( const QModelIndex &index )
2752{
2753 actnEditItem->setEnabled( index.isValid() && !mGroupingMode && !isReadOnly() );
2754}
2755
2756void QgsStyleManagerDialog::selectedSymbolsChanged( const QItemSelection &selected, const QItemSelection &deselected )
2757{
2758 Q_UNUSED( selected )
2759 Q_UNUSED( deselected )
2760 const bool nothingSelected = listItems->selectionModel()->selectedIndexes().empty();
2761 const bool readOnly = isReadOnly();
2762 actnRemoveItem->setDisabled( nothingSelected || readOnly );
2763 actnAddFavorite->setDisabled( nothingSelected || readOnly );
2764 actnRemoveFavorite->setDisabled( nothingSelected || readOnly );
2765 mGroupListMenu->setDisabled( nothingSelected || readOnly );
2766 actnDetag->setDisabled( nothingSelected || readOnly );
2767 actnExportAsPNG->setDisabled( nothingSelected );
2768 actnExportAsSVG->setDisabled( nothingSelected );
2769 if ( mActionCopyToDefault )
2770 mActionCopyToDefault->setDisabled( nothingSelected );
2771 mCopyToDefaultButton->setDisabled( nothingSelected );
2772 actnEditItem->setDisabled( nothingSelected || readOnly );
2773}
2774
2776{
2777 const bool readOnly = isReadOnly();
2778 groupTree->setEnabled( enable );
2779 btnAddTag->setEnabled( enable && !readOnly );
2780 btnAddSmartgroup->setEnabled( enable && !readOnly );
2781 actnAddTag->setEnabled( enable && !readOnly );
2782 actnAddSmartgroup->setEnabled( enable && !readOnly );
2783 actnRemoveGroup->setEnabled( enable && !readOnly );
2784 btnManageGroups->setEnabled( !readOnly && ( enable || mGroupingMode ) ); // always enabled in grouping mode, as it is the only way to leave grouping mode
2785 searchBox->setEnabled( enable );
2786}
2787
2789{
2790 const bool readOnly = isReadOnly();
2791 actnRemoveGroup->setEnabled( enable && !readOnly );
2792 btnManageGroups->setEnabled( !readOnly && ( enable || mGroupingMode ) ); // always enabled in grouping mode, as it is the only way to leave grouping mode
2793}
2794
2796{
2797 QStandardItemModel *treeModel = qobject_cast<QStandardItemModel *>( groupTree->model() );
2798 for ( int i = 0; i < treeModel->rowCount(); i++ )
2799 {
2800 treeModel->item( i )->setEnabled( enable );
2801
2802 if ( treeModel->item( i )->data() == "smartgroups" )
2803 {
2804 for ( int j = 0; j < treeModel->item( i )->rowCount(); j++ )
2805 {
2806 treeModel->item( i )->child( j )->setEnabled( enable );
2807 }
2808 }
2809 }
2810
2811 // The buttons
2812 // NOTE: if you ever change the layout name in the .ui file edit here too
2813 for ( int i = 0; i < symbolBtnsLayout->count(); i++ )
2814 {
2815 QWidget *w = symbolBtnsLayout->itemAt( i )->widget();
2816 if ( w )
2817 w->setEnabled( enable );
2818 }
2819
2820 // The actions
2821 actnRemoveItem->setEnabled( enable );
2822 actnEditItem->setEnabled( enable );
2823 mActionCopyItem->setEnabled( enable );
2824 mActionPasteItem->setEnabled( enable );
2825}
2826
2828{
2829 QPoint globalPos = groupTree->viewport()->mapToGlobal( point );
2830
2831 QModelIndex index = groupTree->indexAt( point );
2832 if ( index.isValid() && !mGroupingMode )
2833 mGroupTreeContextMenu->popup( globalPos );
2834}
2835
2837{
2838 QPoint globalPos = mSymbolViewStackedWidget->currentIndex() == 0
2839 ? listItems->viewport()->mapToGlobal( point )
2840 : mSymbolTreeView->viewport()->mapToGlobal( point );
2841
2842 // Clear all actions and create new actions for every group
2843 mGroupListMenu->clear();
2844
2845 const QModelIndexList indices = listItems->selectionModel()->selectedRows();
2846
2847 if ( !isReadOnly() )
2848 {
2849 const QStringList currentTags = indices.count() == 1 ? indices.at( 0 ).data( QgsStyleModel::TagRole ).toStringList() : QStringList();
2850 QAction *a = nullptr;
2851 QStringList tags = mStyle->tags();
2852 tags.sort();
2853 for ( const QString &tag : std::as_const( tags ) )
2854 {
2855 a = new QAction( tag, mGroupListMenu );
2856 a->setData( tag );
2857 if ( indices.count() == 1 )
2858 {
2859 a->setCheckable( true );
2860 a->setChecked( currentTags.contains( tag ) );
2861 }
2862 connect( a, &QAction::triggered, this, [ = ]( bool ) { tagSelectedSymbols(); }
2863 );
2864 mGroupListMenu->addAction( a );
2865 }
2866
2867 if ( tags.count() > 0 )
2868 {
2869 mGroupListMenu->addSeparator();
2870 }
2871 a = new QAction( tr( "Create New Tag…" ), mGroupListMenu );
2872 connect( a, &QAction::triggered, this, [ = ]( bool ) { tagSelectedSymbols( true ); }
2873 );
2874 mGroupListMenu->addAction( a );
2875 }
2876
2877 const QList< ItemDetails > items = selectedItems();
2878 mActionCopyItem->setEnabled( !items.isEmpty() && ( items.at( 0 ).entityType != QgsStyle::ColorrampEntity ) );
2879
2880 bool enablePaste = false;
2881 std::unique_ptr< QgsSymbol > tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
2882 if ( tempSymbol )
2883 enablePaste = true;
2884 else
2885 {
2886 ( void )QgsTextFormat::fromMimeData( QApplication::clipboard()->mimeData(), &enablePaste );
2887 }
2888 mActionPasteItem->setEnabled( enablePaste );
2889
2890 mGroupMenu->popup( globalPos );
2891}
2892
2894{
2895 if ( isReadOnly() )
2896 return;
2897
2898 const QList< ItemDetails > items = selectedItems();
2899 for ( const ItemDetails &details : items )
2900 {
2901 mStyle->addFavorite( details.entityType, details.name );
2902 }
2903}
2904
2906{
2907 if ( isReadOnly() )
2908 return;
2909
2910 const QList< ItemDetails > items = selectedItems();
2911 for ( const ItemDetails &details : items )
2912 {
2913 mStyle->removeFavorite( details.entityType, details.name );
2914 }
2915}
2916
2918{
2919 QAction *selectedItem = qobject_cast<QAction *>( sender() );
2920 if ( selectedItem )
2921 {
2922 const QList< ItemDetails > items = selectedItems();
2923 QString tag;
2924 if ( newTag )
2925 {
2926 int id = addTag();
2927 if ( id == 0 )
2928 {
2929 return;
2930 }
2931
2932 tag = mStyle->tag( id );
2933 }
2934 else
2935 {
2936 tag = selectedItem->data().toString();
2937 }
2938
2939 for ( const ItemDetails &details : items )
2940 {
2941 mStyle->tagSymbol( details.entityType, details.name, QStringList( tag ) );
2942 }
2943 }
2944}
2945
2947{
2948 if ( isReadOnly() )
2949 return;
2950
2951 QAction *selectedItem = qobject_cast<QAction *>( sender() );
2952
2953 if ( selectedItem )
2954 {
2955 const QList< ItemDetails > items = selectedItems();
2956 for ( const ItemDetails &details : items )
2957 {
2958 mStyle->detagSymbol( details.entityType, details.name );
2959 }
2960 }
2961}
2962
2964{
2965 if ( isReadOnly() )
2966 return;
2967
2968 QStandardItemModel *treeModel = qobject_cast<QStandardItemModel *>( groupTree->model() );
2969
2970 // determine whether it is a valid group
2971 QModelIndex present = groupTree->currentIndex();
2972 if ( present.parent().data( Qt::UserRole + 1 ) != "smartgroups" )
2973 {
2974 // should never appear - blocked by GUI logic
2975 QMessageBox::critical( this, tr( "Edit Smart Group" ),
2976 tr( "You have not selected a Smart Group. Kindly select a Smart Group to edit." ) );
2977 return;
2978 }
2979 QStandardItem *item = treeModel->itemFromIndex( present );
2980
2981 QgsSmartGroupEditorDialog dlg( mStyle, this );
2982 QgsSmartConditionMap map = mStyle->smartgroup( present.data( Qt::UserRole + 1 ).toInt() );
2983 dlg.setSmartgroupName( item->text() );
2984 dlg.setOperator( mStyle->smartgroupOperator( item->data().toInt() ) );
2985 dlg.setConditionMap( map );
2986
2987 if ( dlg.exec() == QDialog::Rejected )
2988 return;
2989
2990 mBlockGroupUpdates++;
2991 mStyle->remove( QgsStyle::SmartgroupEntity, item->data().toInt() );
2992 int id = mStyle->addSmartgroup( dlg.smartgroupName(), dlg.conditionOperator(), dlg.conditionMap() );
2993 mBlockGroupUpdates--;
2994 if ( !id )
2995 {
2996 mMessageBar->pushCritical( tr( "Edit Smart Group" ), tr( "There was an error while editing the smart group." ) );
2997 return;
2998 }
2999 item->setText( dlg.smartgroupName() );
3000 item->setData( id );
3001
3002 groupChanged( present );
3003}
3004
3006{
3007 reject();
3008}
3009
3011{
3012 QgsHelp::openHelp( QStringLiteral( "style_library/style_manager.html" ) );
3013}
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition: qgis.h:228
SymbolType
Symbol types.
Definition: qgis.h:320
@ Marker
Marker symbol.
@ Line
Line symbol.
@ Fill
Fill symbol.
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:3278
A dialog for configuring a 3D symbol.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsStyleModel * defaultStyleModel()
Returns a shared QgsStyleModel containing the default style library (see QgsStyle::defaultStyle()).
static Qgs3DSymbolRegistry * symbol3DRegistry()
Returns registry of available 3D symbols.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
A dialog which allows users to modify the properties of a QgsColorBrewerColorRamp.
QDialogButtonBox * buttonBox() const
Returns a reference to the dialog's button box.
Color ramp utilising "Color Brewer" preset color schemes.
QgsColorBrewerColorRamp * clone() const override
Creates a clone of the color ramp.
static QString typeString()
Returns the string identifier for QgsColorBrewerColorRamp.
QString schemeName() const
Returns the name of the color brewer color scheme.
int colors() const
Returns the number of colors in the ramp.
Abstract base class for color ramps.
Definition: qgscolorramp.h:30
static QList< QPair< QString, QString > > rampTypes()
Returns a list of available ramp types, where the first value in each item is the QgsColorRamp::type(...
A dialog which allows users to modify the properties of a QgsCptCityColorRamp.
bool saveAsGradientRamp() const
Returns true if the ramp should be converted to a QgsGradientColorRamp.
QDialogButtonBox * buttonBox() const
Returns a reference to the dialog's button box.
QgsCptCityColorRamp * clone() const override
Creates a clone of the color ramp.
static QString typeString()
Returns the string identifier for QgsCptCityColorRamp.
QgsGradientColorRamp * cloneGradientRamp() const
QString schemeName() const
QString variantName() const
static QString ensureFileNameHasExtension(const QString &fileName, const QStringList &extensions)
Ensures that a fileName ends with an extension from the provided list of extensions.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgsfillsymbol.h:30
A dialog which allows users to modify the properties of a QgsGradientColorRamp.
QDialogButtonBox * buttonBox() const
Returns a reference to the dialog's button box.
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
static QString typeString()
Returns the string identifier for QgsGradientColorRamp.
QgsGradientColorRamp * clone() const override
Creates a clone of the color ramp.
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition: qgsgui.cpp:178
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:38
static QIcon iconLine()
Returns an icon representing line geometries.
static QIcon iconPolygon()
Returns an icon representing polygon geometries.
static QIcon iconPoint()
Returns an icon representing point geometries.
A dialog for configuring a custom legend patch shape.
Represents a patch shape for use in map legends.
bool isNull() const
Returns true if the patch shape is a null QgsLegendPatchShape, which indicates that the default legen...
A dialog which allows users to modify the properties of a QgsLimitedRandomColorRamp.
QDialogButtonBox * buttonBox() const
Returns a reference to the dialog's button box.
Constrained random color ramp, which returns random colors based on preset parameters.
static QString typeString()
Returns the string identifier for QgsLimitedRandomColorRamp.
QgsLimitedRandomColorRamp * clone() const override
Creates a clone of the color ramp.
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgslinesymbol.h:30
A marker symbol type, for rendering Point and MultiPoint geometries.
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:61
Contains settings for how a map layer will be labeled.
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:477
const QgsProjectStyleSettings * styleSettings() const
Returns the project's style settings, which contains settings and properties relating to how a QgsPro...
bool setValue(const T &value, const QString &dynamicKeyPart=QString()) const
Set settings value.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
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:93
Temporarily sets a cursor override for the QApplication for the lifetime of the object.
Definition: qgsguiutils.h:221
A simple dialog for customizing text formatting settings.
Container for all settings relating to text rendering.
Definition: qgstextformat.h:41
static QgsTextFormat fromMimeData(const QMimeData *data, bool *ok=nullptr)
Attempts to parse the provided mime data as a QgsTextFormat.
QMultiMap< QString, QString > QgsSmartConditionMap
A multimap to hold the smart group conditions as constraint and parameter pairs.
Definition: qgsstyle.h:79
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
@ TagName
Tag name.
Definition: qgsstyle.h:100
QMap< int, QString > QgsSymbolGroupMap
Definition: qgsstyle.h:42