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