QGIS API Documentation  3.25.0-Master (10b47c2603)
qgssymbolselectordialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgssymbolselectordialog.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 
17 
18 #include "qgsstyle.h"
19 #include "qgssymbol.h"
20 #include "qgssymbollayer.h"
21 #include "qgssymbollayerutils.h"
22 #include "qgssymbollayerregistry.h"
24 
25 // the widgets
26 #include "qgssymbolslistwidget.h"
28 #include "qgssymbollayerwidget.h"
31 
32 #include "qgslogger.h"
33 #include "qgsapplication.h"
34 #include "qgssettings.h"
35 #include "qgsfeatureiterator.h"
36 #include "qgsvectorlayer.h"
37 #include "qgssvgcache.h"
38 #include "qgsimagecache.h"
39 #include "qgsproject.h"
40 #include "qgsguiutils.h"
41 #include "qgsgui.h"
42 #include "qgsmarkersymbol.h"
43 #include "qgsfillsymbol.h"
44 #include "qgslinesymbol.h"
45 
46 #include <QColorDialog>
47 #include <QPainter>
48 #include <QStandardItemModel>
49 #include <QInputDialog>
50 #include <QMessageBox>
51 #include <QKeyEvent>
52 #include <QMenu>
53 
54 #include <QWidget>
55 #include <QFile>
56 #include <QStandardItem>
57 
59 
60 static const int SYMBOL_LAYER_ITEM_TYPE = QStandardItem::UserType + 1;
61 
62 DataDefinedRestorer::DataDefinedRestorer( QgsSymbol *symbol, const QgsSymbolLayer *symbolLayer )
63 
64 {
65  if ( symbolLayer->type() == Qgis::SymbolType::Marker && symbol->type() == Qgis::SymbolType::Marker )
66  {
67  Q_ASSERT( symbol->type() == Qgis::SymbolType::Marker );
68  mMarker = static_cast<QgsMarkerSymbol *>( symbol );
69  mMarkerSymbolLayer = static_cast<const QgsMarkerSymbolLayer *>( symbolLayer );
70  mDDSize = mMarker->dataDefinedSize();
71  mDDAngle = mMarker->dataDefinedAngle();
72  // check if restore is actually needed
73  if ( !mDDSize && !mDDAngle )
74  mMarker = nullptr;
75  }
76  else if ( symbolLayer->type() == Qgis::SymbolType::Line && symbol->type() == Qgis::SymbolType::Line )
77  {
78  mLine = static_cast<QgsLineSymbol *>( symbol );
79  mLineSymbolLayer = static_cast<const QgsLineSymbolLayer *>( symbolLayer );
80  mDDWidth = mLine->dataDefinedWidth();
81  // check if restore is actually needed
82  if ( !mDDWidth )
83  mLine = nullptr;
84  }
85  save();
86 }
87 
88 void DataDefinedRestorer::save()
89 {
90  if ( mMarker )
91  {
92  mSize = mMarkerSymbolLayer->size();
93  mAngle = mMarkerSymbolLayer->angle();
94  mMarkerOffset = mMarkerSymbolLayer->offset();
95  }
96  else if ( mLine )
97  {
98  mWidth = mLineSymbolLayer->width();
99  mLineOffset = mLineSymbolLayer->offset();
100  }
101 }
102 
103 void DataDefinedRestorer::restore()
104 {
105  if ( mMarker )
106  {
107  if ( mDDSize &&
108  ( mSize != mMarkerSymbolLayer->size() || mMarkerOffset != mMarkerSymbolLayer->offset() ) )
109  mMarker->setDataDefinedSize( mDDSize );
110  if ( mDDAngle &&
111  mAngle != mMarkerSymbolLayer->angle() )
112  mMarker->setDataDefinedAngle( mDDAngle );
113  }
114  else if ( mLine )
115  {
116  if ( mDDWidth &&
117  ( mWidth != mLineSymbolLayer->width() || mLineOffset != mLineSymbolLayer->offset() ) )
118  mLine->setDataDefinedWidth( mDDWidth );
119  }
120  save();
121 }
122 
123 // Hybrid item which may represent a symbol or a layer
124 // Check using item->isLayer()
125 class SymbolLayerItem : public QStandardItem
126 {
127  public:
128  explicit SymbolLayerItem( QgsSymbolLayer *layer, Qgis::SymbolType symbolType )
129  {
130  setLayer( layer, symbolType );
131  }
132 
133  explicit SymbolLayerItem( QgsSymbol *symbol )
134  {
135  setSymbol( symbol );
136  }
137 
138  void setLayer( QgsSymbolLayer *layer, Qgis::SymbolType symbolType )
139  {
140  mLayer = layer;
141  mIsLayer = true;
142  mSymbol = nullptr;
143  mSymbolType = symbolType;
144  updatePreview();
145  }
146 
147  void setSymbol( QgsSymbol *symbol )
148  {
149  mSymbol = symbol;
150  mIsLayer = false;
151  mLayer = nullptr;
152  updatePreview();
153  }
154 
155  void updatePreview()
156  {
157  if ( !mSize.isValid() )
158  {
159  const int size = QgsGuiUtils::scaleIconSize( 16 );
160  mSize = QSize( size, size );
161  }
162  QIcon icon;
163  if ( mIsLayer )
164  icon = QgsSymbolLayerUtils::symbolLayerPreviewIcon( mLayer, QgsUnitTypes::RenderMillimeters, mSize, QgsMapUnitScale(), mSymbol ? mSymbol->type() : mSymbolType ); //todo: make unit a parameter
165  else
166  icon = QgsSymbolLayerUtils::symbolPreviewIcon( mSymbol, mSize );
167  setIcon( icon );
168 
169  if ( auto *lParent = parent() )
170  static_cast<SymbolLayerItem *>( lParent )->updatePreview();
171  }
172 
173  int type() const override { return SYMBOL_LAYER_ITEM_TYPE; }
174  bool isLayer() { return mIsLayer; }
175 
176  // returns the symbol pointer; helpful in determining a layer's parent symbol
177  QgsSymbol *symbol()
178  {
179  return mSymbol;
180  }
181 
182  QgsSymbolLayer *layer()
183  {
184  return mLayer;
185  }
186 
187  QVariant data( int role ) const override
188  {
189  if ( role == Qt::DisplayRole || role == Qt::EditRole )
190  {
191  if ( mIsLayer )
192  {
194  if ( m )
195  return m->visibleName();
196  else
197  return QString();
198  }
199  else
200  {
201  switch ( mSymbol->type() )
202  {
204  return QCoreApplication::translate( "SymbolLayerItem", "Marker" );
206  return QCoreApplication::translate( "SymbolLayerItem", "Fill" );
208  return QCoreApplication::translate( "SymbolLayerItem", "Line" );
209  default:
210  return "Symbol";
211  }
212  }
213  }
214  else if ( role == Qt::ForegroundRole && mIsLayer )
215  {
216  if ( !mLayer->enabled() )
217  {
218  QPalette pal = qApp->palette();
219  QBrush brush = QStandardItem::data( role ).value< QBrush >();
220  brush.setColor( pal.color( QPalette::Disabled, QPalette::WindowText ) );
221  return brush;
222  }
223  else
224  {
225  return QVariant();
226  }
227  }
228 
229 // if ( role == Qt::SizeHintRole )
230 // return QVariant( QSize( 32, 32 ) );
231  if ( role == Qt::CheckStateRole )
232  return QVariant(); // could be true/false
233  return QStandardItem::data( role );
234  }
235 
236  protected:
237  QgsSymbolLayer *mLayer = nullptr;
238  QgsSymbol *mSymbol = nullptr;
239  bool mIsLayer;
240  QSize mSize;
242 };
243 
245 
247 
249  : QgsPanelWidget( parent )
250  , mStyle( style )
251  , mSymbol( symbol )
252  , mVectorLayer( vl )
253 {
254 #ifdef Q_OS_MAC
255  setWindowModality( Qt::WindowModal );
256 #endif
257 
258  setupUi( this );
259  this->layout()->setContentsMargins( 0, 0, 0, 0 );
260 
261  layersTree->setMaximumHeight( static_cast< int >( Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 7 ) );
262  layersTree->setMinimumHeight( layersTree->maximumHeight() );
263  lblPreview->setMaximumWidth( layersTree->maximumHeight() );
264 
265  // setup icons
266  btnAddLayer->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
267  btnRemoveLayer->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
268  QIcon iconLock;
269  iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "locked.svg" ) ), QSize(), QIcon::Normal, QIcon::On );
270  iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "locked.svg" ) ), QSize(), QIcon::Active, QIcon::On );
271  iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "unlocked.svg" ) ), QSize(), QIcon::Normal, QIcon::Off );
272  iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "unlocked.svg" ) ), QSize(), QIcon::Active, QIcon::Off );
273  btnLock->setIcon( iconLock );
274  btnDuplicate->setIcon( QIcon( QgsApplication::iconPath( "mActionDuplicateLayer.svg" ) ) );
275  btnUp->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowUp.svg" ) ) );
276  btnDown->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowDown.svg" ) ) );
277 
278  mSymbolLayersModel = new QStandardItemModel( layersTree );
279  // Set the symbol
280  layersTree->setModel( mSymbolLayersModel );
281  layersTree->setHeaderHidden( true );
282 
283  //get first feature from layer for previews
284  if ( mVectorLayer )
285  {
286 #if 0 // this is too expensive to do for many providers. TODO revisit when support for connection timeouts is complete across all providers
287  // short timeout for request - it doesn't really matter if we don't get the feature, and this call is blocking UI
288  QgsFeatureIterator it = mVectorLayer->getFeatures( QgsFeatureRequest().setLimit( 1 ).setConnectionTimeout( 100 ) );
289  it.nextFeature( mPreviewFeature );
290 #endif
291  mPreviewExpressionContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( mVectorLayer ) );
292 #if 0
293  mPreviewExpressionContext.setFeature( mPreviewFeature );
294 #endif
295  }
296  else
297  {
298  mPreviewExpressionContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) );
299  }
300 
301  QItemSelectionModel *selModel = layersTree->selectionModel();
302  connect( selModel, &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
303 
304  loadSymbol( mSymbol, static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() ) );
305  updatePreview();
306 
307  connect( btnUp, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::moveLayerUp );
308  connect( btnDown, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::moveLayerDown );
309  connect( btnAddLayer, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::addLayer );
310  connect( btnRemoveLayer, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::removeLayer );
311  connect( btnLock, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::lockLayer );
312  connect( btnDuplicate, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::duplicateLayer );
314 
315  updateUi();
316 
317  // set symbol as active item in the tree
318  const QModelIndex newIndex = layersTree->model()->index( 0, 0 );
319  layersTree->setCurrentIndex( newIndex );
320 
321  setPanelTitle( tr( "Symbol Selector" ) );
322 
323  // when a remote svg has been fetched, update the widget's previews
324  // this is required if the symbol utilizes remote svgs, and the current previews
325  // have been generated using the temporary "downloading" svg. In this case
326  // we require the preview to be regenerated to use the correct fetched
327  // svg
328  connect( QgsApplication::svgCache(), &QgsSvgCache::remoteSvgFetched, this, &QgsSymbolSelectorWidget::projectDataChanged );
329 
330  // when a remote image has been fetched, update the widget's previews
331  // this is required if the symbol utilizes remote images, and the current previews
332  // have been generated using the temporary "downloading" image. In this case
333  // we require the preview to be regenerated to use the correct fetched
334  // image
335  connect( QgsApplication::imageCache(), &QgsImageCache::remoteImageFetched, this, &QgsSymbolSelectorWidget::projectDataChanged );
336 
337  // if project color scheme changes, we need to redraw symbols - they may use project colors and accordingly
338  // need updating to reflect the new colors
339  connect( QgsProject::instance(), &QgsProject::projectColorsChanged, this, &QgsSymbolSelectorWidget::projectDataChanged );
340 
341  connect( QgsProject::instance(), static_cast < void ( QgsProject::* )( const QList<QgsMapLayer *>& layers ) > ( &QgsProject::layersWillBeRemoved ), this, &QgsSymbolSelectorWidget::layersAboutToBeRemoved );
342 }
343 
345 {
346  if ( !mAdvancedMenu )
347  {
348  mAdvancedMenu = new QMenu( this );
349  // Brute force method to activate the Advanced menu
350  layerChanged();
351  }
352  return mAdvancedMenu;
353 }
354 
356 {
357  mContext = context;
358 
359  if ( auto *lExpressionContext = mContext.expressionContext() )
360  {
361  mPreviewExpressionContext = *lExpressionContext;
362  if ( mVectorLayer )
363  mPreviewExpressionContext.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer ) );
364 
365  mPreviewExpressionContext.setFeature( mPreviewFeature );
366  }
367 
368  QWidget *widget = stackedWidget->currentWidget();
369  if ( QgsLayerPropertiesWidget *layerProp = qobject_cast< QgsLayerPropertiesWidget * >( widget ) )
370  layerProp->setContext( context );
371  else if ( QgsSymbolsListWidget *listWidget = qobject_cast< QgsSymbolsListWidget * >( widget ) )
372  listWidget->setContext( context );
373 
374  layerChanged();
375  updatePreview();
376 }
377 
379 {
380  return mContext;
381 }
382 
383 void QgsSymbolSelectorWidget::loadSymbol( QgsSymbol *symbol, SymbolLayerItem *parent )
384 {
385  if ( !symbol )
386  return;
387 
388  if ( !parent )
389  {
390  mSymbol = symbol;
391  mSymbolLayersModel->clear();
392  parent = static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() );
393  }
394 
395  SymbolLayerItem *symbolItem = new SymbolLayerItem( symbol );
396  QFont boldFont = symbolItem->font();
397  boldFont.setBold( true );
398  symbolItem->setFont( boldFont );
399  parent->appendRow( symbolItem );
400 
401  const int count = symbol->symbolLayerCount();
402  for ( int i = count - 1; i >= 0; i-- )
403  {
404  SymbolLayerItem *layerItem = new SymbolLayerItem( symbol->symbolLayer( i ), symbol->type() );
405  layerItem->setEditable( false );
406  symbolItem->appendRow( layerItem );
407  if ( symbol->symbolLayer( i )->subSymbol() )
408  {
409  loadSymbol( symbol->symbolLayer( i )->subSymbol(), layerItem );
410  }
411  layersTree->setExpanded( layerItem->index(), true );
412  }
413  layersTree->setExpanded( symbolItem->index(), true );
414 
415  if ( mSymbol == symbol && !layersTree->currentIndex().isValid() )
416  {
417  // make sure root item for symbol is selected in tree
418  layersTree->setCurrentIndex( symbolItem->index() );
419  }
420 }
421 
422 void QgsSymbolSelectorWidget::reloadSymbol()
423 {
424  mSymbolLayersModel->clear();
425  loadSymbol( mSymbol, static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() ) );
426 }
427 
428 void QgsSymbolSelectorWidget::updateUi()
429 {
430  const QModelIndex currentIdx = layersTree->currentIndex();
431  if ( !currentIdx.isValid() )
432  return;
433 
434  SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( currentIdx ) );
435  if ( !item->isLayer() )
436  {
437  btnUp->setEnabled( false );
438  btnDown->setEnabled( false );
439  btnRemoveLayer->setEnabled( false );
440  btnLock->setEnabled( false );
441  btnDuplicate->setEnabled( false );
442  return;
443  }
444 
445  const int rowCount = item->parent()->rowCount();
446  const int currentRow = item->row();
447 
448  btnUp->setEnabled( currentRow > 0 );
449  btnDown->setEnabled( currentRow < rowCount - 1 );
450  btnRemoveLayer->setEnabled( rowCount > 1 );
451  btnLock->setEnabled( true );
452  btnDuplicate->setEnabled( true );
453 }
454 
456 {
457  if ( !mSymbol )
458  return;
459 
460  std::unique_ptr< QgsSymbol > symbolClone( mSymbol->clone() );
461  const QImage preview = symbolClone->bigSymbolPreviewImage( &mPreviewExpressionContext, Qgis::SymbolPreviewFlags() );
462  lblPreview->setPixmap( QPixmap::fromImage( preview ) );
463  // Hope this is a appropriate place
464  if ( !mBlockModified )
465  emit symbolModified();
466 }
467 
469 {
470  // get current layer item and update its icon
471  SymbolLayerItem *item = currentLayerItem();
472  if ( item )
473  item->updatePreview();
474  // update also preview of the whole symbol
475  updatePreview();
476 }
477 
478 SymbolLayerItem *QgsSymbolSelectorWidget::currentLayerItem()
479 {
480  const QModelIndex idx = layersTree->currentIndex();
481  if ( !idx.isValid() )
482  return nullptr;
483 
484  SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
485  if ( !item->isLayer() )
486  return nullptr;
487 
488  return item;
489 }
490 
491 QgsSymbolLayer *QgsSymbolSelectorWidget::currentLayer()
492 {
493  const QModelIndex idx = layersTree->currentIndex();
494  if ( !idx.isValid() )
495  return nullptr;
496 
497  SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
498  if ( item->isLayer() )
499  return item->layer();
500 
501  return nullptr;
502 }
503 
505 {
506  updateUi();
507 
508  SymbolLayerItem *currentItem = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( layersTree->currentIndex() ) );
509  if ( !currentItem )
510  return;
511 
512  if ( currentItem->isLayer() )
513  {
514  SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( currentItem->parent() );
515  mDataDefineRestorer.reset( new DataDefinedRestorer( parent->symbol(), currentItem->layer() ) );
516  QgsLayerPropertiesWidget *layerProp = new QgsLayerPropertiesWidget( currentItem->layer(), parent->symbol(), mVectorLayer );
517  layerProp->setDockMode( this->dockMode() );
518  layerProp->setContext( mContext );
519  setWidget( layerProp );
520  connect( layerProp, &QgsLayerPropertiesWidget::changed, mDataDefineRestorer.get(), &DataDefinedRestorer::restore );
522  // This connection when layer type is changed
524 
525  connectChildPanel( layerProp );
526  }
527  else
528  {
529  // then it must be a symbol
530  mDataDefineRestorer.reset();
532  currentItem->symbol()->setLayer( mVectorLayer );
534  // Now populate symbols of that type using the symbols list widget:
535  QgsSymbolsListWidget *symbolsList = new QgsSymbolsListWidget( currentItem->symbol(), mStyle, mAdvancedMenu, this, mVectorLayer );
536  symbolsList->setContext( mContext );
537 
538  setWidget( symbolsList );
540  }
541  updateLockButton();
542 }
543 
545 {
546  SymbolLayerItem *currentItem = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( layersTree->currentIndex() ) );
547  if ( !currentItem || currentItem->isLayer() )
548  return;
549  // disconnect to avoid recreating widget
550  disconnect( layersTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
551  if ( currentItem->parent() )
552  {
553  // it is a sub-symbol
554  QgsSymbol *symbol = currentItem->symbol();
555  SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( currentItem->parent() );
556  parent->removeRow( 0 );
557  loadSymbol( symbol, parent );
558  layersTree->setCurrentIndex( parent->child( 0 )->index() );
559  parent->updatePreview();
560  }
561  else
562  {
563  //it is the symbol itself
564  reloadSymbol();
565  const QModelIndex newIndex = layersTree->model()->index( 0, 0 );
566  layersTree->setCurrentIndex( newIndex );
567  }
568  updatePreview();
569  // connect it back once things are set
570  connect( layersTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
571 }
572 
573 void QgsSymbolSelectorWidget::setWidget( QWidget *widget )
574 {
575  const int index = stackedWidget->addWidget( widget );
576  stackedWidget->setCurrentIndex( index );
577  if ( mPresentWidget )
578  mPresentWidget->deleteLater();
579  mPresentWidget = widget;
580 }
581 
582 void QgsSymbolSelectorWidget::updateLockButton()
583 {
584  QgsSymbolLayer *layer = currentLayer();
585  if ( !layer )
586  return;
587  btnLock->setChecked( layer->isLocked() );
588 }
589 
591 {
592  const QModelIndex idx = layersTree->currentIndex();
593  if ( !idx.isValid() )
594  return;
595 
596  int insertIdx = -1;
597  SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
598  if ( item->isLayer() )
599  {
600  insertIdx = item->row();
601  item = static_cast<SymbolLayerItem *>( item->parent() );
602  }
603 
604  QgsSymbol *parentSymbol = item->symbol();
605 
606  // save data-defined values at marker level
607  const QgsProperty ddSize( parentSymbol->type() == Qgis::SymbolType::Marker
608  ? static_cast<QgsMarkerSymbol *>( parentSymbol )->dataDefinedSize()
609  : QgsProperty() );
610  const QgsProperty ddAngle( parentSymbol->type() == Qgis::SymbolType::Marker
611  ? static_cast<QgsMarkerSymbol *>( parentSymbol )->dataDefinedAngle()
612  : QgsProperty() );
613  const QgsProperty ddWidth( parentSymbol->type() == Qgis::SymbolType::Line
614  ? static_cast<QgsLineSymbol *>( parentSymbol )->dataDefinedWidth()
615  : QgsProperty() );
616 
617  QgsSymbolLayer *newLayer = QgsSymbolLayerRegistry::defaultSymbolLayer( parentSymbol->type() );
618  if ( insertIdx == -1 )
619  parentSymbol->appendSymbolLayer( newLayer );
620  else
621  parentSymbol->insertSymbolLayer( item->rowCount() - insertIdx, newLayer );
622 
623  // restore data-defined values at marker level
624  if ( ddSize )
625  static_cast<QgsMarkerSymbol *>( parentSymbol )->setDataDefinedSize( ddSize );
626  if ( ddAngle )
627  static_cast<QgsMarkerSymbol *>( parentSymbol )->setDataDefinedAngle( ddAngle );
628  if ( ddWidth )
629  static_cast<QgsLineSymbol *>( parentSymbol )->setDataDefinedWidth( ddWidth );
630 
631  SymbolLayerItem *newLayerItem = new SymbolLayerItem( newLayer, parentSymbol->type() );
632  item->insertRow( insertIdx == -1 ? 0 : insertIdx, newLayerItem );
633  item->updatePreview();
634 
635  layersTree->setCurrentIndex( mSymbolLayersModel->indexFromItem( newLayerItem ) );
636  updateUi();
637  updatePreview();
638 }
639 
641 {
642  SymbolLayerItem *item = currentLayerItem();
643  const int row = item->row();
644  SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( item->parent() );
645 
646  const int layerIdx = parent->rowCount() - row - 1; // IMPORTANT
647  QgsSymbol *parentSymbol = parent->symbol();
648  QgsSymbolLayer *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
649 
650  parent->removeRow( row );
651  parent->updatePreview();
652 
653  const QModelIndex newIdx = parent->child( 0 )->index();
654  layersTree->setCurrentIndex( newIdx );
655 
656  updateUi();
657  updatePreview();
658  //finally delete the removed layer pointer
659  delete tmpLayer;
660 }
661 
663 {
664  moveLayerByOffset( + 1 );
665 }
666 
668 {
669  moveLayerByOffset( -1 );
670 }
671 
672 void QgsSymbolSelectorWidget::moveLayerByOffset( int offset )
673 {
674  SymbolLayerItem *item = currentLayerItem();
675  if ( !item )
676  return;
677  const int row = item->row();
678 
679  SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( item->parent() );
680  QgsSymbol *parentSymbol = parent->symbol();
681 
682  const int layerIdx = parent->rowCount() - row - 1;
683  // switch layers
684  QgsSymbolLayer *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
685  parentSymbol->insertSymbolLayer( layerIdx - offset, tmpLayer );
686 
687  QList<QStandardItem *> rowItems = parent->takeRow( row );
688  parent->insertRows( row + offset, rowItems );
689  parent->updatePreview();
690 
691  const QModelIndex newIdx = rowItems[ 0 ]->index();
692  layersTree->setCurrentIndex( newIdx );
693 
694  updatePreview();
695  updateUi();
696 }
697 
699 {
700  QgsSymbolLayer *layer = currentLayer();
701  if ( !layer )
702  return;
703  layer->setLocked( btnLock->isChecked() );
704  emit symbolModified();
705 }
706 
708 {
709  const QModelIndex idx = layersTree->currentIndex();
710  if ( !idx.isValid() )
711  return;
712 
713  SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
714  if ( !item->isLayer() )
715  return;
716 
717  QgsSymbolLayer *source = item->layer();
718 
719  const int insertIdx = item->row();
720  item = static_cast<SymbolLayerItem *>( item->parent() );
721 
722  QgsSymbol *parentSymbol = item->symbol();
723 
724  QgsSymbolLayer *newLayer = source->clone();
725  if ( insertIdx == -1 )
726  parentSymbol->appendSymbolLayer( newLayer );
727  else
728  parentSymbol->insertSymbolLayer( item->rowCount() - insertIdx, newLayer );
729 
730  SymbolLayerItem *newLayerItem = new SymbolLayerItem( newLayer, parentSymbol->type() );
731  item->insertRow( insertIdx == -1 ? 0 : insertIdx, newLayerItem );
732  if ( newLayer->subSymbol() )
733  {
734  loadSymbol( newLayer->subSymbol(), newLayerItem );
735  layersTree->setExpanded( newLayerItem->index(), true );
736  }
737  item->updatePreview();
738 
739  layersTree->setCurrentIndex( mSymbolLayersModel->indexFromItem( newLayerItem ) );
740  updateUi();
741  updatePreview();
742 }
743 
745 {
746  SymbolLayerItem *item = currentLayerItem();
747  QgsSymbolLayer *layer = item->layer();
748 
749  if ( layer->subSymbol() )
750  {
751  item->removeRow( 0 );
752  }
753  QgsSymbol *symbol = static_cast<SymbolLayerItem *>( item->parent() )->symbol();
754 
755  // update symbol layer item
756  item->setLayer( newLayer, symbol->type() );
757  // When it is a marker symbol
758  if ( newLayer->subSymbol() )
759  {
760  loadSymbol( newLayer->subSymbol(), item );
761  layersTree->setExpanded( item->index(), true );
762  }
763 
764  // Change the symbol at last to avoid deleting item's layer
765  const int layerIdx = item->parent()->rowCount() - item->row() - 1;
766  symbol->changeSymbolLayer( layerIdx, newLayer );
767 
768  item->updatePreview();
769  updatePreview();
770  // Important: This lets the layer have its own layer properties widget
771  layerChanged();
772 }
773 
774 QgsSymbolSelectorDialog::QgsSymbolSelectorDialog( QgsSymbol *symbol, QgsStyle *style, QgsVectorLayer *vl, QWidget *parent, bool embedded )
775  : QDialog( parent )
776 {
777  setLayout( new QVBoxLayout() );
778 
779  mSelectorWidget = new QgsSymbolSelectorWidget( symbol, style, vl, this );
780  mButtonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
781 
782  connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
783  connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
784  connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsSymbolSelectorDialog::showHelp );
785 
786  layout()->addWidget( mSelectorWidget );
787  layout()->addWidget( mButtonBox );
788 
789  connect( mSelectorWidget, &QgsPanelWidget::panelAccepted, this, &QDialog::reject );
790 
791  mSelectorWidget->setMinimumSize( 460, 560 );
792  setObjectName( QStringLiteral( "SymbolSelectorDialog" ) );
794 
795  // Can be embedded in renderer properties dialog
796  if ( embedded )
797  {
798  mButtonBox->hide();
799  layout()->setContentsMargins( 0, 0, 0, 0 );
800  }
801  else
802  {
803  setWindowTitle( tr( "Symbol Selector" ) );
804  }
805  mSelectorWidget->setDockMode( embedded );
806 }
807 
809 {
810  return mSelectorWidget->advancedMenu();
811 }
812 
814 {
815  mContext = context;
816 }
817 
819 {
820  return mContext;
821 }
822 
824 {
825  return mSelectorWidget->symbol();
826 }
827 
829 {
830  // Ignore the ESC key to avoid close the dialog without the properties window
831  if ( !isWindow() && e->key() == Qt::Key_Escape )
832  {
833  e->ignore();
834  }
835  else
836  {
837  QDialog::keyPressEvent( e );
838  }
839 }
840 
841 void QgsSymbolSelectorDialog::reloadSymbol()
842 {
843  mSelectorWidget->reloadSymbol();
844 }
845 
846 void QgsSymbolSelectorDialog::loadSymbol( QgsSymbol *symbol, SymbolLayerItem *parent )
847 {
848  mSelectorWidget->loadSymbol( symbol, parent );
849 }
850 
851 void QgsSymbolSelectorDialog::updateUi()
852 {
853  mSelectorWidget->updateUi();
854 }
855 
856 void QgsSymbolSelectorDialog::updateLockButton()
857 {
858  mSelectorWidget->updateLockButton();
859 }
860 
861 SymbolLayerItem *QgsSymbolSelectorDialog::currentLayerItem()
862 {
863  return mSelectorWidget->currentLayerItem();
864 }
865 
866 QgsSymbolLayer *QgsSymbolSelectorDialog::currentLayer()
867 {
868  return mSelectorWidget->currentLayer();
869 }
870 
871 void QgsSymbolSelectorDialog::moveLayerByOffset( int offset )
872 {
873  mSelectorWidget->moveLayerByOffset( offset );
874 }
875 
876 void QgsSymbolSelectorDialog::setWidget( QWidget *widget )
877 {
878  mSelectorWidget->setWidget( widget );
879 }
880 
882 {
883  mSelectorWidget->moveLayerDown();
884 }
885 
887 {
888  mSelectorWidget->moveLayerUp();
889 }
890 
892 {
893  mSelectorWidget->addLayer();
894 }
895 
897 {
898  mSelectorWidget->removeLayer();
899 }
900 
902 {
903  mSelectorWidget->lockLayer();
904 }
905 
907 {
908  mSelectorWidget->duplicateLayer();
909 }
910 
912 {
913  mSelectorWidget->layerChanged();
914 }
915 
917 {
918  mSelectorWidget->updateLayerPreview();
919 }
920 
922 {
923  mSelectorWidget->updatePreview();
924 }
925 
927 {
928  mSelectorWidget->symbolChanged();
929 }
930 
932 {
933  mSelectorWidget->changeLayer( layer );
934 }
935 
936 QDialogButtonBox *QgsSymbolSelectorDialog::buttonBox() const
937 {
938  return mButtonBox;
939 }
940 
941 void QgsSymbolSelectorDialog::showHelp()
942 {
943  QgsHelp::openHelp( QStringLiteral( "style_library/symbol_selector.html" ) );
944 }
945 
946 void QgsSymbolSelectorWidget::projectDataChanged()
947 {
948  mBlockModified = true;
949  symbolChanged();
950  updatePreview();
951  mBlockModified = false;
952 }
953 
954 void QgsSymbolSelectorWidget::layersAboutToBeRemoved( const QList<QgsMapLayer *> &layers )
955 {
956  if ( mVectorLayer && layers.contains( mVectorLayer ) )
957  {
958  disconnect( QgsProject::instance(), &QgsProject::projectColorsChanged, this, &QgsSymbolSelectorWidget::projectDataChanged );
959  }
960 }
SymbolType
Symbol types.
Definition: qgis.h:205
@ Marker
Marker symbol.
@ Line
Line symbol.
@ Fill
Fill symbol.
@ Hybrid
Hybrid symbol.
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:1856
static QgsSymbolLayerRegistry * symbolLayerRegistry()
Returns the application's symbol layer registry, used for managing symbol layers.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition: qgsgui.cpp:180
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
void remoteImageFetched(const QString &url)
Emitted when the cache has finished retrieving an image file from a remote url.
void setDockMode(bool dockMode) override
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
void changeLayer(QgsSymbolLayer *)
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgslinesymbol.h:30
QgsProperty dataDefinedWidth() const
Returns data defined width for whole symbol (including all symbol layers).
Struct for storing maximum and minimum scales for measurements in map units.
Abstract base class for marker symbol layers.
A marker symbol type, for rendering Point and MultiPoint geometries.
QgsProperty dataDefinedAngle() const
Returns data defined angle for whole symbol (including all symbol layers).
QgsProperty dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
Base class for any widget that can be shown as a inline panel.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
void connectChildPanel(QgsPanelWidget *panel)
Connect the given sub panel widgets showPanel signals to this current panels main showPanel event to ...
void widgetChanged()
Emitted when the widget state changes.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
virtual void setDockMode(bool dockMode)
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
bool dockMode()
Returns the dock mode state.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:104
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:479
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the registry.
void projectColorsChanged()
Emitted whenever the project's color scheme has been changed.
A store for object properties.
Definition: qgsproperty.h:231
void remoteSvgFetched(const QString &url)
Emitted when the cache has finished retrieving an SVG file from a remote url.
Stores metadata about one symbol layer class.
QgsSymbolLayerAbstractMetadata * symbolLayerMetadata(const QString &name) const
Returns metadata for specified symbol layer. Returns nullptr if not found.
static QgsSymbolLayer * defaultSymbolLayer(Qgis::SymbolType type)
create a new instance of symbol layer for specified symbol type with default settings
static QIcon symbolPreviewIcon(const QgsSymbol *symbol, QSize size, int padding=0, QgsLegendPatchShape *shape=nullptr)
Returns an icon preview for a color ramp.
static QIcon symbolLayerPreviewIcon(const QgsSymbolLayer *layer, QgsUnitTypes::RenderUnit u, QSize size, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::SymbolType parentSymbolType=Qgis::SymbolType::Hybrid)
Draws a symbol layer preview to an icon.
Qgis::SymbolType type() const
bool isLocked() const
Returns true if the symbol layer colors are locked and the layer will ignore any symbol-level color c...
virtual QgsSymbolLayer * clone() const =0
Shall be reimplemented by subclasses to create a deep copy of the instance.
virtual QgsSymbol * subSymbol()
Returns the symbol's sub symbol, if present.
void setLocked(bool locked)
Sets whether the layer's colors are locked.
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
QgsSymbolSelectorDialog(QgsSymbol *symbol, QgsStyle *style, QgsVectorLayer *vl, QWidget *parent=nullptr, bool embedded=false)
Constructor for QgsSymbolSelectorDialog.
QgsSymbolWidgetContext context() const
Returns the context in which the symbol widget is shown, e.g., the associated map canvas and expressi...
QMenu * advancedMenu()
Returns menu for "advanced" button - create it if doesn't exist and show the advanced button.
void symbolChanged()
Slot to update tree when a new symbol from style.
QDialogButtonBox * buttonBox() const
Returns a reference to the dialog's button box.
QgsSymbol * symbol()
Returns the symbol that is currently active in the widget.
void keyPressEvent(QKeyEvent *e) override
void duplicateLayer()
Duplicates the current symbol layer and places the duplicated layer above the current symbol layer.
void changeLayer(QgsSymbolLayer *layer)
alters tree and sets proper widget when Layer Type is changed
void loadSymbol(QgsSymbol *symbol, SymbolLayerItem *parent=nullptr)
Loads the given symbol into the widget.
Symbol selector widget that can be used to select and build a symbol.
void loadSymbol(QgsSymbol *symbol, SymbolLayerItem *parent=nullptr)
Loads the given symbol into the widget.
void symbolChanged()
Slot to update tree when a new symbol from style.
void addLayer()
Add a symbol layer to the bottom of the stack.
QMenu * advancedMenu()
Returns menu for "advanced" button - create it if doesn't exist and show the advanced button.
void layerChanged()
Called when the layer changes in the widget.
void changeLayer(QgsSymbolLayer *layer)
alters tree and sets proper widget when Layer Type is changed
void updatePreview()
Update the preview of the whole symbol in the interface.
QgsSymbolSelectorWidget(QgsSymbol *symbol, QgsStyle *style, QgsVectorLayer *vl, QWidget *parent=nullptr)
Symbol selector widget that can be used to select and build a symbol.
void removeLayer()
Remove the current active symbol layer.
QgsSymbolWidgetContext context() const
Returns the context in which the symbol widget is shown, e.g., the associated map canvas and expressi...
void moveLayerDown()
Move the active symbol layer down.
void symbolModified()
Emitted when a symbol is modified in the widget.
void duplicateLayer()
Duplicates the current symbol layer and places the duplicated layer above the current symbol layer.
void lockLayer()
Lock the current active symbol layer.
QgsSymbol * symbol()
Returns the symbol that is currently active in the widget.
void updateLayerPreview()
Update the single symbol layer preview in the widget.
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
void moveLayerUp()
Move the active symbol layer up.
Contains settings which reflect the context in which a symbol (or renderer) widget is shown,...
QgsExpressionContext * expressionContext() const
Returns the expression context used for the widget, if set.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:93
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:423
bool appendSymbolLayer(QgsSymbolLayer *layer)
Appends a symbol layer at the end of the current symbol layer list.
Definition: qgssymbol.cpp:446
bool insertSymbolLayer(int index, QgsSymbolLayer *layer)
Inserts a symbol layer to specified index.
Definition: qgssymbol.cpp:433
bool changeSymbolLayer(int index, QgsSymbolLayer *layer)
Deletes the current layer at the specified index and replaces it with layer.
Definition: qgssymbol.cpp:476
QgsSymbolLayer * takeSymbolLayer(int index)
Removes a symbol layer from the list and returns a pointer to it.
Definition: qgssymbol.cpp:467
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition: qgssymbol.h:215
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:152
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
Q_DECL_DEPRECATED void setLayer(const QgsVectorLayer *layer)
Definition: qgssymbol.cpp:894
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:2617
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:2616