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