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