QGIS API Documentation  3.20.0-Odense (decaadbb31)
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 )
129  {
130  setLayer( layer );
131  }
132 
133  explicit SymbolLayerItem( QgsSymbol *symbol )
134  {
135  setSymbol( symbol );
136  }
137 
138  void setLayer( QgsSymbolLayer *layer )
139  {
140  mLayer = layer;
141  mIsLayer = true;
142  mSymbol = nullptr;
143  updatePreview();
144  }
145 
146  void setSymbol( QgsSymbol *symbol )
147  {
148  mSymbol = symbol;
149  mIsLayer = false;
150  mLayer = nullptr;
151  updatePreview();
152  }
153 
154  void updatePreview()
155  {
156  if ( !mSize.isValid() )
157  {
158  const int size = QgsGuiUtils::scaleIconSize( 16 );
159  mSize = QSize( size, size );
160  }
161  QIcon icon;
162  if ( mIsLayer )
163  icon = QgsSymbolLayerUtils::symbolLayerPreviewIcon( mLayer, QgsUnitTypes::RenderMillimeters, mSize ); //todo: make unit a parameter
164  else
165  icon = QgsSymbolLayerUtils::symbolPreviewIcon( mSymbol, mSize );
166  setIcon( icon );
167 
168  if ( auto *lParent = parent() )
169  static_cast<SymbolLayerItem *>( lParent )->updatePreview();
170  }
171 
172  int type() const override { return SYMBOL_LAYER_ITEM_TYPE; }
173  bool isLayer() { return mIsLayer; }
174 
175  // returns the symbol pointer; helpful in determining a layer's parent symbol
176  QgsSymbol *symbol()
177  {
178  return mSymbol;
179  }
180 
181  QgsSymbolLayer *layer()
182  {
183  return mLayer;
184  }
185 
186  QVariant data( int role ) const override
187  {
188  if ( role == Qt::DisplayRole || role == Qt::EditRole )
189  {
190  if ( mIsLayer )
191  return QgsApplication::symbolLayerRegistry()->symbolLayerMetadata( mLayer->layerType() )->visibleName();
192  else
193  {
194  switch ( mSymbol->type() )
195  {
197  return QCoreApplication::translate( "SymbolLayerItem", "Marker" );
199  return QCoreApplication::translate( "SymbolLayerItem", "Fill" );
201  return QCoreApplication::translate( "SymbolLayerItem", "Line" );
202  default:
203  return "Symbol";
204  }
205  }
206  }
207  else if ( role == Qt::ForegroundRole && mIsLayer )
208  {
209  QBrush brush( Qt::black, Qt::SolidPattern );
210  if ( !mLayer->enabled() )
211  {
212  brush.setColor( Qt::lightGray );
213  }
214  return brush;
215  }
216 
217 // if ( role == Qt::SizeHintRole )
218 // return QVariant( QSize( 32, 32 ) );
219  if ( role == Qt::CheckStateRole )
220  return QVariant(); // could be true/false
221  return QStandardItem::data( role );
222  }
223 
224  protected:
225  QgsSymbolLayer *mLayer = nullptr;
226  QgsSymbol *mSymbol = nullptr;
227  bool mIsLayer;
228  QSize mSize;
229 };
230 
232 
234 
236  : QgsPanelWidget( parent )
237  , mStyle( style )
238  , mSymbol( symbol )
239  , mVectorLayer( vl )
240 {
241 #ifdef Q_OS_MAC
242  setWindowModality( Qt::WindowModal );
243 #endif
244 
245  setupUi( this );
246  this->layout()->setContentsMargins( 0, 0, 0, 0 );
247 
248  layersTree->setMaximumHeight( static_cast< int >( Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 7 ) );
249  layersTree->setMinimumHeight( layersTree->maximumHeight() );
250  lblPreview->setMaximumWidth( layersTree->maximumHeight() );
251 
252  // setup icons
253  btnAddLayer->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
254  btnRemoveLayer->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
255  QIcon iconLock;
256  iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "locked.svg" ) ), QSize(), QIcon::Normal, QIcon::On );
257  iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "locked.svg" ) ), QSize(), QIcon::Active, QIcon::On );
258  iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "unlocked.svg" ) ), QSize(), QIcon::Normal, QIcon::Off );
259  iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "unlocked.svg" ) ), QSize(), QIcon::Active, QIcon::Off );
260  btnLock->setIcon( iconLock );
261  btnDuplicate->setIcon( QIcon( QgsApplication::iconPath( "mActionDuplicateLayer.svg" ) ) );
262  btnUp->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowUp.svg" ) ) );
263  btnDown->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowDown.svg" ) ) );
264 
265  mSymbolLayersModel = new QStandardItemModel( layersTree );
266  // Set the symbol
267  layersTree->setModel( mSymbolLayersModel );
268  layersTree->setHeaderHidden( true );
269 
270  //get first feature from layer for previews
271  if ( mVectorLayer )
272  {
273 #if 0 // this is too expensive to do for many providers. TODO revisit when support for connection timeouts is complete across all providers
274  // short timeout for request - it doesn't really matter if we don't get the feature, and this call is blocking UI
275  QgsFeatureIterator it = mVectorLayer->getFeatures( QgsFeatureRequest().setLimit( 1 ).setConnectionTimeout( 100 ) );
276  it.nextFeature( mPreviewFeature );
277 #endif
278  mPreviewExpressionContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( mVectorLayer ) );
279 #if 0
280  mPreviewExpressionContext.setFeature( mPreviewFeature );
281 #endif
282  }
283  else
284  {
285  mPreviewExpressionContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) );
286  }
287 
288  QItemSelectionModel *selModel = layersTree->selectionModel();
289  connect( selModel, &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
290 
291  loadSymbol( mSymbol, static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() ) );
292  updatePreview();
293 
294  connect( btnUp, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::moveLayerUp );
295  connect( btnDown, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::moveLayerDown );
296  connect( btnAddLayer, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::addLayer );
297  connect( btnRemoveLayer, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::removeLayer );
298  connect( btnLock, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::lockLayer );
299  connect( btnDuplicate, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::duplicateLayer );
301 
302  updateUi();
303 
304  // set symbol as active item in the tree
305  QModelIndex newIndex = layersTree->model()->index( 0, 0 );
306  layersTree->setCurrentIndex( newIndex );
307 
308  setPanelTitle( tr( "Symbol Selector" ) );
309 
310  // when a remote svg has been fetched, update the widget's previews
311  // this is required if the symbol utilizes remote svgs, and the current previews
312  // have been generated using the temporary "downloading" svg. In this case
313  // we require the preview to be regenerated to use the correct fetched
314  // svg
315  connect( QgsApplication::svgCache(), &QgsSvgCache::remoteSvgFetched, this, &QgsSymbolSelectorWidget::projectDataChanged );
316 
317  // when a remote image has been fetched, update the widget's previews
318  // this is required if the symbol utilizes remote images, and the current previews
319  // have been generated using the temporary "downloading" image. In this case
320  // we require the preview to be regenerated to use the correct fetched
321  // image
322  connect( QgsApplication::imageCache(), &QgsImageCache::remoteImageFetched, this, &QgsSymbolSelectorWidget::projectDataChanged );
323 
324  // if project color scheme changes, we need to redraw symbols - they may use project colors and accordingly
325  // need updating to reflect the new colors
326  connect( QgsProject::instance(), &QgsProject::projectColorsChanged, this, &QgsSymbolSelectorWidget::projectDataChanged );
327 
328  connect( QgsProject::instance(), static_cast < void ( QgsProject::* )( const QList<QgsMapLayer *>& layers ) > ( &QgsProject::layersWillBeRemoved ), this, &QgsSymbolSelectorWidget::layersAboutToBeRemoved );
329 }
330 
332 {
333  if ( !mAdvancedMenu )
334  {
335  mAdvancedMenu = new QMenu( this );
336  // Brute force method to activate the Advanced menu
337  layerChanged();
338  }
339  return mAdvancedMenu;
340 }
341 
343 {
344  mContext = context;
345 
346  if ( auto *lExpressionContext = mContext.expressionContext() )
347  {
348  mPreviewExpressionContext = *lExpressionContext;
349  if ( mVectorLayer )
350  mPreviewExpressionContext.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer ) );
351 
352  mPreviewExpressionContext.setFeature( mPreviewFeature );
353  }
354 
355  QWidget *widget = stackedWidget->currentWidget();
356  if ( QgsLayerPropertiesWidget *layerProp = qobject_cast< QgsLayerPropertiesWidget * >( widget ) )
357  layerProp->setContext( context );
358  else if ( QgsSymbolsListWidget *listWidget = qobject_cast< QgsSymbolsListWidget * >( widget ) )
359  listWidget->setContext( context );
360 
361  layerChanged();
362  updatePreview();
363 }
364 
366 {
367  return mContext;
368 }
369 
370 void QgsSymbolSelectorWidget::loadSymbol( QgsSymbol *symbol, SymbolLayerItem *parent )
371 {
372  if ( !symbol )
373  return;
374 
375  if ( !parent )
376  {
377  mSymbol = symbol;
378  mSymbolLayersModel->clear();
379  parent = static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() );
380  }
381 
382  SymbolLayerItem *symbolItem = new SymbolLayerItem( symbol );
383  QFont boldFont = symbolItem->font();
384  boldFont.setBold( true );
385  symbolItem->setFont( boldFont );
386  parent->appendRow( symbolItem );
387 
388  int count = symbol->symbolLayerCount();
389  for ( int i = count - 1; i >= 0; i-- )
390  {
391  SymbolLayerItem *layerItem = new SymbolLayerItem( symbol->symbolLayer( i ) );
392  layerItem->setEditable( false );
393  symbolItem->appendRow( layerItem );
394  if ( symbol->symbolLayer( i )->subSymbol() )
395  {
396  loadSymbol( symbol->symbolLayer( i )->subSymbol(), layerItem );
397  }
398  layersTree->setExpanded( layerItem->index(), true );
399  }
400  layersTree->setExpanded( symbolItem->index(), true );
401 
402  if ( mSymbol == symbol && !layersTree->currentIndex().isValid() )
403  {
404  // make sure root item for symbol is selected in tree
405  layersTree->setCurrentIndex( symbolItem->index() );
406  }
407 }
408 
409 void QgsSymbolSelectorWidget::reloadSymbol()
410 {
411  mSymbolLayersModel->clear();
412  loadSymbol( mSymbol, static_cast<SymbolLayerItem *>( mSymbolLayersModel->invisibleRootItem() ) );
413 }
414 
415 void QgsSymbolSelectorWidget::updateUi()
416 {
417  QModelIndex currentIdx = layersTree->currentIndex();
418  if ( !currentIdx.isValid() )
419  return;
420 
421  SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( currentIdx ) );
422  if ( !item->isLayer() )
423  {
424  btnUp->setEnabled( false );
425  btnDown->setEnabled( false );
426  btnRemoveLayer->setEnabled( false );
427  btnLock->setEnabled( false );
428  btnDuplicate->setEnabled( false );
429  return;
430  }
431 
432  int rowCount = item->parent()->rowCount();
433  int currentRow = item->row();
434 
435  btnUp->setEnabled( currentRow > 0 );
436  btnDown->setEnabled( currentRow < rowCount - 1 );
437  btnRemoveLayer->setEnabled( rowCount > 1 );
438  btnLock->setEnabled( true );
439  btnDuplicate->setEnabled( true );
440 }
441 
443 {
444  if ( !mSymbol )
445  return;
446 
447  std::unique_ptr< QgsSymbol > symbolClone( mSymbol->clone() );
448  QImage preview = symbolClone->bigSymbolPreviewImage( &mPreviewExpressionContext, Qgis::SymbolPreviewFlags() );
449  lblPreview->setPixmap( QPixmap::fromImage( preview ) );
450  // Hope this is a appropriate place
451  if ( !mBlockModified )
452  emit symbolModified();
453 }
454 
456 {
457  // get current layer item and update its icon
458  SymbolLayerItem *item = currentLayerItem();
459  if ( item )
460  item->updatePreview();
461  // update also preview of the whole symbol
462  updatePreview();
463 }
464 
465 SymbolLayerItem *QgsSymbolSelectorWidget::currentLayerItem()
466 {
467  QModelIndex idx = layersTree->currentIndex();
468  if ( !idx.isValid() )
469  return nullptr;
470 
471  SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
472  if ( !item->isLayer() )
473  return nullptr;
474 
475  return item;
476 }
477 
478 QgsSymbolLayer *QgsSymbolSelectorWidget::currentLayer()
479 {
480  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 item->layer();
487 
488  return nullptr;
489 }
490 
492 {
493  updateUi();
494 
495  SymbolLayerItem *currentItem = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( layersTree->currentIndex() ) );
496  if ( !currentItem )
497  return;
498 
499  if ( currentItem->isLayer() )
500  {
501  SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( currentItem->parent() );
502  mDataDefineRestorer.reset( new DataDefinedRestorer( parent->symbol(), currentItem->layer() ) );
503  QgsLayerPropertiesWidget *layerProp = new QgsLayerPropertiesWidget( currentItem->layer(), parent->symbol(), mVectorLayer );
504  layerProp->setDockMode( this->dockMode() );
505  layerProp->setContext( mContext );
506  setWidget( layerProp );
507  connect( layerProp, &QgsLayerPropertiesWidget::changed, mDataDefineRestorer.get(), &DataDefinedRestorer::restore );
509  // This connection when layer type is changed
511 
512  connectChildPanel( layerProp );
513  }
514  else
515  {
516  // then it must be a symbol
517  mDataDefineRestorer.reset();
519  currentItem->symbol()->setLayer( mVectorLayer );
521  // Now populate symbols of that type using the symbols list widget:
522  QgsSymbolsListWidget *symbolsList = new QgsSymbolsListWidget( currentItem->symbol(), mStyle, mAdvancedMenu, this, mVectorLayer );
523  symbolsList->setContext( mContext );
524 
525  setWidget( symbolsList );
527  }
528  updateLockButton();
529 }
530 
532 {
533  SymbolLayerItem *currentItem = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( layersTree->currentIndex() ) );
534  if ( !currentItem || currentItem->isLayer() )
535  return;
536  // disconnect to avoid recreating widget
537  disconnect( layersTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
538  if ( currentItem->parent() )
539  {
540  // it is a sub-symbol
541  QgsSymbol *symbol = currentItem->symbol();
542  SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( currentItem->parent() );
543  parent->removeRow( 0 );
544  loadSymbol( symbol, parent );
545  layersTree->setCurrentIndex( parent->child( 0 )->index() );
546  parent->updatePreview();
547  }
548  else
549  {
550  //it is the symbol itself
551  reloadSymbol();
552  QModelIndex newIndex = layersTree->model()->index( 0, 0 );
553  layersTree->setCurrentIndex( newIndex );
554  }
555  updatePreview();
556  // connect it back once things are set
557  connect( layersTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
558 }
559 
560 void QgsSymbolSelectorWidget::setWidget( QWidget *widget )
561 {
562  int index = stackedWidget->addWidget( widget );
563  stackedWidget->setCurrentIndex( index );
564  if ( mPresentWidget )
565  mPresentWidget->deleteLater();
566  mPresentWidget = widget;
567 }
568 
569 void QgsSymbolSelectorWidget::updateLockButton()
570 {
571  QgsSymbolLayer *layer = currentLayer();
572  if ( !layer )
573  return;
574  btnLock->setChecked( layer->isLocked() );
575 }
576 
578 {
579  QModelIndex idx = layersTree->currentIndex();
580  if ( !idx.isValid() )
581  return;
582 
583  int insertIdx = -1;
584  SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
585  if ( item->isLayer() )
586  {
587  insertIdx = item->row();
588  item = static_cast<SymbolLayerItem *>( item->parent() );
589  }
590 
591  QgsSymbol *parentSymbol = item->symbol();
592 
593  // save data-defined values at marker level
594  QgsProperty ddSize( parentSymbol->type() == Qgis::SymbolType::Marker
595  ? static_cast<QgsMarkerSymbol *>( parentSymbol )->dataDefinedSize()
596  : QgsProperty() );
597  QgsProperty ddAngle( parentSymbol->type() == Qgis::SymbolType::Marker
598  ? static_cast<QgsMarkerSymbol *>( parentSymbol )->dataDefinedAngle()
599  : QgsProperty() );
600  QgsProperty ddWidth( parentSymbol->type() == Qgis::SymbolType::Line
601  ? static_cast<QgsLineSymbol *>( parentSymbol )->dataDefinedWidth()
602  : QgsProperty() );
603 
605  if ( insertIdx == -1 )
606  parentSymbol->appendSymbolLayer( newLayer );
607  else
608  parentSymbol->insertSymbolLayer( item->rowCount() - insertIdx, newLayer );
609 
610  // restore data-defined values at marker level
611  if ( ddSize )
612  static_cast<QgsMarkerSymbol *>( parentSymbol )->setDataDefinedSize( ddSize );
613  if ( ddAngle )
614  static_cast<QgsMarkerSymbol *>( parentSymbol )->setDataDefinedAngle( ddAngle );
615  if ( ddWidth )
616  static_cast<QgsLineSymbol *>( parentSymbol )->setDataDefinedWidth( ddWidth );
617 
618  SymbolLayerItem *newLayerItem = new SymbolLayerItem( newLayer );
619  item->insertRow( insertIdx == -1 ? 0 : insertIdx, newLayerItem );
620  item->updatePreview();
621 
622  layersTree->setCurrentIndex( mSymbolLayersModel->indexFromItem( newLayerItem ) );
623  updateUi();
624  updatePreview();
625 }
626 
628 {
629  SymbolLayerItem *item = currentLayerItem();
630  int row = item->row();
631  SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( item->parent() );
632 
633  int layerIdx = parent->rowCount() - row - 1; // IMPORTANT
634  QgsSymbol *parentSymbol = parent->symbol();
635  QgsSymbolLayer *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
636 
637  parent->removeRow( row );
638  parent->updatePreview();
639 
640  QModelIndex newIdx = parent->child( 0 )->index();
641  layersTree->setCurrentIndex( newIdx );
642 
643  updateUi();
644  updatePreview();
645  //finally delete the removed layer pointer
646  delete tmpLayer;
647 }
648 
650 {
651  moveLayerByOffset( + 1 );
652 }
653 
655 {
656  moveLayerByOffset( -1 );
657 }
658 
659 void QgsSymbolSelectorWidget::moveLayerByOffset( int offset )
660 {
661  SymbolLayerItem *item = currentLayerItem();
662  if ( !item )
663  return;
664  int row = item->row();
665 
666  SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( item->parent() );
667  QgsSymbol *parentSymbol = parent->symbol();
668 
669  int layerIdx = parent->rowCount() - row - 1;
670  // switch layers
671  QgsSymbolLayer *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
672  parentSymbol->insertSymbolLayer( layerIdx - offset, tmpLayer );
673 
674  QList<QStandardItem *> rowItems = parent->takeRow( row );
675  parent->insertRows( row + offset, rowItems );
676  parent->updatePreview();
677 
678  QModelIndex newIdx = rowItems[ 0 ]->index();
679  layersTree->setCurrentIndex( newIdx );
680 
681  updatePreview();
682  updateUi();
683 }
684 
686 {
687  QgsSymbolLayer *layer = currentLayer();
688  if ( !layer )
689  return;
690  layer->setLocked( btnLock->isChecked() );
691  emit symbolModified();
692 }
693 
695 {
696  QModelIndex idx = layersTree->currentIndex();
697  if ( !idx.isValid() )
698  return;
699 
700  SymbolLayerItem *item = static_cast<SymbolLayerItem *>( mSymbolLayersModel->itemFromIndex( idx ) );
701  if ( !item->isLayer() )
702  return;
703 
704  QgsSymbolLayer *source = item->layer();
705 
706  int insertIdx = item->row();
707  item = static_cast<SymbolLayerItem *>( item->parent() );
708 
709  QgsSymbol *parentSymbol = item->symbol();
710 
711  QgsSymbolLayer *newLayer = source->clone();
712  if ( insertIdx == -1 )
713  parentSymbol->appendSymbolLayer( newLayer );
714  else
715  parentSymbol->insertSymbolLayer( item->rowCount() - insertIdx, newLayer );
716 
717  SymbolLayerItem *newLayerItem = new SymbolLayerItem( newLayer );
718  item->insertRow( insertIdx == -1 ? 0 : insertIdx, newLayerItem );
719  if ( newLayer->subSymbol() )
720  {
721  loadSymbol( newLayer->subSymbol(), newLayerItem );
722  layersTree->setExpanded( newLayerItem->index(), true );
723  }
724  item->updatePreview();
725 
726  layersTree->setCurrentIndex( mSymbolLayersModel->indexFromItem( newLayerItem ) );
727  updateUi();
728  updatePreview();
729 }
730 
732 {
733  SymbolLayerItem *item = currentLayerItem();
734  QgsSymbolLayer *layer = item->layer();
735 
736  if ( layer->subSymbol() )
737  {
738  item->removeRow( 0 );
739  }
740  // update symbol layer item
741  item->setLayer( newLayer );
742  // When it is a marker symbol
743  if ( newLayer->subSymbol() )
744  {
745  loadSymbol( newLayer->subSymbol(), item );
746  layersTree->setExpanded( item->index(), true );
747  }
748 
749  // Change the symbol at last to avoid deleting item's layer
750  QgsSymbol *symbol = static_cast<SymbolLayerItem *>( item->parent() )->symbol();
751  int layerIdx = item->parent()->rowCount() - item->row() - 1;
752  symbol->changeSymbolLayer( layerIdx, newLayer );
753 
754  item->updatePreview();
755  updatePreview();
756  // Important: This lets the layer have its own layer properties widget
757  layerChanged();
758 }
759 
760 QgsSymbolSelectorDialog::QgsSymbolSelectorDialog( QgsSymbol *symbol, QgsStyle *style, QgsVectorLayer *vl, QWidget *parent, bool embedded )
761  : QDialog( parent )
762 {
763  setLayout( new QVBoxLayout() );
764 
765  mSelectorWidget = new QgsSymbolSelectorWidget( symbol, style, vl, this );
766  mButtonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
767 
768  connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
769  connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
770  connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsSymbolSelectorDialog::showHelp );
771 
772  layout()->addWidget( mSelectorWidget );
773  layout()->addWidget( mButtonBox );
774 
775  connect( mSelectorWidget, &QgsPanelWidget::panelAccepted, this, &QDialog::reject );
776 
777  mSelectorWidget->setMinimumSize( 460, 560 );
778  setObjectName( QStringLiteral( "SymbolSelectorDialog" ) );
780 
781  // Can be embedded in renderer properties dialog
782  if ( embedded )
783  {
784  mButtonBox->hide();
785  layout()->setContentsMargins( 0, 0, 0, 0 );
786  }
787  else
788  {
789  setWindowTitle( tr( "Symbol Selector" ) );
790  }
791  mSelectorWidget->setDockMode( embedded );
792 }
793 
795 {
796  return mSelectorWidget->advancedMenu();
797 }
798 
800 {
801  mContext = context;
802 }
803 
805 {
806  return mContext;
807 }
808 
810 {
811  return mSelectorWidget->symbol();
812 }
813 
815 {
816  // Ignore the ESC key to avoid close the dialog without the properties window
817  if ( !isWindow() && e->key() == Qt::Key_Escape )
818  {
819  e->ignore();
820  }
821  else
822  {
823  QDialog::keyPressEvent( e );
824  }
825 }
826 
827 void QgsSymbolSelectorDialog::reloadSymbol()
828 {
829  mSelectorWidget->reloadSymbol();
830 }
831 
832 void QgsSymbolSelectorDialog::loadSymbol( QgsSymbol *symbol, SymbolLayerItem *parent )
833 {
834  mSelectorWidget->loadSymbol( symbol, parent );
835 }
836 
837 void QgsSymbolSelectorDialog::updateUi()
838 {
839  mSelectorWidget->updateUi();
840 }
841 
842 void QgsSymbolSelectorDialog::updateLockButton()
843 {
844  mSelectorWidget->updateLockButton();
845 }
846 
847 SymbolLayerItem *QgsSymbolSelectorDialog::currentLayerItem()
848 {
849  return mSelectorWidget->currentLayerItem();
850 }
851 
852 QgsSymbolLayer *QgsSymbolSelectorDialog::currentLayer()
853 {
854  return mSelectorWidget->currentLayer();
855 }
856 
857 void QgsSymbolSelectorDialog::moveLayerByOffset( int offset )
858 {
859  mSelectorWidget->moveLayerByOffset( offset );
860 }
861 
862 void QgsSymbolSelectorDialog::setWidget( QWidget *widget )
863 {
864  mSelectorWidget->setWidget( widget );
865 }
866 
868 {
869  mSelectorWidget->moveLayerDown();
870 }
871 
873 {
874  mSelectorWidget->moveLayerUp();
875 }
876 
878 {
879  mSelectorWidget->addLayer();
880 }
881 
883 {
884  mSelectorWidget->removeLayer();
885 }
886 
888 {
889  mSelectorWidget->lockLayer();
890 }
891 
893 {
894  mSelectorWidget->duplicateLayer();
895 }
896 
898 {
899  mSelectorWidget->layerChanged();
900 }
901 
903 {
904  mSelectorWidget->updateLayerPreview();
905 }
906 
908 {
909  mSelectorWidget->updatePreview();
910 }
911 
913 {
914  mSelectorWidget->symbolChanged();
915 }
916 
918 {
919  mSelectorWidget->changeLayer( layer );
920 }
921 
922 QDialogButtonBox *QgsSymbolSelectorDialog::buttonBox() const
923 {
924  return mButtonBox;
925 }
926 
927 void QgsSymbolSelectorDialog::showHelp()
928 {
929  QgsHelp::openHelp( QStringLiteral( "style_library/symbol_selector.html" ) );
930 }
931 
932 void QgsSymbolSelectorWidget::projectDataChanged()
933 {
934  mBlockModified = true;
935  symbolChanged();
936  updatePreview();
937  mBlockModified = false;
938 }
939 
940 void QgsSymbolSelectorWidget::layersAboutToBeRemoved( const QList<QgsMapLayer *> &layers )
941 {
942  if ( mVectorLayer && layers.contains( mVectorLayer ) )
943  {
944  disconnect( QgsProject::instance(), &QgsProject::projectColorsChanged, this, &QgsSymbolSelectorWidget::projectDataChanged );
945  }
946 }
@ Marker
Marker symbol.
@ Line
Line symbol.
@ Fill
Fill symbol.
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:416
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 QgsGui * instance()
Returns a pointer to the singleton instance.
Definition: qgsgui.cpp:65
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition: qgsgui.cpp:156
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
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).
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:99
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:467
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:232
void remoteSvgFetched(const QString &url)
Emitted when the cache has finished retrieving an SVG file from a remote url.
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())
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:38
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:420
bool appendSymbolLayer(QgsSymbolLayer *layer)
Appends a symbol layer at the end of the current symbol layer list.
Definition: qgssymbol.cpp:443
bool insertSymbolLayer(int index, QgsSymbolLayer *layer)
Inserts a symbol layer to specified index.
Definition: qgssymbol.cpp:430
bool changeSymbolLayer(int index, QgsSymbolLayer *layer)
Deletes the current layer at the specified index and replaces it with layer.
Definition: qgssymbol.cpp:473
QgsSymbolLayer * takeSymbolLayer(int index)
Removes a symbol layer from the list and returns a pointer to it.
Definition: qgssymbol.cpp:464
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition: qgssymbol.h:160
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:97
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
@ 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:1080
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:1079