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