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