QGIS API Documentation  3.2.0-Bonn (bc43194)
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 "qgsstyle.h"
20 #include "qgssymbol.h"
21 #include "qgssymbollayerutils.h"
22 #include "qgscolorramp.h"
23 
32 #include "qgssettings.h"
33 
34 #include <QAction>
35 #include <QFile>
36 #include <QFileDialog>
37 #include <QInputDialog>
38 #include <QMessageBox>
39 #include <QPushButton>
40 #include <QStandardItemModel>
41 #include <QMenu>
42 
43 #include "qgsapplication.h"
44 #include "qgslogger.h"
45 
47  : QDialog( parent )
48  , mStyle( style )
49  , mModified( false )
50 {
51  setupUi( this );
52  connect( tabItemType, &QTabWidget::currentChanged, this, &QgsStyleManagerDialog::tabItemType_currentChanged );
53  connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsStyleManagerDialog::showHelp );
54  connect( buttonBox, &QDialogButtonBox::rejected, this, &QgsStyleManagerDialog::onClose );
55 
56 #ifdef Q_OS_MAC
57  setWindowModality( Qt::WindowModal );
58 #endif
59 
60  QgsSettings settings;
61 
62  restoreGeometry( settings.value( QStringLiteral( "Windows/StyleV2Manager/geometry" ) ).toByteArray() );
63  mSplitter->setSizes( QList<int>() << 170 << 540 );
64  mSplitter->restoreState( settings.value( QStringLiteral( "Windows/StyleV2Manager/splitter" ) ).toByteArray() );
65 
66  tabItemType->setDocumentMode( true );
67  searchBox->setShowSearchIcon( true );
68  searchBox->setPlaceholderText( tr( "Filter symbols…" ) );
69 
70  connect( this, &QDialog::finished, this, &QgsStyleManagerDialog::onFinished );
71 
72  connect( listItems, &QAbstractItemView::doubleClicked, this, &QgsStyleManagerDialog::editItem );
73 
74  connect( btnAddItem, &QPushButton::clicked, this, [ = ]( bool ) { addItem(); }
75  );
76  connect( btnEditItem, &QPushButton::clicked, this, [ = ]( bool ) { editItem(); }
77  );
78  connect( actnEditItem, &QAction::triggered, this, [ = ]( bool ) { editItem(); }
79  );
80  connect( btnRemoveItem, &QPushButton::clicked, this, [ = ]( bool ) { removeItem(); }
81  );
82  connect( actnRemoveItem, &QAction::triggered, this, [ = ]( bool ) { removeItem(); }
83  );
84 
85  QMenu *shareMenu = new QMenu( tr( "Share Menu" ), this );
86  QAction *exportAction = new QAction( tr( "Export Symbol(s)…" ), this );
87  exportAction->setIcon( QIcon( QgsApplication::iconPath( "mActionFileSave.svg" ) ) );
88  shareMenu->addAction( exportAction );
89  QAction *importAction = new QAction( tr( "Import Symbol(s)…" ), this );
90  importAction->setIcon( QIcon( QgsApplication::iconPath( "mActionFileOpen.svg" ) ) );
91  shareMenu->addAction( importAction );
92  shareMenu->addSeparator();
93  shareMenu->addAction( actnExportAsPNG );
94  shareMenu->addAction( actnExportAsSVG );
95  connect( actnExportAsPNG, &QAction::triggered, this, &QgsStyleManagerDialog::exportItemsPNG );
96  connect( actnExportAsSVG, &QAction::triggered, this, &QgsStyleManagerDialog::exportItemsSVG );
97  connect( exportAction, &QAction::triggered, this, &QgsStyleManagerDialog::exportItems );
98  connect( importAction, &QAction::triggered, this, &QgsStyleManagerDialog::importItems );
99  btnShare->setMenu( shareMenu );
100 
101  // Set editing mode off by default
102  mGrouppingMode = false;
103 
104  QStandardItemModel *model = new QStandardItemModel( listItems );
105  listItems->setModel( model );
106  listItems->setSelectionMode( QAbstractItemView::ExtendedSelection );
107 
108  connect( model, &QStandardItemModel::itemChanged, this, &QgsStyleManagerDialog::itemChanged );
109  connect( listItems->selectionModel(), &QItemSelectionModel::currentChanged,
111  connect( listItems->selectionModel(), &QItemSelectionModel::selectionChanged,
113 
114  populateTypes();
115 
116  QStandardItemModel *groupModel = new QStandardItemModel( groupTree );
117  groupTree->setModel( groupModel );
118  groupTree->setHeaderHidden( true );
119  populateGroups();
120  groupTree->setCurrentIndex( groupTree->model()->index( 0, 0 ) );
121 
122  connect( groupTree->selectionModel(), &QItemSelectionModel::currentChanged,
124  connect( groupModel, &QStandardItemModel::itemChanged,
126 
127  QMenu *groupMenu = new QMenu( tr( "Group Actions" ), this );
128  connect( actnTagSymbols, &QAction::triggered, this, &QgsStyleManagerDialog::tagSymbolsAction );
129  groupMenu->addAction( actnTagSymbols );
130  connect( actnFinishTagging, &QAction::triggered, this, &QgsStyleManagerDialog::tagSymbolsAction );
131  actnFinishTagging->setVisible( false );
132  groupMenu->addAction( actnFinishTagging );
133  groupMenu->addAction( actnEditSmartGroup );
134  btnManageGroups->setMenu( groupMenu );
135 
136  connect( searchBox, &QLineEdit::textChanged, this, &QgsStyleManagerDialog::filterSymbols );
137 
138  // Context menu for groupTree
139  groupTree->setContextMenuPolicy( Qt::CustomContextMenu );
140  connect( groupTree, &QWidget::customContextMenuRequested,
142 
143  // Context menu for listItems
144  listItems->setContextMenuPolicy( Qt::CustomContextMenu );
145  connect( listItems, &QWidget::customContextMenuRequested,
147 
148  // Menu for the "Add item" toolbutton when in colorramp mode
149  QStringList rampTypes;
150  rampTypes << tr( "Gradient" ) << tr( "Color presets" ) << tr( "Random" ) << tr( "Catalog: cpt-city" );
151  rampTypes << tr( "Catalog: ColorBrewer" );
152  mMenuBtnAddItemColorRamp = new QMenu( this );
153  Q_FOREACH ( const QString &rampType, rampTypes )
154  mMenuBtnAddItemColorRamp->addAction( new QAction( rampType, this ) );
155  connect( mMenuBtnAddItemColorRamp, &QMenu::triggered,
156  this, static_cast<bool ( QgsStyleManagerDialog::* )( QAction * )>( &QgsStyleManagerDialog::addColorRamp ) );
157 
158  // Context menu for symbols/colorramps. The menu entries for every group are created when displaying the menu.
159  mGroupMenu = new QMenu( this );
160  connect( actnAddFavorite, &QAction::triggered, this, &QgsStyleManagerDialog::addFavoriteSelectedSymbols );
161  mGroupMenu->addAction( actnAddFavorite );
162  connect( actnRemoveFavorite, &QAction::triggered, this, &QgsStyleManagerDialog::removeFavoriteSelectedSymbols );
163  mGroupMenu->addAction( actnRemoveFavorite );
164  mGroupMenu->addSeparator()->setParent( this );
165  mGroupListMenu = new QMenu( mGroupMenu );
166  mGroupListMenu->setTitle( tr( "Add to Tag" ) );
167  mGroupListMenu->setEnabled( false );
168  mGroupMenu->addMenu( mGroupListMenu );
169  actnDetag->setData( 0 );
170  connect( actnDetag, &QAction::triggered, this, &QgsStyleManagerDialog::detagSelectedSymbols );
171  mGroupMenu->addAction( actnDetag );
172  mGroupMenu->addSeparator()->setParent( this );
173  mGroupMenu->addAction( actnRemoveItem );
174  mGroupMenu->addAction( actnEditItem );
175  mGroupMenu->addSeparator()->setParent( this );
176  mGroupMenu->addAction( actnExportAsPNG );
177  mGroupMenu->addAction( actnExportAsSVG );
178 
179  // Context menu for the group tree
180  mGroupTreeContextMenu = new QMenu( this );
181  connect( actnEditSmartGroup, &QAction::triggered, this, &QgsStyleManagerDialog::editSmartgroupAction );
182  mGroupTreeContextMenu->addAction( actnEditSmartGroup );
183  connect( actnAddTag, &QAction::triggered, this, [ = ]( bool ) { addTag(); }
184  );
185  mGroupTreeContextMenu->addAction( actnAddTag );
186  connect( actnAddSmartgroup, &QAction::triggered, this, [ = ]( bool ) { addSmartgroup(); }
187  );
188  mGroupTreeContextMenu->addAction( actnAddSmartgroup );
189  connect( actnRemoveGroup, &QAction::triggered, this, &QgsStyleManagerDialog::removeGroup );
190  mGroupTreeContextMenu->addAction( actnRemoveGroup );
191 
192  tabItemType_currentChanged( 0 );
193 }
194 
196 {
197  if ( mModified )
198  {
199  mStyle->save();
200  }
201 
202  QgsSettings settings;
203  settings.setValue( QStringLiteral( "Windows/StyleV2Manager/geometry" ), saveGeometry() );
204  settings.setValue( QStringLiteral( "Windows/StyleV2Manager/splitter" ), mSplitter->saveState() );
205 }
206 
208 {
209 #if 0
210  // save current selection index in types combo
211  int current = ( tabItemType->count() > 0 ? tabItemType->currentIndex() : 0 );
212 
213 // no counting of style items
214  int markerCount = 0, lineCount = 0, fillCount = 0;
215 
216  QStringList symbolNames = mStyle->symbolNames();
217  for ( int i = 0; i < symbolNames.count(); ++i )
218  {
219  switch ( mStyle->symbolRef( symbolNames[i] )->type() )
220  {
221  case QgsSymbol::Marker:
222  markerCount++;
223  break;
224  case QgsSymbol::Line:
225  lineCount++;
226  break;
227  case QgsSymbol::Fill:
228  fillCount++;
229  break;
230  default:
231  Q_ASSERT( 0 && "unknown symbol type" );
232  break;
233  }
234  }
235 
236  cboItemType->clear();
237  cboItemType->addItem( tr( "Marker symbol (%1)" ).arg( markerCount ), QVariant( QgsSymbol::Marker ) );
238  cboItemType->addItem( tr( "Line symbol (%1)" ).arg( lineCount ), QVariant( QgsSymbol::Line ) );
239  cboItemType->addItem( tr( "Fill symbol (%1)" ).arg( fillCount ), QVariant( QgsSymbol::Fill ) );
240 
241  cboItemType->addItem( tr( "Color ramp (%1)" ).arg( mStyle->colorRampCount() ), QVariant( 3 ) );
242 
243  // update current index to previous selection
244  cboItemType->setCurrentIndex( current );
245 #endif
246 }
247 
248 void QgsStyleManagerDialog::tabItemType_currentChanged( int )
249 {
250  // when in Color Ramp tab, add menu to add item button and hide "Export symbols as PNG/SVG"
251  bool flag = currentItemType() != 3;
252  searchBox->setPlaceholderText( flag ? tr( "Filter symbols…" ) : tr( "Filter color ramps…" ) );
253  btnAddItem->setMenu( flag ? nullptr : mMenuBtnAddItemColorRamp );
254  actnExportAsPNG->setVisible( flag );
255  actnExportAsSVG->setVisible( flag );
256 
257  listItems->setIconSize( QSize( 100, 90 ) );
258  listItems->setGridSize( QSize( 120, 110 ) );
259 
260  populateList();
261 }
262 
264 {
265  if ( currentItemType() > 3 )
266  {
267  Q_ASSERT( false && "not implemented" );
268  return;
269  }
270  groupChanged( groupTree->selectionModel()->currentIndex() );
271 }
272 
273 void QgsStyleManagerDialog::populateSymbols( const QStringList &symbolNames, bool check )
274 {
275  QStandardItemModel *model = qobject_cast<QStandardItemModel *>( listItems->model() );
276  model->clear();
277 
278  int type = currentItemType();
279  for ( int i = 0; i < symbolNames.count(); ++i )
280  {
281  QString name = symbolNames[i];
282  QgsSymbol *symbol = mStyle->symbol( name );
283  if ( symbol && symbol->type() == type )
284  {
285  QStringList tags = mStyle->tagsOfSymbol( QgsStyle::SymbolEntity, name );
286  QStandardItem *item = new QStandardItem( name );
287  QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( symbol, listItems->iconSize(), 18 );
288  item->setIcon( icon );
289  item->setData( name ); // used to find out original name when user edited the name
290  item->setCheckable( check );
291  item->setToolTip( QStringLiteral( "<b>%1</b><br><i>%2</i>" ).arg( name, tags.count() > 0 ? tags.join( QStringLiteral( ", " ) ) : tr( "Not tagged" ) ) );
292  // add to model
293  model->appendRow( item );
294  }
295  delete symbol;
296  }
297  selectedSymbolsChanged( QItemSelection(), QItemSelection() );
298  symbolSelected( listItems->currentIndex() );
299 }
300 
301 
302 void QgsStyleManagerDialog::populateColorRamps( const QStringList &colorRamps, bool check )
303 {
304  QStandardItemModel *model = qobject_cast<QStandardItemModel *>( listItems->model() );
305  model->clear();
306 
307  for ( int i = 0; i < colorRamps.count(); ++i )
308  {
309  QString name = colorRamps[i];
310  std::unique_ptr< QgsColorRamp > ramp( mStyle->colorRamp( name ) );
311 
312  QStandardItem *item = new QStandardItem( name );
313  QIcon icon = QgsSymbolLayerUtils::colorRampPreviewIcon( ramp.get(), listItems->iconSize(), 18 );
314  item->setIcon( icon );
315  item->setData( name ); // used to find out original name when user edited the name
316  item->setCheckable( check );
317  item->setToolTip( name );
318  model->appendRow( item );
319  }
320  selectedSymbolsChanged( QItemSelection(), QItemSelection() );
321  symbolSelected( listItems->currentIndex() );
322 }
323 
325 {
326  switch ( tabItemType->currentIndex() )
327  {
328  case 0:
329  return QgsSymbol::Marker;
330  case 1:
331  return QgsSymbol::Line;
332  case 2:
333  return QgsSymbol::Fill;
334  case 3:
335  return 3;
336  default:
337  return 0;
338  }
339 }
340 
342 {
343  QModelIndex index = listItems->selectionModel()->currentIndex();
344  if ( !index.isValid() )
345  return QString();
346  return index.model()->data( index, 0 ).toString();
347 }
348 
350 {
351  bool changed = false;
352  if ( currentItemType() < 3 )
353  {
354  changed = addSymbol();
355  }
356  else if ( currentItemType() == 3 )
357  {
358  changed = addColorRamp();
359  }
360  else
361  {
362  Q_ASSERT( false && "not implemented" );
363  }
364 
365  if ( changed )
366  {
367  populateList();
368  populateTypes();
369  }
370 }
371 
373 {
374  // create new symbol with current type
375  QgsSymbol *symbol = nullptr;
376  QString name = tr( "new symbol" );
377  switch ( currentItemType() )
378  {
379  case QgsSymbol::Marker:
380  symbol = new QgsMarkerSymbol();
381  name = tr( "new marker" );
382  break;
383  case QgsSymbol::Line:
384  symbol = new QgsLineSymbol();
385  name = tr( "new line" );
386  break;
387  case QgsSymbol::Fill:
388  symbol = new QgsFillSymbol();
389  name = tr( "new fill symbol" );
390  break;
391  default:
392  Q_ASSERT( false && "unknown symbol type" );
393  return false;
394  }
395 
396  // get symbol design
397  // NOTE : Set the parent widget as "this" to notify the Symbol selector
398  // that, it is being called by Style Manager, so recursive calling
399  // of style manager and symbol selector can be arrested
400  // See also: editSymbol()
401  QgsSymbolSelectorDialog dlg( symbol, mStyle, nullptr, this );
402  if ( dlg.exec() == 0 )
403  {
404  delete symbol;
405  return false;
406  }
407 
408  QgsStyleSaveDialog saveDlg( this );
409  if ( !saveDlg.exec() )
410  {
411  delete symbol;
412  return false;
413  }
414 
415  name = saveDlg.name();
416 
417  // request valid/unique name
418  bool nameInvalid = true;
419  while ( nameInvalid )
420  {
421  // validate name
422  if ( name.isEmpty() )
423  {
424  QMessageBox::warning( this, tr( "Save Symbol" ),
425  tr( "Cannot save symbol without name. Enter a name." ) );
426  }
427  else if ( mStyle->symbolNames().contains( name ) )
428  {
429  int res = QMessageBox::warning( this, tr( "Save Symbol" ),
430  tr( "Symbol with name '%1' already exists. Overwrite?" )
431  .arg( name ),
432  QMessageBox::Yes | QMessageBox::No );
433  if ( res == QMessageBox::Yes )
434  {
435  mStyle->removeSymbol( name );
436  nameInvalid = false;
437  }
438  }
439  else
440  {
441  // valid name
442  nameInvalid = false;
443  }
444  if ( nameInvalid )
445  {
446  bool ok;
447  name = QInputDialog::getText( this, tr( "Symbol Name" ),
448  tr( "Please enter a name for new symbol:" ),
449  QLineEdit::Normal, name, &ok );
450  if ( !ok )
451  {
452  delete symbol;
453  return false;
454  }
455  }
456  }
457 
458  QStringList symbolTags = saveDlg.tags().split( ',' );
459 
460  // add new symbol to style and re-populate the list
461  mStyle->addSymbol( name, symbol );
462  mStyle->saveSymbol( name, symbol, saveDlg.isFavorite(), symbolTags );
463 
464  mModified = true;
465  return true;
466 }
467 
468 
469 QString QgsStyleManagerDialog::addColorRampStatic( QWidget *parent, QgsStyle *style, QString rampType )
470 {
471  // let the user choose the color ramp type if rampType is not given
472  bool ok = true;
473  if ( rampType.isEmpty() )
474  {
475  QStringList rampTypes;
476  rampTypes << tr( "Gradient" ) << tr( "Color presets" ) << tr( "Random" ) << tr( "Catalog: cpt-city" );
477  rampTypes << tr( "Catalog: ColorBrewer" );
478  rampType = QInputDialog::getItem( parent, tr( "Color Ramp Type" ),
479  tr( "Please select color ramp type:" ), rampTypes, 0, false, &ok );
480  }
481  if ( !ok || rampType.isEmpty() )
482  return QString();
483 
484  QString name = tr( "new ramp" );
485 
486  std::unique_ptr< QgsColorRamp > ramp;
487  if ( rampType == tr( "Gradient" ) )
488  {
490  if ( !dlg.exec() )
491  {
492  return QString();
493  }
494  ramp.reset( dlg.ramp().clone() );
495  name = tr( "new gradient ramp" );
496  }
497  else if ( rampType == tr( "Random" ) )
498  {
500  if ( !dlg.exec() )
501  {
502  return QString();
503  }
504  ramp.reset( dlg.ramp().clone() );
505  name = tr( "new random ramp" );
506  }
507  else if ( rampType == tr( "Catalog: ColorBrewer" ) )
508  {
510  if ( !dlg.exec() )
511  {
512  return QString();
513  }
514  ramp.reset( dlg.ramp().clone() );
515  name = dlg.ramp().schemeName() + QString::number( dlg.ramp().colors() );
516  }
517  else if ( rampType == tr( "Color presets" ) )
518  {
520  if ( !dlg.exec() )
521  {
522  return QString();
523  }
524  ramp.reset( dlg.ramp().clone() );
525  name = tr( "new preset ramp" );
526  }
527  else if ( rampType == tr( "Catalog: cpt-city" ) )
528  {
529  QgsCptCityColorRampDialog dlg( QgsCptCityColorRamp( QLatin1String( "" ), QLatin1String( "" ) ), parent );
530  if ( !dlg.exec() )
531  {
532  return QString();
533  }
534  // name = dlg.selectedName();
535  name = QFileInfo( dlg.ramp().schemeName() ).baseName() + dlg.ramp().variantName();
536  if ( dlg.saveAsGradientRamp() )
537  {
538  ramp.reset( dlg.ramp().cloneGradientRamp() );
539  }
540  else
541  {
542  ramp.reset( dlg.ramp().clone() );
543  }
544  }
545  else
546  {
547  // Q_ASSERT( 0 && "invalid ramp type" );
548  // bailing out is rather harsh!
549  QgsDebugMsg( "invalid ramp type " + rampType );
550  return QString();
551  }
552 
554  if ( !saveDlg.exec() )
555  {
556  return QString();
557  }
558 
559  name = saveDlg.name();
560 
561  // get valid/unique name
562  bool nameInvalid = true;
563  while ( nameInvalid )
564  {
565  // validate name
566  if ( name.isEmpty() )
567  {
568  QMessageBox::warning( parent, tr( "Save Color Ramp" ),
569  tr( "Cannot save color ramp without name. Enter a name." ) );
570  }
571  else if ( style->colorRampNames().contains( name ) )
572  {
573  int res = QMessageBox::warning( parent, tr( "Save Color Ramp" ),
574  tr( "Color ramp with name '%1' already exists. Overwrite?" )
575  .arg( name ),
576  QMessageBox::Yes | QMessageBox::No );
577  if ( res == QMessageBox::Yes )
578  {
579  nameInvalid = false;
580  }
581  }
582  else
583  {
584  // valid name
585  nameInvalid = false;
586  }
587  if ( nameInvalid )
588  {
589  bool ok;
590  name = QInputDialog::getText( parent, tr( "Color Ramp Name" ),
591  tr( "Please enter a name for new color ramp:" ),
592  QLineEdit::Normal, name, &ok );
593  if ( !ok )
594  {
595  return QString();
596  }
597  }
598  }
599 
600  QStringList colorRampTags = saveDlg.tags().split( ',' );
601  QgsColorRamp *r = ramp.release();
602 
603  // add new symbol to style and re-populate the list
604  style->addColorRamp( name, r );
605  style->saveColorRamp( name, r, saveDlg.isFavorite(), colorRampTags );
606 
607  return name;
608 }
609 
610 
612 {
613  return addColorRamp( nullptr );
614 }
615 
616 bool QgsStyleManagerDialog::addColorRamp( QAction *action )
617 {
618  // pass the action text, which is the color ramp type
619  QString rampName = addColorRampStatic( this, mStyle,
620  action ? action->text() : QString() );
621  if ( !rampName.isEmpty() )
622  {
623  mModified = true;
624  populateList();
625  return true;
626  }
627 
628  return false;
629 }
630 
632 {
633  bool changed = false;
634  if ( currentItemType() < 3 )
635  {
636  changed = editSymbol();
637  }
638  else if ( currentItemType() == 3 )
639  {
640  changed = editColorRamp();
641  }
642  else
643  {
644  Q_ASSERT( false && "not implemented" );
645  }
646 
647  if ( changed )
648  populateList();
649 }
650 
652 {
653  QString symbolName = currentItemName();
654  if ( symbolName.isEmpty() )
655  return false;
656 
657  QgsSymbol *symbol = mStyle->symbol( symbolName );
658 
659  // let the user edit the symbol and update list when done
660  QgsSymbolSelectorDialog dlg( symbol, mStyle, nullptr, this );
661  if ( dlg.exec() == 0 )
662  {
663  delete symbol;
664  return false;
665  }
666 
667  // by adding symbol to style with the same name the old effectively gets overwritten
668  mStyle->addSymbol( symbolName, symbol, true );
669  mModified = true;
670  return true;
671 }
672 
674 {
675  QString name = currentItemName();
676  if ( name.isEmpty() )
677  return false;
678 
679  std::unique_ptr< QgsColorRamp > ramp( mStyle->colorRamp( name ) );
680 
681  if ( ramp->type() == QLatin1String( "gradient" ) )
682  {
683  QgsGradientColorRamp *gradRamp = static_cast<QgsGradientColorRamp *>( ramp.get() );
684  QgsGradientColorRampDialog dlg( *gradRamp, this );
685  if ( !dlg.exec() )
686  {
687  return false;
688  }
689  ramp.reset( dlg.ramp().clone() );
690  }
691  else if ( ramp->type() == QLatin1String( "random" ) )
692  {
693  QgsLimitedRandomColorRamp *randRamp = static_cast<QgsLimitedRandomColorRamp *>( ramp.get() );
694  QgsLimitedRandomColorRampDialog dlg( *randRamp, this );
695  if ( !dlg.exec() )
696  {
697  return false;
698  }
699  ramp.reset( dlg.ramp().clone() );
700  }
701  else if ( ramp->type() == QLatin1String( "colorbrewer" ) )
702  {
703  QgsColorBrewerColorRamp *brewerRamp = static_cast<QgsColorBrewerColorRamp *>( ramp.get() );
704  QgsColorBrewerColorRampDialog dlg( *brewerRamp, this );
705  if ( !dlg.exec() )
706  {
707  return false;
708  }
709  ramp.reset( dlg.ramp().clone() );
710  }
711  else if ( ramp->type() == QLatin1String( "preset" ) )
712  {
713  QgsPresetSchemeColorRamp *presetRamp = static_cast<QgsPresetSchemeColorRamp *>( ramp.get() );
714  QgsPresetColorRampDialog dlg( *presetRamp, this );
715  if ( !dlg.exec() )
716  {
717  return false;
718  }
719  ramp.reset( dlg.ramp().clone() );
720  }
721  else if ( ramp->type() == QLatin1String( "cpt-city" ) )
722  {
723  QgsCptCityColorRamp *cptCityRamp = static_cast<QgsCptCityColorRamp *>( ramp.get() );
724  QgsCptCityColorRampDialog dlg( *cptCityRamp, this );
725  if ( !dlg.exec() )
726  {
727  return false;
728  }
729  if ( dlg.saveAsGradientRamp() )
730  {
731  ramp.reset( dlg.ramp().cloneGradientRamp() );
732  }
733  else
734  {
735  ramp.reset( dlg.ramp().clone() );
736  }
737  }
738  else
739  {
740  Q_ASSERT( false && "invalid ramp type" );
741  }
742 
743  mStyle->addColorRamp( name, ramp.release(), true );
744  mModified = true;
745  return true;
746 }
747 
748 
750 {
751  bool changed = false;
752  if ( currentItemType() < 3 )
753  {
754  changed = removeSymbol();
755  }
756  else if ( currentItemType() == 3 )
757  {
758  changed = removeColorRamp();
759  }
760  else
761  {
762  Q_ASSERT( false && "not implemented" );
763  }
764 
765  if ( changed )
766  {
767  populateList();
768  populateTypes();
769  }
770 }
771 
773 {
774  QModelIndexList indexes = listItems->selectionModel()->selectedIndexes();
775  if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Remove Symbol" ),
776  QString( tr( "Do you really want to remove %n symbol(s)?", nullptr, indexes.count() ) ),
777  QMessageBox::Yes,
778  QMessageBox::No ) )
779  return false;
780 
781  QgsTemporaryCursorOverride override( Qt::WaitCursor );
782 
783  Q_FOREACH ( const QModelIndex &index, indexes )
784  {
785  QString symbolName = index.data().toString();
786  // delete from style and update list
787  if ( !symbolName.isEmpty() )
788  mStyle->removeSymbol( symbolName );
789  }
790  mModified = true;
791  return true;
792 }
793 
795 {
796  QModelIndexList indexes = listItems->selectionModel()->selectedIndexes();
797  if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Remove Color Ramp" ),
798  QString( tr( "Do you really want to remove %n ramp(s)?", nullptr, indexes.count() ) ),
799  QMessageBox::Yes,
800  QMessageBox::No ) )
801  return false;
802 
803  QgsTemporaryCursorOverride override( Qt::WaitCursor );
804 
805  Q_FOREACH ( const QModelIndex &index, indexes )
806  {
807  QString rampName = index.data().toString();
808  // delete from style and update list
809  if ( !rampName.isEmpty() )
810  mStyle->removeColorRamp( rampName );
811  }
812  mModified = true;
813  return true;
814 }
815 
816 void QgsStyleManagerDialog::itemChanged( QStandardItem *item )
817 {
818  // an item has been edited
819  QString oldName = item->data().toString();
820 
821  bool changed = false;
822  if ( currentItemType() < 3 )
823  {
824  changed = mStyle->renameSymbol( oldName, item->text() );
825  }
826  else if ( currentItemType() == 3 )
827  {
828  changed = mStyle->renameColorRamp( oldName, item->text() );
829  }
830 
831  if ( changed )
832  {
833  populateList();
834  mModified = true;
835  }
836  else
837  {
838  QMessageBox::critical( this, tr( "Save Item" ),
839  tr( "Name is already taken by another item. Choose a different name." ) );
840  item->setText( oldName );
841  }
842 }
843 
845 {
846  QString dir = QFileDialog::getExistingDirectory( this, tr( "Export Selected Symbols as PNG" ),
847  QDir::home().absolutePath(),
848  QFileDialog::ShowDirsOnly
849  | QFileDialog::DontResolveSymlinks );
850  exportSelectedItemsImages( dir, QStringLiteral( "png" ), QSize( 32, 32 ) );
851 }
852 
854 {
855  QString dir = QFileDialog::getExistingDirectory( this, tr( "Export Selected Symbols as SVG" ),
856  QDir::home().absolutePath(),
857  QFileDialog::ShowDirsOnly
858  | QFileDialog::DontResolveSymlinks );
859  exportSelectedItemsImages( dir, QStringLiteral( "svg" ), QSize( 32, 32 ) );
860 }
861 
862 
863 void QgsStyleManagerDialog::exportSelectedItemsImages( const QString &dir, const QString &format, QSize size )
864 {
865  if ( dir.isEmpty() )
866  return;
867 
868  QModelIndexList indexes = listItems->selectionModel()->selection().indexes();
869  Q_FOREACH ( const QModelIndex &index, indexes )
870  {
871  QString name = index.data().toString();
872  QString path = dir + '/' + name + '.' + format;
873  QgsSymbol *sym = mStyle->symbol( name );
874  sym->exportImage( path, format, size );
875  }
876 }
877 
879 {
881  dlg.exec();
882 }
883 
885 {
887  dlg.exec();
888  populateList();
889  populateGroups();
890 }
891 
892 void QgsStyleManagerDialog::setBold( QStandardItem *item )
893 {
894  QFont font = item->font();
895  font.setBold( true );
896  item->setFont( font );
897 }
898 
900 {
901  QStandardItemModel *model = qobject_cast<QStandardItemModel *>( groupTree->model() );
902  model->clear();
903 
904  QStandardItem *favoriteSymbols = new QStandardItem( tr( "Favorites" ) );
905  favoriteSymbols->setData( "favorite" );
906  favoriteSymbols->setEditable( false );
907  setBold( favoriteSymbols );
908  model->appendRow( favoriteSymbols );
909 
910  QStandardItem *allSymbols = new QStandardItem( tr( "All Symbols" ) );
911  allSymbols->setData( "all" );
912  allSymbols->setEditable( false );
913  setBold( allSymbols );
914  model->appendRow( allSymbols );
915 
916  QStandardItem *taggroup = new QStandardItem( QLatin1String( "" ) ); //require empty name to get first order groups
917  taggroup->setData( "tags" );
918  taggroup->setEditable( false );
919  QStringList tags = mStyle->tags();
920  tags.sort();
921  Q_FOREACH ( const QString &tag, tags )
922  {
923  QStandardItem *item = new QStandardItem( tag );
924  item->setData( mStyle->tagId( tag ) );
925  taggroup->appendRow( item );
926  }
927  taggroup->setText( tr( "Tags" ) );//set title later
928  setBold( taggroup );
929  model->appendRow( taggroup );
930 
931  QStandardItem *smart = new QStandardItem( tr( "Smart Groups" ) );
932  smart->setData( "smartgroups" );
933  smart->setEditable( false );
934  setBold( smart );
936  QgsSymbolGroupMap::const_iterator i = sgMap.constBegin();
937  while ( i != sgMap.constEnd() )
938  {
939  QStandardItem *item = new QStandardItem( i.value() );
940  item->setData( i.key() );
941  smart->appendRow( item );
942  ++i;
943  }
944  model->appendRow( smart );
945 
946  // expand things in the group tree
947  int rows = model->rowCount( model->indexFromItem( model->invisibleRootItem() ) );
948  for ( int i = 0; i < rows; i++ )
949  {
950  groupTree->setExpanded( model->indexFromItem( model->item( i ) ), true );
951  }
952 }
953 
954 void QgsStyleManagerDialog::groupChanged( const QModelIndex &index )
955 {
956  QStringList symbolNames;
957  QStringList groupSymbols;
958 
960  if ( currentItemType() > 3 )
961  {
962  QgsDebugMsg( "Entity not implemented" );
963  return;
964  }
965 
966  QString category = index.data( Qt::UserRole + 1 ).toString();
967  if ( category == QLatin1String( "all" ) || category == QLatin1String( "tags" ) || category == QLatin1String( "smartgroups" ) )
968  {
969  enableGroupInputs( false );
970  if ( category == QLatin1String( "tags" ) )
971  {
972  actnAddTag->setEnabled( true );
973  actnAddSmartgroup->setEnabled( false );
974  }
975  else if ( category == QLatin1String( "smartgroups" ) )
976  {
977  actnAddTag->setEnabled( false );
978  actnAddSmartgroup->setEnabled( true );
979  }
980  symbolNames = currentItemType() < 3 ? mStyle->symbolNames() : mStyle->colorRampNames();
981  }
982  else if ( category == QLatin1String( "favorite" ) )
983  {
984  enableGroupInputs( false );
985  symbolNames = mStyle->symbolsOfFavorite( type );
986  }
987  else if ( index.parent().data( Qt::UserRole + 1 ) == "smartgroups" )
988  {
989  actnRemoveGroup->setEnabled( true );
990  btnManageGroups->setEnabled( true );
991  int groupId = index.data( Qt::UserRole + 1 ).toInt();
992  symbolNames = mStyle->symbolsOfSmartgroup( type, groupId );
993  }
994  else // tags
995  {
996  enableGroupInputs( true );
997  int tagId = index.data( Qt::UserRole + 1 ).toInt();
998  symbolNames = mStyle->symbolsWithTag( type, tagId );
999  if ( mGrouppingMode && tagId )
1000  {
1001  groupSymbols = symbolNames;
1002  symbolNames = type == QgsStyle::SymbolEntity ? mStyle->symbolNames() : mStyle->colorRampNames();
1003  }
1004  }
1005 
1006  symbolNames.sort();
1007  if ( currentItemType() < 3 )
1008  {
1009  populateSymbols( symbolNames, mGrouppingMode );
1010  }
1011  else if ( currentItemType() == 3 )
1012  {
1013  populateColorRamps( symbolNames, mGrouppingMode );
1014  }
1015 
1016  if ( mGrouppingMode )
1017  {
1018  setSymbolsChecked( groupSymbols );
1019  }
1020 
1021  actnEditSmartGroup->setVisible( false );
1022  actnAddTag->setVisible( false );
1023  actnAddSmartgroup->setVisible( false );
1024  actnRemoveGroup->setVisible( false );
1025  actnTagSymbols->setVisible( false );
1026  actnFinishTagging->setVisible( false );
1027 
1028  if ( index.parent().isValid() )
1029  {
1030  if ( index.parent().data( Qt::UserRole + 1 ).toString() == QLatin1String( "smartgroups" ) )
1031  {
1032  actnEditSmartGroup->setVisible( !mGrouppingMode );
1033  }
1034  else if ( index.parent().data( Qt::UserRole + 1 ).toString() == QLatin1String( "tags" ) )
1035  {
1036  actnAddTag->setVisible( !mGrouppingMode );
1037  actnTagSymbols->setVisible( !mGrouppingMode );
1038  actnFinishTagging->setVisible( mGrouppingMode );
1039  }
1040  actnRemoveGroup->setVisible( true );
1041  }
1042  else if ( index.data( Qt::UserRole + 1 ) == "smartgroups" )
1043  {
1044  actnAddSmartgroup->setVisible( !mGrouppingMode );
1045  }
1046  else if ( index.data( Qt::UserRole + 1 ) == "tags" )
1047  {
1048  actnAddTag->setVisible( !mGrouppingMode );
1049  }
1050 }
1051 
1053 {
1054  QStandardItemModel *model = qobject_cast<QStandardItemModel *>( groupTree->model() );
1055  QModelIndex index;
1056  for ( int i = 0; i < groupTree->model()->rowCount(); i++ )
1057  {
1058  index = groupTree->model()->index( i, 0 );
1059  QString data = index.data( Qt::UserRole + 1 ).toString();
1060  if ( data == QLatin1String( "tags" ) )
1061  {
1062  break;
1063  }
1064  }
1065 
1066  QString itemName;
1067  int id;
1068  bool ok;
1069  itemName = QInputDialog::getText( this, tr( "Add Tag" ),
1070  tr( "Please enter name for the new tag:" ), QLineEdit::Normal, tr( "New tag" ), &ok ).trimmed();
1071  if ( !ok || itemName.isEmpty() )
1072  return 0;
1073 
1074  int check = mStyle->tagId( itemName );
1075  if ( check > 0 )
1076  {
1077  QMessageBox::critical( this, tr( "Add Tag" ),
1078  tr( "Tag name already exists in your symbol database." ) );
1079  return 0;
1080  }
1081  id = mStyle->addTag( itemName );
1082  if ( !id )
1083  {
1084  QMessageBox::critical( this, tr( "Add Tag" ),
1085  tr( "New tag could not be created.\n"
1086  "There was a problem with your symbol database." ) );
1087  return 0;
1088  }
1089 
1090  QStandardItem *parentItem = model->itemFromIndex( index );
1091  QStandardItem *childItem = new QStandardItem( itemName );
1092  childItem->setData( id );
1093  parentItem->appendRow( childItem );
1094 
1095  return id;
1096 }
1097 
1099 {
1100  QStandardItemModel *model = qobject_cast<QStandardItemModel *>( groupTree->model() );
1101  QModelIndex index;
1102  for ( int i = 0; i < groupTree->model()->rowCount(); i++ )
1103  {
1104  index = groupTree->model()->index( i, 0 );
1105  QString data = index.data( Qt::UserRole + 1 ).toString();
1106  if ( data == QLatin1String( "smartgroups" ) )
1107  {
1108  break;
1109  }
1110  }
1111 
1112  QString itemName;
1113  int id;
1114  QgsSmartGroupEditorDialog dlg( mStyle, this );
1115  if ( dlg.exec() == QDialog::Rejected )
1116  return 0;
1117  id = mStyle->addSmartgroup( dlg.smartgroupName(), dlg.conditionOperator(), dlg.conditionMap() );
1118  if ( !id )
1119  return 0;
1120  itemName = dlg.smartgroupName();
1121 
1122  QStandardItem *parentItem = model->itemFromIndex( index );
1123  QStandardItem *childItem = new QStandardItem( itemName );
1124  childItem->setData( id );
1125  parentItem->appendRow( childItem );
1126 
1127  return id;
1128 }
1129 
1131 {
1132  QStandardItemModel *model = qobject_cast<QStandardItemModel *>( groupTree->model() );
1133  QModelIndex index = groupTree->currentIndex();
1134 
1135  // do not allow removal of system-defined groupings
1136  QString data = index.data( Qt::UserRole + 1 ).toString();
1137  if ( data == QLatin1String( "all" ) || data == QLatin1String( "favorite" ) || data == QLatin1String( "tags" ) || index.data() == "smartgroups" )
1138  {
1139  int err = QMessageBox::critical( this, tr( "Remove Group" ),
1140  tr( "Invalid selection. Cannot delete system defined categories.\n"
1141  "Kindly select a group or smart group you might want to delete." ) );
1142  if ( err )
1143  return;
1144  }
1145 
1146  QStandardItem *parentItem = model->itemFromIndex( index.parent() );
1147  if ( parentItem->data( Qt::UserRole + 1 ).toString() == QLatin1String( "smartgroups" ) )
1148  {
1149  mStyle->remove( QgsStyle::SmartgroupEntity, index.data( Qt::UserRole + 1 ).toInt() );
1150  }
1151  else
1152  {
1153  mStyle->remove( QgsStyle::TagEntity, index.data( Qt::UserRole + 1 ).toInt() );
1154  }
1155  parentItem->removeRow( index.row() );
1156 }
1157 
1158 void QgsStyleManagerDialog::groupRenamed( QStandardItem *item )
1159 {
1160  QgsDebugMsg( "Symbol group edited: data=" + item->data( Qt::UserRole + 1 ).toString() + " text=" + item->text() );
1161  int id = item->data( Qt::UserRole + 1 ).toInt();
1162  QString name = item->text();
1163  if ( item->parent()->data( Qt::UserRole + 1 ) == "smartgroups" )
1164  {
1166  }
1167  else
1168  {
1169  mStyle->rename( QgsStyle::TagEntity, id, name );
1170  }
1171 }
1172 
1174 {
1175 
1176  QStandardItemModel *treeModel = qobject_cast<QStandardItemModel *>( groupTree->model() );
1177  QStandardItemModel *model = qobject_cast<QStandardItemModel *>( listItems->model() );
1178 
1179  if ( mGrouppingMode )
1180  {
1181  mGrouppingMode = false;
1182  actnTagSymbols->setVisible( true );
1183  actnFinishTagging->setVisible( false );
1184  // disconnect slot which handles regrouping
1185  disconnect( model, &QStandardItemModel::itemChanged,
1187 
1188  // disabel all items except groups in groupTree
1190  groupChanged( groupTree->currentIndex() );
1191 
1192  // Finally: Reconnect all Symbol editing functionalities
1193  connect( treeModel, &QStandardItemModel::itemChanged,
1195  connect( model, &QStandardItemModel::itemChanged,
1197  // Reset the selection mode
1198  listItems->setSelectionMode( QAbstractItemView::ExtendedSelection );
1199  }
1200  else
1201  {
1202  bool validGroup = false;
1203  // determine whether it is a valid group
1204  QModelIndex present = groupTree->currentIndex();
1205  while ( present.parent().isValid() )
1206  {
1207  if ( present.parent().data() == "Tags" )
1208  {
1209  validGroup = true;
1210  break;
1211  }
1212  present = present.parent();
1213  }
1214  if ( !validGroup )
1215  return;
1216 
1217  mGrouppingMode = true;
1218  // Change visibility of actions
1219  actnTagSymbols->setVisible( false );
1220  actnFinishTagging->setVisible( true );
1221  // Remove all Symbol editing functionalities
1222  disconnect( treeModel, &QStandardItemModel::itemChanged,
1224  disconnect( model, &QStandardItemModel::itemChanged,
1226 
1227  // disabel all items except groups in groupTree
1228  enableItemsForGroupingMode( false );
1229  groupChanged( groupTree->currentIndex() );
1230  btnManageGroups->setEnabled( true );
1231 
1232 
1233  // Connect to slot which handles regrouping
1234  connect( model, &QStandardItemModel::itemChanged,
1236 
1237  // No selection should be possible
1238  listItems->setSelectionMode( QAbstractItemView::NoSelection );
1239  }
1240 }
1241 
1242 void QgsStyleManagerDialog::regrouped( QStandardItem *item )
1243 {
1245  if ( currentItemType() > 3 )
1246  {
1247  QgsDebugMsg( "Unknown style entity" );
1248  return;
1249  }
1250 
1251  QStandardItemModel *treeModel = qobject_cast<QStandardItemModel *>( groupTree->model() );
1252  QString tag = treeModel->itemFromIndex( groupTree->currentIndex() )->text();
1253 
1254  QString symbolName = item->text();
1255  bool regrouped;
1256  if ( item->checkState() == Qt::Checked )
1257  regrouped = mStyle->tagSymbol( type, symbolName, QStringList( tag ) );
1258  else
1259  regrouped = mStyle->detagSymbol( type, symbolName, QStringList( tag ) );
1260  if ( !regrouped )
1261  {
1262  int er = QMessageBox::critical( this, tr( "Group Items" ),
1263  tr( "There was a problem with the symbols database while regrouping." ) );
1264  // call the slot again to get back to normal
1265  if ( er )
1266  tagSymbolsAction();
1267  }
1268 }
1269 
1270 void QgsStyleManagerDialog::setSymbolsChecked( const QStringList &symbols )
1271 {
1272  QStandardItemModel *model = qobject_cast<QStandardItemModel *>( listItems->model() );
1273  Q_FOREACH ( const QString &symbol, symbols )
1274  {
1275  QList<QStandardItem *> items = model->findItems( symbol );
1276  Q_FOREACH ( QStandardItem *item, items )
1277  item->setCheckState( Qt::Checked );
1278  }
1279 }
1280 
1281 void QgsStyleManagerDialog::filterSymbols( const QString &qword )
1282 {
1283  QStringList items;
1285  items.sort();
1286  if ( currentItemType() == 3 )
1287  {
1288  populateColorRamps( items );
1289  }
1290  else
1291  {
1292  populateSymbols( items );
1293  }
1294 }
1295 
1296 void QgsStyleManagerDialog::symbolSelected( const QModelIndex &index )
1297 {
1298  actnEditItem->setEnabled( index.isValid() && !mGrouppingMode );
1299 }
1300 
1301 void QgsStyleManagerDialog::selectedSymbolsChanged( const QItemSelection &selected, const QItemSelection &deselected )
1302 {
1303  Q_UNUSED( selected );
1304  Q_UNUSED( deselected );
1305  bool nothingSelected = listItems->selectionModel()->selectedIndexes().empty();
1306  actnRemoveItem->setDisabled( nothingSelected );
1307  actnAddFavorite->setDisabled( nothingSelected );
1308  actnRemoveFavorite->setDisabled( nothingSelected );
1309  mGroupListMenu->setDisabled( nothingSelected );
1310  actnDetag->setDisabled( nothingSelected );
1311  actnExportAsPNG->setDisabled( nothingSelected );
1312  actnExportAsSVG->setDisabled( nothingSelected );
1313  actnEditItem->setDisabled( nothingSelected );
1314 }
1315 
1317 {
1318  groupTree->setEnabled( enable );
1319  btnAddTag->setEnabled( enable );
1320  btnAddSmartgroup->setEnabled( enable );
1321  actnAddTag->setEnabled( enable );
1322  actnAddSmartgroup->setEnabled( enable );
1323  actnRemoveGroup->setEnabled( enable );
1324  btnManageGroups->setEnabled( enable || mGrouppingMode ); // always enabled in grouping mode, as it is the only way to leave grouping mode
1325  searchBox->setEnabled( enable );
1326 }
1327 
1329 {
1330  actnRemoveGroup->setEnabled( enable );
1331  btnManageGroups->setEnabled( enable || mGrouppingMode ); // always enabled in grouping mode, as it is the only way to leave grouping mode
1332 }
1333 
1335 {
1336  QStandardItemModel *treeModel = qobject_cast<QStandardItemModel *>( groupTree->model() );
1337  for ( int i = 0; i < treeModel->rowCount(); i++ )
1338  {
1339  treeModel->item( i )->setEnabled( enable );
1340 
1341  if ( treeModel->item( i )->data() == "smartgroups" )
1342  {
1343  for ( int j = 0; j < treeModel->item( i )->rowCount(); j++ )
1344  {
1345  treeModel->item( i )->child( j )->setEnabled( enable );
1346  }
1347  }
1348  }
1349 
1350  // The buttons
1351  // NOTE: if you ever change the layout name in the .ui file edit here too
1352  for ( int i = 0; i < symbolBtnsLayout->count(); i++ )
1353  {
1354  QWidget *w = qobject_cast<QWidget *>( symbolBtnsLayout->itemAt( i )->widget() );
1355  if ( w )
1356  w->setEnabled( enable );
1357  }
1358 
1359  // The actions
1360  actnRemoveItem->setEnabled( enable );
1361  actnEditItem->setEnabled( enable );
1362 }
1363 
1365 {
1366  QPoint globalPos = groupTree->viewport()->mapToGlobal( point );
1367 
1368  QModelIndex index = groupTree->indexAt( point );
1369  QgsDebugMsg( "Now you clicked: " + index.data().toString() );
1370 
1371  if ( index.isValid() && !mGrouppingMode )
1372  mGroupTreeContextMenu->popup( globalPos );
1373 }
1374 
1376 {
1377  QPoint globalPos = listItems->viewport()->mapToGlobal( point );
1378 
1379  // Clear all actions and create new actions for every group
1380  mGroupListMenu->clear();
1381 
1382  QAction *a = nullptr;
1383  QStringList tags = mStyle->tags();
1384  tags.sort();
1385  Q_FOREACH ( const QString &tag, tags )
1386  {
1387  a = new QAction( tag, mGroupListMenu );
1388  a->setData( tag );
1389  connect( a, &QAction::triggered, this, [ = ]( bool ) { tagSelectedSymbols(); }
1390  );
1391  mGroupListMenu->addAction( a );
1392  }
1393 
1394  if ( tags.count() > 0 )
1395  {
1396  mGroupListMenu->addSeparator();
1397  }
1398  a = new QAction( tr( "Create New Tag…" ), mGroupListMenu );
1399  connect( a, &QAction::triggered, this, [ = ]( bool ) { tagSelectedSymbols( true ); }
1400  );
1401  mGroupListMenu->addAction( a );
1402 
1403  mGroupMenu->popup( globalPos );
1404 }
1405 
1407 {
1409  if ( currentItemType() > 3 )
1410  {
1411  QgsDebugMsg( "unknown entity type" );
1412  return;
1413  }
1414 
1415  QModelIndexList indexes = listItems->selectionModel()->selectedIndexes();
1416  Q_FOREACH ( const QModelIndex &index, indexes )
1417  {
1418  mStyle->addFavorite( type, index.data().toString() );
1419  }
1420  populateList();
1421 }
1422 
1424 {
1426  if ( currentItemType() > 3 )
1427  {
1428  QgsDebugMsg( "unknown entity type" );
1429  return;
1430  }
1431 
1432  QModelIndexList indexes = listItems->selectionModel()->selectedIndexes();
1433  Q_FOREACH ( const QModelIndex &index, indexes )
1434  {
1435  mStyle->removeFavorite( type, index.data().toString() );
1436  }
1437  populateList();
1438 }
1439 
1441 {
1442  QAction *selectedItem = qobject_cast<QAction *>( sender() );
1443  if ( selectedItem )
1444  {
1446  if ( currentItemType() > 3 )
1447  {
1448  QgsDebugMsg( "unknown entity type" );
1449  return;
1450  }
1451 
1452  QString tag;
1453  if ( newTag )
1454  {
1455  int id = addTag();
1456  if ( id == 0 )
1457  {
1458  return;
1459  }
1460 
1461  tag = mStyle->tag( id );
1462  }
1463  else
1464  {
1465  tag = selectedItem->data().toString();
1466  }
1467 
1468  QModelIndexList indexes = listItems->selectionModel()->selectedIndexes();
1469  Q_FOREACH ( const QModelIndex &index, indexes )
1470  {
1471  mStyle->tagSymbol( type, index.data().toString(), QStringList( tag ) );
1472  }
1473  populateList();
1474 
1475  QgsDebugMsg( "Selected Action: " + selectedItem->text() );
1476  }
1477 }
1478 
1480 {
1481  QAction *selectedItem = qobject_cast<QAction *>( sender() );
1482 
1483  if ( selectedItem )
1484  {
1486  if ( currentItemType() > 3 )
1487  {
1488  QgsDebugMsg( "unknown entity type" );
1489  return;
1490  }
1491  QModelIndexList indexes = listItems->selectionModel()->selectedIndexes();
1492  Q_FOREACH ( const QModelIndex &index, indexes )
1493  {
1494  mStyle->detagSymbol( type, index.data().toString() );
1495  }
1496  populateList();
1497 
1498  QgsDebugMsg( "Selected Action: " + selectedItem->text() );
1499  }
1500 }
1501 
1503 {
1504  QStandardItemModel *treeModel = qobject_cast<QStandardItemModel *>( groupTree->model() );
1505 
1506  // determine whether it is a valid group
1507  QModelIndex present = groupTree->currentIndex();
1508  if ( present.parent().data( Qt::UserRole + 1 ) != "smartgroups" )
1509  {
1510  QMessageBox::critical( this, tr( "Edit Smart Group" ),
1511  tr( "You have not selected a Smart Group. Kindly select a Smart Group to edit." ) );
1512  return;
1513  }
1514  QStandardItem *item = treeModel->itemFromIndex( present );
1515 
1516  QgsSmartGroupEditorDialog dlg( mStyle, this );
1517  QgsSmartConditionMap map = mStyle->smartgroup( present.data( Qt::UserRole + 1 ).toInt() );
1518  dlg.setSmartgroupName( item->text() );
1519  dlg.setOperator( mStyle->smartgroupOperator( item->data().toInt() ) );
1520  dlg.setConditionMap( map );
1521 
1522  if ( dlg.exec() == QDialog::Rejected )
1523  return;
1524 
1525  mStyle->remove( QgsStyle::SmartgroupEntity, item->data().toInt() );
1526  int id = mStyle->addSmartgroup( dlg.smartgroupName(), dlg.conditionOperator(), dlg.conditionMap() );
1527  if ( !id )
1528  {
1529  QMessageBox::critical( this, tr( "Edit Smart Group" ),
1530  tr( "There was some error while editing the smart group." ) );
1531  return;
1532  }
1533  item->setText( dlg.smartgroupName() );
1534  item->setData( id );
1535 
1536  groupChanged( present );
1537 }
1538 
1540 {
1541  reject();
1542 }
1543 
1545 {
1546  QgsHelp::openHelp( QStringLiteral( "working_with_vector/style_library.html#the-style-manager" ) );
1547 }
void groupChanged(const QModelIndex &)
void tagSymbolsAction()
carry out symbol tagging using check boxes
QMenu * mGroupMenu
Context menu for the symbols/colorramps.
QString tags() const
returns the text value of the tags element
void populateTypes()
populate combo box with known style items (symbols, color ramps)
Temporarily sets a cursor override for the QApplication for the lifetime of the object.
Definition: qgsguiutils.h:175
bool addColorRamp(const QString &name, QgsColorRamp *colorRamp, bool update=false)
Adds a color ramp to the style.
Definition: qgsstyle.cpp:191
void setBold(QStandardItem *)
sets the text of the item with bold font
bool save(QString filename=QString())
Saves style into a file (will use current filename if empty string is passed)
Definition: qgsstyle.cpp:419
bool mGrouppingMode
Mode to display the symbol list.
bool addSymbol()
add a new symbol to style
a dialog for setting properties of a newly saved style.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
QStringList symbolsWithTag(StyleEntity type, int tagid) const
Returns the symbol names with which have the given tag.
Definition: qgsstyle.cpp:561
QgsColorBrewerColorRamp * clone() const override
Creates a clone of the color ramp.
void populateList()
adds symbols of some type to list
void onFinished()
called when the dialog is going to be closed
QgsSmartConditionMap conditionMap()
returns the condition map
int addSmartgroup()
add a smartgroup
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setSymbolsChecked(const QStringList &)
to set symbols checked when in editing mode
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
void selectedSymbolsChanged(const QItemSelection &selected, const QItemSelection &deselected)
Perform tasks when the selected symbols change.
void remove(StyleEntity type, int id)
Removes the specified entity from the db.
Definition: qgsstyle.cpp:687
QStringList tagsOfSymbol(StyleEntity type, const QString &symbol)
Returns the tags associated with the symbol.
Definition: qgsstyle.cpp:991
QString name() const
returns the text value of the name element
Abstract base class for color ramps.
Definition: qgscolorramp.h:31
QMap< int, QString > QgsSymbolGroupMap
Definition: qgsstyle.h:38
QString schemeName() const
Definition: qgscolorramp.h:666
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
bool saveColorRamp(const QString &name, QgsColorRamp *ramp, bool favorite, const QStringList &tags)
Adds the colorramp to the DB.
Definition: qgsstyle.cpp:215
void populateGroups()
populate the groups
bool tagSymbol(StyleEntity type, const QString &symbol, const QStringList &tags)
Tags the symbol with the tags in the list.
Definition: qgsstyle.cpp:851
void filterSymbols(const QString &)
filter the symbols based on input search term
bool addColorRamp()
add a new color ramp to style
Line symbol.
Definition: qgssymbol.h:86
A dialog which allows users to modify the properties of a QgsColorBrewerColorRamp.
void populateSymbols(const QStringList &symbolNames, bool checkable=false)
populate list view with symbols of the current type with the given names
QgsColorRamp * colorRamp(const QString &name) const
Returns a new copy of the specified color ramp.
Definition: qgsstyle.cpp:262
QString smartgroupName()
returns the value from mNameLineEdit
bool removeColorRamp(const QString &name)
Removes color ramp from style (and delete it)
Definition: qgsstyle.cpp:244
A dialog which allows users to modify the properties of a QgsGradientColorRamp.
QgsStyleManagerDialog(QgsStyle *style, QWidget *parent SIP_TRANSFERTHIS=nullptr)
A dialog which allows users to modify the properties of a QgsPresetSchemeColorRamp.
A dialog which allows users to modify the properties of a QgsLimitedRandomColorRamp.
StyleEntity
Enum for Entities involved in a style.
Definition: qgsstyle.h:95
void saveGeometry(QWidget *widget, const QString &keyName)
Save the wigget geometry into settings.
void tagSelectedSymbols(bool newTag=false)
Tag selected symbols using menu item selection.
bool restoreGeometry(QWidget *widget, const QString &keyName)
Restore the wigget geometry from settings.
QgsGradientColorRamp * clone() const override
Creates a clone of the color ramp.
bool renameSymbol(const QString &oldName, const QString &newName)
Changessymbol&#39;s name.
Definition: qgsstyle.cpp:465
QMenu * mMenuBtnAddItemColorRamp
Menu for the "Add item" toolbutton when in colorramp mode.
void exportImage(const QString &path, const QString &format, QSize size)
export symbol as image format. PNG and SVG supported
Definition: qgssymbol.cpp:491
int colors() const
Returns the number of colors in the ramp.
Definition: qgscolorramp.h:575
Constrained random color ramp, which returns random colors based on preset parameters.
Definition: qgscolorramp.h:283
static QIcon symbolPreviewIcon(QgsSymbol *symbol, QSize size, int padding=0)
Returns an icon preview for a color ramp.
QString tag(int id) const
Returns the tag name for the given id.
Definition: qgsstyle.cpp:1058
const QgsSymbol * symbolRef(const QString &name) const
Returns a const pointer to a symbol (doesn&#39;t create new instance)
Definition: qgsstyle.cpp:175
QStringList symbolNames()
Returns a list of names of symbols.
Definition: qgsstyle.cpp:185
bool removeFavorite(StyleEntity type, const QString &name)
Removes the specified symbol from favorites.
Definition: qgsstyle.cpp:766
QgsPresetSchemeColorRamp * clone() const override
Creates a clone of the color ramp.
static QIcon colorRampPreviewIcon(QgsColorRamp *ramp, QSize size, int padding=0)
Returns an icon preview for a color ramp.
QStringList findSymbols(StyleEntity type, const QString &qword)
Returns the names of the symbols which have a matching &#39;substring&#39; in its definition.
Definition: qgsstyle.cpp:787
void enableItemsForGroupingMode(bool)
Enables or disables the groupTree items for grouping mode.
void editSmartgroupAction()
edit the selected smart group
void exportSelectedItemsImages(const QString &dir, const QString &format, QSize size)
int addTag(const QString &tagName)
Adds a new tag and returns the tag&#39;s id.
Definition: qgsstyle.cpp:610
void symbolSelected(const QModelIndex &)
Perform symbol specific tasks when selected.
QString smartgroupOperator(int id)
Returns the operator for the smartgroup clumsy implementation TODO create a class for smartgroups...
Definition: qgsstyle.cpp:1372
void grouptreeContextMenu(QPoint)
Context menu for the groupTree.
static QString addColorRampStatic(QWidget *parent, QgsStyle *style, QString RampType=QString())
open add color ramp dialog, return color ramp&#39;s name if the ramp has been added
void removeFavoriteSelectedSymbols()
Remove selected symbols from favorites.
A scheme based color ramp consisting of a list of predefined colors.
Definition: qgscolorramp.h:471
void rename(StyleEntity type, int id, const QString &newName)
Renames the given entity with the specified id.
Definition: qgsstyle.cpp:650
void itemChanged(QStandardItem *item)
QgsGradientColorRamp * cloneGradientRamp() const
QgsSmartConditionMap smartgroup(int id)
Returns the QgsSmartConditionMap for the given id.
Definition: qgsstyle.cpp:1332
QStringList colorRampNames()
Returns a list of names of color ramps.
Definition: qgsstyle.cpp:278
QgsLimitedRandomColorRamp * clone() const override
Creates a clone of the color ramp.
QStringList symbolsOfSmartgroup(StyleEntity type, int id)
Returns the symbols for the smartgroup.
Definition: qgsstyle.cpp:1236
void enableSymbolInputs(bool)
Enables or disbables the symbol specific inputs.
int tagId(const QString &tag)
Returns the DB id for the given tag name.
Definition: qgsstyle.cpp:1133
QString schemeName() const
Returns the name of the color brewer color scheme.
Definition: qgscolorramp.h:569
void addFavoriteSelectedSymbols()
Add selected symbols to favorites.
Marker symbol.
Definition: qgssymbol.h:85
bool addSymbol(const QString &name, QgsSymbol *symbol, bool update=false)
Adds a symbol to style and takes symbol&#39;s ownership.
Definition: qgsstyle.cpp:86
QStringList tags() const
Returns a list of all tags in the style database.
Definition: qgsstyle.cpp:630
Fill symbol.
Definition: qgssymbol.h:87
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:1143
QMenu * mGroupTreeContextMenu
Context menu for the group tree.
SymbolType type() const
Definition: qgssymbol.h:113
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
void setOperator(const QString &)
sets the operator AND/OR
QgsCptCityColorRamp * clone() const override
Creates a clone of the color ramp.
QgsPresetSchemeColorRamp ramp
void removeGroup()
remove a tag or smartgroup
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:35
void listitemsContextMenu(QPoint)
Context menu for the listItems ( symbols list )
Color ramp utilising "Color Brewer" preset color schemes.
Definition: qgscolorramp.h:535
bool isFavorite() const
returns whether the favorite element is checked
bool renameColorRamp(const QString &oldName, const QString &newName)
Changes ramp&#39;s name.
Definition: qgsstyle.cpp:497
int colorRampCount()
Returns count of color ramps.
Definition: qgsstyle.cpp:273
void setSmartgroupName(const QString &)
sets the smart group Name
void showHelp()
Open the associated help.
QgsSymbol * symbol(const QString &name)
Returns a NEW copy of symbol.
Definition: qgsstyle.cpp:169
bool saveSymbol(const QString &name, QgsSymbol *symbol, bool favorite, const QStringList &tags)
Adds the symbol to the DB with the tags.
Definition: qgsstyle.cpp:110
void onClose()
Close the dialog.
bool removeSymbol(const QString &name)
Removes symbol from style (and delete it)
Definition: qgsstyle.cpp:141
A dialog which allows users to modify the properties of a QgsCptCityColorRamp.
void groupRenamed(QStandardItem *)
bool addFavorite(StyleEntity type, const QString &name)
Adds the specified symbol to favorites.
Definition: qgsstyle.cpp:745
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Definition: qgscolorramp.h:139
void regrouped(QStandardItem *)
symbol changed from one group
void populateColorRamps(const QStringList &colorRamps, bool checkable=false)
populate list view with color ramps
QMenu * mGroupListMenu
Sub-menu of mGroupMenu, dynamically filled to show one entry for every group.
void setConditionMap(const QgsSmartConditionMap &)
sets up the GUI for the given conditionmap
QString variantName() const
Definition: qgscolorramp.h:667
bool detagSymbol(StyleEntity type, const QString &symbol, const QStringList &tags)
Detags the symbol with the given list.
Definition: qgsstyle.cpp:901
QStringList symbolsOfFavorite(StyleEntity type) const
Returns the symbol names which are flagged as favorite.
Definition: qgsstyle.cpp:525
QMultiMap< QString, QString > QgsSmartConditionMap
A multimap to hold the smart group conditions as constraint and parameter pairs.
Definition: qgsstyle.h:63
bool saveAsGradientRamp() const
Returns true if the ramp should be converted to a QgsGradientColorRamp.
QgsSymbolGroupMap smartgroupsListMap()
Returns the smart groups map with id as key and name as value.
Definition: qgsstyle.cpp:1187
void detagSelectedSymbols()
Remove all tags from selected symbols.
QString conditionOperator()
returns the AND/OR condition
void enableGroupInputs(bool)
Enables or disables the groupTree specific inputs.