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