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