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