QGIS API Documentation  3.0.2-Girona (307d082)
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  Q_FOREACH ( const QModelIndex &index, indexes )
782  {
783  QString symbolName = index.data().toString();
784  // delete from style and update list
785  if ( !symbolName.isEmpty() )
786  mStyle->removeSymbol( symbolName );
787  }
788  mModified = true;
789  return true;
790 }
791 
793 {
794  QModelIndexList indexes = listItems->selectionModel()->selectedIndexes();
795  if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Remove Color Ramp" ),
796  QString( tr( "Do you really want to remove %n ramp(s)?", nullptr, indexes.count() ) ),
797  QMessageBox::Yes,
798  QMessageBox::No ) )
799  return false;
800 
801  Q_FOREACH ( const QModelIndex &index, indexes )
802  {
803  QString rampName = index.data().toString();
804  // delete from style and update list
805  if ( !rampName.isEmpty() )
806  mStyle->removeColorRamp( rampName );
807  }
808  mModified = true;
809  return true;
810 }
811 
812 void QgsStyleManagerDialog::itemChanged( QStandardItem *item )
813 {
814  // an item has been edited
815  QString oldName = item->data().toString();
816 
817  bool changed = false;
818  if ( currentItemType() < 3 )
819  {
820  changed = mStyle->renameSymbol( oldName, item->text() );
821  }
822  else if ( currentItemType() == 3 )
823  {
824  changed = mStyle->renameColorRamp( oldName, item->text() );
825  }
826 
827  if ( changed )
828  {
829  populateList();
830  mModified = true;
831  }
832  else
833  {
834  QMessageBox::critical( this, tr( "Save Item" ),
835  tr( "Name is already taken by another item. Choose a different name." ) );
836  item->setText( oldName );
837  }
838 }
839 
841 {
842  QString dir = QFileDialog::getExistingDirectory( this, tr( "Export Selected Symbols as PNG" ),
843  QDir::home().absolutePath(),
844  QFileDialog::ShowDirsOnly
845  | QFileDialog::DontResolveSymlinks );
846  exportSelectedItemsImages( dir, QStringLiteral( "png" ), QSize( 32, 32 ) );
847 }
848 
850 {
851  QString dir = QFileDialog::getExistingDirectory( this, tr( "Export Selected Symbols as SVG" ),
852  QDir::home().absolutePath(),
853  QFileDialog::ShowDirsOnly
854  | QFileDialog::DontResolveSymlinks );
855  exportSelectedItemsImages( dir, QStringLiteral( "svg" ), QSize( 32, 32 ) );
856 }
857 
858 
859 void QgsStyleManagerDialog::exportSelectedItemsImages( const QString &dir, const QString &format, QSize size )
860 {
861  if ( dir.isEmpty() )
862  return;
863 
864  QModelIndexList indexes = listItems->selectionModel()->selection().indexes();
865  Q_FOREACH ( const QModelIndex &index, indexes )
866  {
867  QString name = index.data().toString();
868  QString path = dir + '/' + name + '.' + format;
869  QgsSymbol *sym = mStyle->symbol( name );
870  sym->exportImage( path, format, size );
871  }
872 }
873 
875 {
877  dlg.exec();
878 }
879 
881 {
883  dlg.exec();
884  populateList();
885  populateGroups();
886 }
887 
888 void QgsStyleManagerDialog::setBold( QStandardItem *item )
889 {
890  QFont font = item->font();
891  font.setBold( true );
892  item->setFont( font );
893 }
894 
896 {
897  QStandardItemModel *model = qobject_cast<QStandardItemModel *>( groupTree->model() );
898  model->clear();
899 
900  QStandardItem *favoriteSymbols = new QStandardItem( tr( "Favorites" ) );
901  favoriteSymbols->setData( "favorite" );
902  favoriteSymbols->setEditable( false );
903  setBold( favoriteSymbols );
904  model->appendRow( favoriteSymbols );
905 
906  QStandardItem *allSymbols = new QStandardItem( tr( "All Symbols" ) );
907  allSymbols->setData( "all" );
908  allSymbols->setEditable( false );
909  setBold( allSymbols );
910  model->appendRow( allSymbols );
911 
912  QStandardItem *taggroup = new QStandardItem( QLatin1String( "" ) ); //require empty name to get first order groups
913  taggroup->setData( "tags" );
914  taggroup->setEditable( false );
915  QStringList tags = mStyle->tags();
916  tags.sort();
917  Q_FOREACH ( const QString &tag, tags )
918  {
919  QStandardItem *item = new QStandardItem( tag );
920  item->setData( mStyle->tagId( tag ) );
921  taggroup->appendRow( item );
922  }
923  taggroup->setText( tr( "Tags" ) );//set title later
924  setBold( taggroup );
925  model->appendRow( taggroup );
926 
927  QStandardItem *smart = new QStandardItem( tr( "Smart Groups" ) );
928  smart->setData( "smartgroups" );
929  smart->setEditable( false );
930  setBold( smart );
932  QgsSymbolGroupMap::const_iterator i = sgMap.constBegin();
933  while ( i != sgMap.constEnd() )
934  {
935  QStandardItem *item = new QStandardItem( i.value() );
936  item->setData( i.key() );
937  smart->appendRow( item );
938  ++i;
939  }
940  model->appendRow( smart );
941 
942  // expand things in the group tree
943  int rows = model->rowCount( model->indexFromItem( model->invisibleRootItem() ) );
944  for ( int i = 0; i < rows; i++ )
945  {
946  groupTree->setExpanded( model->indexFromItem( model->item( i ) ), true );
947  }
948 }
949 
950 void QgsStyleManagerDialog::groupChanged( const QModelIndex &index )
951 {
952  QStringList symbolNames;
953  QStringList groupSymbols;
954 
956  if ( currentItemType() > 3 )
957  {
958  QgsDebugMsg( "Entity not implemented" );
959  return;
960  }
961 
962  QString category = index.data( Qt::UserRole + 1 ).toString();
963  if ( category == QLatin1String( "all" ) || category == QLatin1String( "tags" ) || category == QLatin1String( "smartgroups" ) )
964  {
965  enableGroupInputs( false );
966  if ( category == QLatin1String( "tags" ) )
967  {
968  actnAddTag->setEnabled( true );
969  actnAddSmartgroup->setEnabled( false );
970  }
971  else if ( category == QLatin1String( "smartgroups" ) )
972  {
973  actnAddTag->setEnabled( false );
974  actnAddSmartgroup->setEnabled( true );
975  }
976  symbolNames = currentItemType() < 3 ? mStyle->symbolNames() : mStyle->colorRampNames();
977  }
978  else if ( category == QLatin1String( "favorite" ) )
979  {
980  enableGroupInputs( false );
981  symbolNames = mStyle->symbolsOfFavorite( type );
982  }
983  else if ( index.parent().data( Qt::UserRole + 1 ) == "smartgroups" )
984  {
985  actnRemoveGroup->setEnabled( true );
986  btnManageGroups->setEnabled( true );
987  int groupId = index.data( Qt::UserRole + 1 ).toInt();
988  symbolNames = mStyle->symbolsOfSmartgroup( type, groupId );
989  }
990  else // tags
991  {
992  enableGroupInputs( true );
993  int tagId = index.data( Qt::UserRole + 1 ).toInt();
994  symbolNames = mStyle->symbolsWithTag( type, tagId );
995  if ( mGrouppingMode && tagId )
996  {
997  groupSymbols = symbolNames;
998  symbolNames = type == QgsStyle::SymbolEntity ? mStyle->symbolNames() : mStyle->colorRampNames();
999  }
1000  }
1001 
1002  symbolNames.sort();
1003  if ( currentItemType() < 3 )
1004  {
1005  populateSymbols( symbolNames, mGrouppingMode );
1006  }
1007  else if ( currentItemType() == 3 )
1008  {
1009  populateColorRamps( symbolNames, mGrouppingMode );
1010  }
1011 
1012  if ( mGrouppingMode )
1013  {
1014  setSymbolsChecked( groupSymbols );
1015  }
1016 
1017  actnEditSmartGroup->setVisible( false );
1018  actnAddTag->setVisible( false );
1019  actnAddSmartgroup->setVisible( false );
1020  actnRemoveGroup->setVisible( false );
1021  actnTagSymbols->setVisible( false );
1022  actnFinishTagging->setVisible( false );
1023 
1024  if ( index.parent().isValid() )
1025  {
1026  if ( index.parent().data( Qt::UserRole + 1 ).toString() == QLatin1String( "smartgroups" ) )
1027  {
1028  actnEditSmartGroup->setVisible( !mGrouppingMode );
1029  }
1030  else if ( index.parent().data( Qt::UserRole + 1 ).toString() == QLatin1String( "tags" ) )
1031  {
1032  actnAddTag->setVisible( !mGrouppingMode );
1033  actnTagSymbols->setVisible( !mGrouppingMode );
1034  actnFinishTagging->setVisible( mGrouppingMode );
1035  }
1036  actnRemoveGroup->setVisible( true );
1037  }
1038  else if ( index.data( Qt::UserRole + 1 ) == "smartgroups" )
1039  {
1040  actnAddSmartgroup->setVisible( !mGrouppingMode );
1041  }
1042  else if ( index.data( Qt::UserRole + 1 ) == "tags" )
1043  {
1044  actnAddTag->setVisible( !mGrouppingMode );
1045  }
1046 }
1047 
1049 {
1050  QStandardItemModel *model = qobject_cast<QStandardItemModel *>( groupTree->model() );
1051  QModelIndex index;
1052  for ( int i = 0; i < groupTree->model()->rowCount(); i++ )
1053  {
1054  index = groupTree->model()->index( i, 0 );
1055  QString data = index.data( Qt::UserRole + 1 ).toString();
1056  if ( data == QLatin1String( "tags" ) )
1057  {
1058  break;
1059  }
1060  }
1061 
1062  QString itemName;
1063  int id;
1064  bool ok;
1065  itemName = QInputDialog::getText( this, tr( "Add Tag" ),
1066  tr( "Please enter name for the new tag:" ), QLineEdit::Normal, tr( "New tag" ), &ok ).trimmed();
1067  if ( !ok || itemName.isEmpty() )
1068  return 0;
1069 
1070  int check = mStyle->tagId( itemName );
1071  if ( check > 0 )
1072  {
1073  QMessageBox::critical( this, tr( "Add Tag" ),
1074  tr( "Tag name already exists in your symbol database." ) );
1075  return 0;
1076  }
1077  id = mStyle->addTag( itemName );
1078  if ( !id )
1079  {
1080  QMessageBox::critical( this, tr( "Add Tag" ),
1081  tr( "New tag could not be created.\n"
1082  "There was a problem with your symbol database." ) );
1083  return 0;
1084  }
1085 
1086  QStandardItem *parentItem = model->itemFromIndex( index );
1087  QStandardItem *childItem = new QStandardItem( itemName );
1088  childItem->setData( id );
1089  parentItem->appendRow( childItem );
1090 
1091  return id;
1092 }
1093 
1095 {
1096  QStandardItemModel *model = qobject_cast<QStandardItemModel *>( groupTree->model() );
1097  QModelIndex index;
1098  for ( int i = 0; i < groupTree->model()->rowCount(); i++ )
1099  {
1100  index = groupTree->model()->index( i, 0 );
1101  QString data = index.data( Qt::UserRole + 1 ).toString();
1102  if ( data == QLatin1String( "smartgroups" ) )
1103  {
1104  break;
1105  }
1106  }
1107 
1108  QString itemName;
1109  int id;
1110  QgsSmartGroupEditorDialog dlg( mStyle, this );
1111  if ( dlg.exec() == QDialog::Rejected )
1112  return 0;
1113  id = mStyle->addSmartgroup( dlg.smartgroupName(), dlg.conditionOperator(), dlg.conditionMap() );
1114  if ( !id )
1115  return 0;
1116  itemName = dlg.smartgroupName();
1117 
1118  QStandardItem *parentItem = model->itemFromIndex( index );
1119  QStandardItem *childItem = new QStandardItem( itemName );
1120  childItem->setData( id );
1121  parentItem->appendRow( childItem );
1122 
1123  return id;
1124 }
1125 
1127 {
1128  QStandardItemModel *model = qobject_cast<QStandardItemModel *>( groupTree->model() );
1129  QModelIndex index = groupTree->currentIndex();
1130 
1131  // do not allow removal of system-defined groupings
1132  QString data = index.data( Qt::UserRole + 1 ).toString();
1133  if ( data == QLatin1String( "all" ) || data == QLatin1String( "favorite" ) || data == QLatin1String( "tags" ) || index.data() == "smartgroups" )
1134  {
1135  int err = QMessageBox::critical( this, tr( "Remove Group" ),
1136  tr( "Invalid selection. Cannot delete system defined categories.\n"
1137  "Kindly select a group or smart group you might want to delete." ) );
1138  if ( err )
1139  return;
1140  }
1141 
1142  QStandardItem *parentItem = model->itemFromIndex( index.parent() );
1143  if ( parentItem->data( Qt::UserRole + 1 ).toString() == QLatin1String( "smartgroups" ) )
1144  {
1145  mStyle->remove( QgsStyle::SmartgroupEntity, index.data( Qt::UserRole + 1 ).toInt() );
1146  }
1147  else
1148  {
1149  mStyle->remove( QgsStyle::TagEntity, index.data( Qt::UserRole + 1 ).toInt() );
1150  }
1151  parentItem->removeRow( index.row() );
1152 }
1153 
1154 void QgsStyleManagerDialog::groupRenamed( QStandardItem *item )
1155 {
1156  QgsDebugMsg( "Symbol group edited: data=" + item->data( Qt::UserRole + 1 ).toString() + " text=" + item->text() );
1157  int id = item->data( Qt::UserRole + 1 ).toInt();
1158  QString name = item->text();
1159  if ( item->parent()->data( Qt::UserRole + 1 ) == "smartgroups" )
1160  {
1162  }
1163  else
1164  {
1165  mStyle->rename( QgsStyle::TagEntity, id, name );
1166  }
1167 }
1168 
1170 {
1171 
1172  QStandardItemModel *treeModel = qobject_cast<QStandardItemModel *>( groupTree->model() );
1173  QStandardItemModel *model = qobject_cast<QStandardItemModel *>( listItems->model() );
1174 
1175  if ( mGrouppingMode )
1176  {
1177  mGrouppingMode = false;
1178  actnTagSymbols->setVisible( true );
1179  actnFinishTagging->setVisible( false );
1180  // disconnect slot which handles regrouping
1181  disconnect( model, &QStandardItemModel::itemChanged,
1183 
1184  // disabel all items except groups in groupTree
1186  groupChanged( groupTree->currentIndex() );
1187 
1188  // Finally: Reconnect all Symbol editing functionalities
1189  connect( treeModel, &QStandardItemModel::itemChanged,
1191  connect( model, &QStandardItemModel::itemChanged,
1193  // Reset the selection mode
1194  listItems->setSelectionMode( QAbstractItemView::ExtendedSelection );
1195  }
1196  else
1197  {
1198  bool validGroup = false;
1199  // determine whether it is a valid group
1200  QModelIndex present = groupTree->currentIndex();
1201  while ( present.parent().isValid() )
1202  {
1203  if ( present.parent().data() == "Tags" )
1204  {
1205  validGroup = true;
1206  break;
1207  }
1208  present = present.parent();
1209  }
1210  if ( !validGroup )
1211  return;
1212 
1213  mGrouppingMode = true;
1214  // Change visibility of actions
1215  actnTagSymbols->setVisible( false );
1216  actnFinishTagging->setVisible( true );
1217  // Remove all Symbol editing functionalities
1218  disconnect( treeModel, &QStandardItemModel::itemChanged,
1220  disconnect( model, &QStandardItemModel::itemChanged,
1222 
1223  // disabel all items except groups in groupTree
1224  enableItemsForGroupingMode( false );
1225  groupChanged( groupTree->currentIndex() );
1226  btnManageGroups->setEnabled( true );
1227 
1228 
1229  // Connect to slot which handles regrouping
1230  connect( model, &QStandardItemModel::itemChanged,
1232 
1233  // No selection should be possible
1234  listItems->setSelectionMode( QAbstractItemView::NoSelection );
1235  }
1236 }
1237 
1238 void QgsStyleManagerDialog::regrouped( QStandardItem *item )
1239 {
1241  if ( currentItemType() > 3 )
1242  {
1243  QgsDebugMsg( "Unknown style entity" );
1244  return;
1245  }
1246 
1247  QStandardItemModel *treeModel = qobject_cast<QStandardItemModel *>( groupTree->model() );
1248  QString tag = treeModel->itemFromIndex( groupTree->currentIndex() )->text();
1249 
1250  QString symbolName = item->text();
1251  bool regrouped;
1252  if ( item->checkState() == Qt::Checked )
1253  regrouped = mStyle->tagSymbol( type, symbolName, QStringList( tag ) );
1254  else
1255  regrouped = mStyle->detagSymbol( type, symbolName, QStringList( tag ) );
1256  if ( !regrouped )
1257  {
1258  int er = QMessageBox::critical( this, tr( "Group Items" ),
1259  tr( "There was a problem with the symbols database while regrouping." ) );
1260  // call the slot again to get back to normal
1261  if ( er )
1262  tagSymbolsAction();
1263  }
1264 }
1265 
1266 void QgsStyleManagerDialog::setSymbolsChecked( const QStringList &symbols )
1267 {
1268  QStandardItemModel *model = qobject_cast<QStandardItemModel *>( listItems->model() );
1269  Q_FOREACH ( const QString &symbol, symbols )
1270  {
1271  QList<QStandardItem *> items = model->findItems( symbol );
1272  Q_FOREACH ( QStandardItem *item, items )
1273  item->setCheckState( Qt::Checked );
1274  }
1275 }
1276 
1277 void QgsStyleManagerDialog::filterSymbols( const QString &qword )
1278 {
1279  QStringList items;
1281  items.sort();
1282  if ( currentItemType() == 3 )
1283  {
1284  populateColorRamps( items );
1285  }
1286  else
1287  {
1288  populateSymbols( items );
1289  }
1290 }
1291 
1292 void QgsStyleManagerDialog::symbolSelected( const QModelIndex &index )
1293 {
1294  actnEditItem->setEnabled( index.isValid() && !mGrouppingMode );
1295 }
1296 
1297 void QgsStyleManagerDialog::selectedSymbolsChanged( const QItemSelection &selected, const QItemSelection &deselected )
1298 {
1299  Q_UNUSED( selected );
1300  Q_UNUSED( deselected );
1301  bool nothingSelected = listItems->selectionModel()->selectedIndexes().empty();
1302  actnRemoveItem->setDisabled( nothingSelected );
1303  actnAddFavorite->setDisabled( nothingSelected );
1304  actnRemoveFavorite->setDisabled( nothingSelected );
1305  mGroupListMenu->setDisabled( nothingSelected );
1306  actnDetag->setDisabled( nothingSelected );
1307  actnExportAsPNG->setDisabled( nothingSelected );
1308  actnExportAsSVG->setDisabled( nothingSelected );
1309  actnEditItem->setDisabled( nothingSelected );
1310 }
1311 
1313 {
1314  groupTree->setEnabled( enable );
1315  btnAddTag->setEnabled( enable );
1316  btnAddSmartgroup->setEnabled( enable );
1317  actnAddTag->setEnabled( enable );
1318  actnAddSmartgroup->setEnabled( enable );
1319  actnRemoveGroup->setEnabled( enable );
1320  btnManageGroups->setEnabled( enable || mGrouppingMode ); // always enabled in grouping mode, as it is the only way to leave grouping mode
1321  searchBox->setEnabled( enable );
1322 }
1323 
1325 {
1326  actnRemoveGroup->setEnabled( enable );
1327  btnManageGroups->setEnabled( enable || mGrouppingMode ); // always enabled in grouping mode, as it is the only way to leave grouping mode
1328 }
1329 
1331 {
1332  QStandardItemModel *treeModel = qobject_cast<QStandardItemModel *>( groupTree->model() );
1333  for ( int i = 0; i < treeModel->rowCount(); i++ )
1334  {
1335  treeModel->item( i )->setEnabled( enable );
1336 
1337  if ( treeModel->item( i )->data() == "smartgroups" )
1338  {
1339  for ( int j = 0; j < treeModel->item( i )->rowCount(); j++ )
1340  {
1341  treeModel->item( i )->child( j )->setEnabled( enable );
1342  }
1343  }
1344  }
1345 
1346  // The buttons
1347  // NOTE: if you ever change the layout name in the .ui file edit here too
1348  for ( int i = 0; i < symbolBtnsLayout->count(); i++ )
1349  {
1350  QWidget *w = qobject_cast<QWidget *>( symbolBtnsLayout->itemAt( i )->widget() );
1351  if ( w )
1352  w->setEnabled( enable );
1353  }
1354 
1355  // The actions
1356  actnRemoveItem->setEnabled( enable );
1357  actnEditItem->setEnabled( enable );
1358 }
1359 
1361 {
1362  QPoint globalPos = groupTree->viewport()->mapToGlobal( point );
1363 
1364  QModelIndex index = groupTree->indexAt( point );
1365  QgsDebugMsg( "Now you clicked: " + index.data().toString() );
1366 
1367  if ( index.isValid() && !mGrouppingMode )
1368  mGroupTreeContextMenu->popup( globalPos );
1369 }
1370 
1372 {
1373  QPoint globalPos = listItems->viewport()->mapToGlobal( point );
1374 
1375  // Clear all actions and create new actions for every group
1376  mGroupListMenu->clear();
1377 
1378  QAction *a = nullptr;
1379  QStringList tags = mStyle->tags();
1380  tags.sort();
1381  Q_FOREACH ( const QString &tag, tags )
1382  {
1383  a = new QAction( tag, mGroupListMenu );
1384  a->setData( tag );
1385  connect( a, &QAction::triggered, this, [ = ]( bool ) { tagSelectedSymbols(); }
1386  );
1387  mGroupListMenu->addAction( a );
1388  }
1389 
1390  if ( tags.count() > 0 )
1391  {
1392  mGroupListMenu->addSeparator();
1393  }
1394  a = new QAction( tr( "Create New Tag…" ), mGroupListMenu );
1395  connect( a, &QAction::triggered, this, [ = ]( bool ) { tagSelectedSymbols( true ); }
1396  );
1397  mGroupListMenu->addAction( a );
1398 
1399  mGroupMenu->popup( globalPos );
1400 }
1401 
1403 {
1405  if ( currentItemType() > 3 )
1406  {
1407  QgsDebugMsg( "unknown entity type" );
1408  return;
1409  }
1410 
1411  QModelIndexList indexes = listItems->selectionModel()->selectedIndexes();
1412  Q_FOREACH ( const QModelIndex &index, indexes )
1413  {
1414  mStyle->addFavorite( type, index.data().toString() );
1415  }
1416  populateList();
1417 }
1418 
1420 {
1422  if ( currentItemType() > 3 )
1423  {
1424  QgsDebugMsg( "unknown entity type" );
1425  return;
1426  }
1427 
1428  QModelIndexList indexes = listItems->selectionModel()->selectedIndexes();
1429  Q_FOREACH ( const QModelIndex &index, indexes )
1430  {
1431  mStyle->removeFavorite( type, index.data().toString() );
1432  }
1433  populateList();
1434 }
1435 
1437 {
1438  QAction *selectedItem = qobject_cast<QAction *>( sender() );
1439  if ( selectedItem )
1440  {
1442  if ( currentItemType() > 3 )
1443  {
1444  QgsDebugMsg( "unknown entity type" );
1445  return;
1446  }
1447 
1448  QString tag;
1449  if ( newTag )
1450  {
1451  int id = addTag();
1452  if ( id == 0 )
1453  {
1454  return;
1455  }
1456 
1457  tag = mStyle->tag( id );
1458  }
1459  else
1460  {
1461  tag = selectedItem->data().toString();
1462  }
1463 
1464  QModelIndexList indexes = listItems->selectionModel()->selectedIndexes();
1465  Q_FOREACH ( const QModelIndex &index, indexes )
1466  {
1467  mStyle->tagSymbol( type, index.data().toString(), QStringList( tag ) );
1468  }
1469  populateList();
1470 
1471  QgsDebugMsg( "Selected Action: " + selectedItem->text() );
1472  }
1473 }
1474 
1476 {
1477  QAction *selectedItem = qobject_cast<QAction *>( sender() );
1478 
1479  if ( selectedItem )
1480  {
1482  if ( currentItemType() > 3 )
1483  {
1484  QgsDebugMsg( "unknown entity type" );
1485  return;
1486  }
1487  QModelIndexList indexes = listItems->selectionModel()->selectedIndexes();
1488  Q_FOREACH ( const QModelIndex &index, indexes )
1489  {
1490  mStyle->detagSymbol( type, index.data().toString() );
1491  }
1492  populateList();
1493 
1494  QgsDebugMsg( "Selected Action: " + selectedItem->text() );
1495  }
1496 }
1497 
1499 {
1500  QStandardItemModel *treeModel = qobject_cast<QStandardItemModel *>( groupTree->model() );
1501 
1502  // determine whether it is a valid group
1503  QModelIndex present = groupTree->currentIndex();
1504  if ( present.parent().data( Qt::UserRole + 1 ) != "smartgroups" )
1505  {
1506  QMessageBox::critical( this, tr( "Edit Smart Group" ),
1507  tr( "You have not selected a Smart Group. Kindly select a Smart Group to edit." ) );
1508  return;
1509  }
1510  QStandardItem *item = treeModel->itemFromIndex( present );
1511 
1512  QgsSmartGroupEditorDialog dlg( mStyle, this );
1513  QgsSmartConditionMap map = mStyle->smartgroup( present.data( Qt::UserRole + 1 ).toInt() );
1514  dlg.setSmartgroupName( item->text() );
1515  dlg.setOperator( mStyle->smartgroupOperator( item->data().toInt() ) );
1516  dlg.setConditionMap( map );
1517 
1518  if ( dlg.exec() == QDialog::Rejected )
1519  return;
1520 
1521  mStyle->remove( QgsStyle::SmartgroupEntity, item->data().toInt() );
1522  int id = mStyle->addSmartgroup( dlg.smartgroupName(), dlg.conditionOperator(), dlg.conditionMap() );
1523  if ( !id )
1524  {
1525  QMessageBox::critical( this, tr( "Edit Smart Group" ),
1526  tr( "There was some error while editing the smart group." ) );
1527  return;
1528  }
1529  item->setText( dlg.smartgroupName() );
1530  item->setData( id );
1531 
1532  groupChanged( present );
1533 }
1534 
1536 {
1537  reject();
1538 }
1539 
1541 {
1542  QgsHelp::openHelp( QStringLiteral( "working_with_vector/style_library.html#the-style-manager" ) );
1543 }
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)
bool addColorRamp(const QString &name, QgsColorRamp *colorRamp, bool update=false)
Adds a color ramp to the style.
Definition: qgsstyle.cpp:186
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:414
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:57
QStringList symbolsWithTag(StyleEntity type, int tagid) const
Returns the symbol names with which have the given tag.
Definition: qgsstyle.cpp:556
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
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:682
QStringList tagsOfSymbol(StyleEntity type, const QString &symbol)
Returns the tags associated with the symbol.
Definition: qgsstyle.cpp:998
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:667
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:210
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:257
QString smartgroupName()
returns the value from mNameLineEdit
bool removeColorRamp(const QString &name)
Removes color ramp from style (and delete it)
Definition: qgsstyle.cpp:239
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.
void setValue(const QString &key, const QVariant &value, const QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
bool renameSymbol(const QString &oldName, const QString &newName)
Changessymbol&#39;s name.
Definition: qgsstyle.cpp:460
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:496
int colors() const
Returns the number of colors in the ramp.
Definition: qgscolorramp.h:576
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:1065
const QgsSymbol * symbolRef(const QString &name) const
Returns a const pointer to a symbol (doesn&#39;t create new instance)
Definition: qgsstyle.cpp:170
QStringList symbolNames()
Returns a list of names of symbols.
Definition: qgsstyle.cpp:180
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:605
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:1366
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:645
void itemChanged(QStandardItem *item)
QgsGradientColorRamp * cloneGradientRamp() const
QgsSmartConditionMap smartgroup(int id)
Returns the QgsSmartConditionMap for the given id.
Definition: qgsstyle.cpp:1326
QStringList colorRampNames()
Returns a list of names of color ramps.
Definition: qgsstyle.cpp:273
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:1230
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:1127
QString schemeName() const
Returns the name of the color brewer color scheme.
Definition: qgscolorramp.h:570
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:81
QStringList tags() const
Returns a list of all tags in the style database.
Definition: qgsstyle.cpp:625
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:1137
QMenu * mGroupTreeContextMenu
Context menu for the group tree.
SymbolType type() const
Definition: qgssymbol.h:113
void setOperator(const QString &)
sets the operator AND/OR
QgsCptCityColorRamp * clone() const override
Creates a clone of the color ramp.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
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:536
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:492
int colorRampCount()
Returns count of color ramps.
Definition: qgsstyle.cpp:268
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:164
bool saveSymbol(const QString &name, QgsSymbol *symbol, bool favorite, const QStringList &tags)
Adds the symbol to the DB with the tags.
Definition: qgsstyle.cpp:105
void onClose()
Close the dialog.
bool removeSymbol(const QString &name)
Removes symbol from style (and delete it)
Definition: qgsstyle.cpp:136
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:668
bool detagSymbol(StyleEntity type, const QString &symbol, const QStringList &tags)
Detags the symbol with the given list.
Definition: qgsstyle.cpp:908
QStringList symbolsOfFavorite(StyleEntity type) const
Returns the symbol names which are flagged as favorite.
Definition: qgsstyle.cpp:520
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:1181
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.