QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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 ( parent() )
166  static_cast<SymbolLayerItem *>( parent() )->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 
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  mBlockModified = true;
316  symbolChanged();
317  updatePreview();
318  mBlockModified = false;
319  } );
321  {
322  // when a remote image has been fetched, update the widget's previews
323  // this is required if the symbol utilizes remote images, and the current previews
324  // have been generated using the temporary "downloading" image. In this case
325  // we require the preview to be regenerated to use the correct fetched
326  // image
327  mBlockModified = true;
328  symbolChanged();
329  updatePreview();
330  mBlockModified = false;
331  } );
332 
334  {
335  // if project color scheme changes, we need to redraw symbols - they may use project colors and accordingly
336  // need updating to reflect the new colors
337  mBlockModified = true;
338  symbolChanged();
339  updatePreview();
340  mBlockModified = false;
341  } );
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 ( mContext.expressionContext() )
360  {
361  mPreviewExpressionContext = *mContext.expressionContext();
362  if ( mVectorLayer )
363  mPreviewExpressionContext.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer ) );
364 
365  mPreviewExpressionContext.setFeature( mPreviewFeature );
366  }
367 
368  QWidget *widget = stackedWidget->currentWidget();
369  QgsLayerPropertiesWidget *layerProp = dynamic_cast< QgsLayerPropertiesWidget * >( widget );
370  QgsSymbolsListWidget *listWidget = dynamic_cast< QgsSymbolsListWidget * >( widget );
371 
372  if ( layerProp )
373  layerProp->setContext( context );
374  if ( listWidget )
375  listWidget->setContext( context );
376 
377  layerChanged();
378  updatePreview();
379 }
380 
382 {
383  return mContext;
384 }
385 
386 void QgsSymbolSelectorWidget::loadSymbol( QgsSymbol *symbol, SymbolLayerItem *parent )
387 {
388  if ( !symbol )
389  return;
390 
391  if ( !parent )
392  {
393  mSymbol = symbol;
394  model->clear();
395  parent = static_cast<SymbolLayerItem *>( model->invisibleRootItem() );
396  }
397 
398  SymbolLayerItem *symbolItem = new SymbolLayerItem( symbol );
399  QFont boldFont = symbolItem->font();
400  boldFont.setBold( true );
401  symbolItem->setFont( boldFont );
402  parent->appendRow( symbolItem );
403 
404  int count = symbol->symbolLayerCount();
405  for ( int i = count - 1; i >= 0; i-- )
406  {
407  SymbolLayerItem *layerItem = new SymbolLayerItem( symbol->symbolLayer( i ) );
408  layerItem->setEditable( false );
409  symbolItem->appendRow( layerItem );
410  if ( symbol->symbolLayer( i )->subSymbol() )
411  {
412  loadSymbol( symbol->symbolLayer( i )->subSymbol(), layerItem );
413  }
414  layersTree->setExpanded( layerItem->index(), true );
415  }
416  layersTree->setExpanded( symbolItem->index(), true );
417 }
418 
419 
420 void QgsSymbolSelectorWidget::reloadSymbol()
421 {
422  model->clear();
423  loadSymbol( mSymbol, static_cast<SymbolLayerItem *>( model->invisibleRootItem() ) );
424 }
425 
426 void QgsSymbolSelectorWidget::updateUi()
427 {
428  QModelIndex currentIdx = layersTree->currentIndex();
429  if ( !currentIdx.isValid() )
430  return;
431 
432  SymbolLayerItem *item = static_cast<SymbolLayerItem *>( model->itemFromIndex( currentIdx ) );
433  if ( !item->isLayer() )
434  {
435  btnUp->setEnabled( false );
436  btnDown->setEnabled( false );
437  btnRemoveLayer->setEnabled( false );
438  btnLock->setEnabled( false );
439  btnDuplicate->setEnabled( false );
440  return;
441  }
442 
443  int rowCount = item->parent()->rowCount();
444  int currentRow = item->row();
445 
446  btnUp->setEnabled( currentRow > 0 );
447  btnDown->setEnabled( currentRow < rowCount - 1 );
448  btnRemoveLayer->setEnabled( rowCount > 1 );
449  btnLock->setEnabled( true );
450  btnDuplicate->setEnabled( true );
451 }
452 
454 {
455  if ( !mSymbol )
456  return;
457 
458  std::unique_ptr< QgsSymbol > symbolClone( mSymbol->clone() );
459  QImage preview = symbolClone->bigSymbolPreviewImage( &mPreviewExpressionContext );
460  lblPreview->setPixmap( QPixmap::fromImage( preview ) );
461  // Hope this is a appropriate place
462  if ( !mBlockModified )
463  emit symbolModified();
464 }
465 
467 {
468  // get current layer item and update its icon
469  SymbolLayerItem *item = currentLayerItem();
470  if ( item )
471  item->updatePreview();
472  // update also preview of the whole symbol
473  updatePreview();
474 }
475 
476 SymbolLayerItem *QgsSymbolSelectorWidget::currentLayerItem()
477 {
478  QModelIndex idx = layersTree->currentIndex();
479  if ( !idx.isValid() )
480  return nullptr;
481 
482  SymbolLayerItem *item = static_cast<SymbolLayerItem *>( model->itemFromIndex( idx ) );
483  if ( !item->isLayer() )
484  return nullptr;
485 
486  return item;
487 }
488 
489 QgsSymbolLayer *QgsSymbolSelectorWidget::currentLayer()
490 {
491  QModelIndex idx = layersTree->currentIndex();
492  if ( !idx.isValid() )
493  return nullptr;
494 
495  SymbolLayerItem *item = static_cast<SymbolLayerItem *>( model->itemFromIndex( idx ) );
496  if ( item->isLayer() )
497  return item->layer();
498 
499  return nullptr;
500 }
501 
503 {
504  updateUi();
505 
506  SymbolLayerItem *currentItem = static_cast<SymbolLayerItem *>( model->itemFromIndex( layersTree->currentIndex() ) );
507  if ( !currentItem )
508  return;
509 
510  if ( currentItem->isLayer() )
511  {
512  SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( currentItem->parent() );
513  mDataDefineRestorer.reset( new DataDefinedRestorer( parent->symbol(), currentItem->layer() ) );
514  QgsLayerPropertiesWidget *layerProp = new QgsLayerPropertiesWidget( currentItem->layer(), parent->symbol(), mVectorLayer );
515  layerProp->setDockMode( this->dockMode() );
516  layerProp->setContext( mContext );
517  setWidget( layerProp );
518  connect( layerProp, &QgsLayerPropertiesWidget::changed, mDataDefineRestorer.get(), &DataDefinedRestorer::restore );
520  // This connection when layer type is changed
522 
523  connectChildPanel( layerProp );
524  }
525  else
526  {
527  mDataDefineRestorer.reset();
528  // then it must be a symbol
530  currentItem->symbol()->setLayer( mVectorLayer );
532  // Now populate symbols of that type using the symbols list widget:
533  QgsSymbolsListWidget *symbolsList = new QgsSymbolsListWidget( currentItem->symbol(), mStyle, mAdvancedMenu, this, mVectorLayer );
534  symbolsList->setContext( mContext );
535 
536  setWidget( symbolsList );
538  }
539  updateLockButton();
540 }
541 
543 {
544  SymbolLayerItem *currentItem = static_cast<SymbolLayerItem *>( model->itemFromIndex( layersTree->currentIndex() ) );
545  if ( !currentItem || currentItem->isLayer() )
546  return;
547  // disconnect to avoid recreating widget
548  disconnect( layersTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
549  if ( currentItem->parent() )
550  {
551  // it is a sub-symbol
552  QgsSymbol *symbol = currentItem->symbol();
553  SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( currentItem->parent() );
554  parent->removeRow( 0 );
555  loadSymbol( symbol, parent );
556  layersTree->setCurrentIndex( parent->child( 0 )->index() );
557  parent->updatePreview();
558  }
559  else
560  {
561  //it is the symbol itself
562  reloadSymbol();
563  QModelIndex newIndex = layersTree->model()->index( 0, 0 );
564  layersTree->setCurrentIndex( newIndex );
565  }
566  updatePreview();
567  // connect it back once things are set
568  connect( layersTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
569 }
570 
571 void QgsSymbolSelectorWidget::setWidget( QWidget *widget )
572 {
573  int index = stackedWidget->addWidget( widget );
574  stackedWidget->setCurrentIndex( index );
575  if ( mPresentWidget )
576  mPresentWidget->deleteLater();
577  mPresentWidget = widget;
578 }
579 
580 void QgsSymbolSelectorWidget::updateLockButton()
581 {
582  QgsSymbolLayer *layer = currentLayer();
583  if ( !layer )
584  return;
585  btnLock->setChecked( layer->isLocked() );
586 }
587 
589 {
590  QModelIndex idx = layersTree->currentIndex();
591  if ( !idx.isValid() )
592  return;
593 
594  int insertIdx = -1;
595  SymbolLayerItem *item = static_cast<SymbolLayerItem *>( model->itemFromIndex( idx ) );
596  if ( item->isLayer() )
597  {
598  insertIdx = item->row();
599  item = static_cast<SymbolLayerItem *>( item->parent() );
600  }
601 
602  QgsSymbol *parentSymbol = item->symbol();
603 
604  // save data-defined values at marker level
605  QgsProperty ddSize( parentSymbol->type() == QgsSymbol::Marker
606  ? static_cast<QgsMarkerSymbol *>( parentSymbol )->dataDefinedSize()
607  : QgsProperty() );
608  QgsProperty ddAngle( parentSymbol->type() == QgsSymbol::Marker
609  ? static_cast<QgsMarkerSymbol *>( parentSymbol )->dataDefinedAngle()
610  : QgsProperty() );
611  QgsProperty ddWidth( parentSymbol->type() == QgsSymbol::Line
612  ? static_cast<QgsLineSymbol *>( parentSymbol )->dataDefinedWidth()
613  : QgsProperty() );
614 
616  if ( insertIdx == -1 )
617  parentSymbol->appendSymbolLayer( newLayer );
618  else
619  parentSymbol->insertSymbolLayer( item->rowCount() - insertIdx, newLayer );
620 
621  // restore data-defined values at marker level
622  if ( ddSize )
623  static_cast<QgsMarkerSymbol *>( parentSymbol )->setDataDefinedSize( ddSize );
624  if ( ddAngle )
625  static_cast<QgsMarkerSymbol *>( parentSymbol )->setDataDefinedAngle( ddAngle );
626  if ( ddWidth )
627  static_cast<QgsLineSymbol *>( parentSymbol )->setDataDefinedWidth( ddWidth );
628 
629  SymbolLayerItem *newLayerItem = new SymbolLayerItem( newLayer );
630  item->insertRow( insertIdx == -1 ? 0 : insertIdx, newLayerItem );
631  item->updatePreview();
632 
633  layersTree->setCurrentIndex( model->indexFromItem( newLayerItem ) );
634  updateUi();
635  updatePreview();
636 }
637 
639 {
640  SymbolLayerItem *item = currentLayerItem();
641  int row = item->row();
642  SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( item->parent() );
643 
644  int layerIdx = parent->rowCount() - row - 1; // IMPORTANT
645  QgsSymbol *parentSymbol = parent->symbol();
646  QgsSymbolLayer *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
647 
648  parent->removeRow( row );
649  parent->updatePreview();
650 
651  QModelIndex newIdx = parent->child( 0 )->index();
652  layersTree->setCurrentIndex( newIdx );
653 
654  updateUi();
655  updatePreview();
656  //finally delete the removed layer pointer
657  delete tmpLayer;
658 }
659 
661 {
662  moveLayerByOffset( + 1 );
663 }
664 
666 {
667  moveLayerByOffset( -1 );
668 }
669 
670 void QgsSymbolSelectorWidget::moveLayerByOffset( int offset )
671 {
672  SymbolLayerItem *item = currentLayerItem();
673  if ( !item )
674  return;
675  int row = item->row();
676 
677  SymbolLayerItem *parent = static_cast<SymbolLayerItem *>( item->parent() );
678  QgsSymbol *parentSymbol = parent->symbol();
679 
680  int layerIdx = parent->rowCount() - row - 1;
681  // switch layers
682  QgsSymbolLayer *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
683  parentSymbol->insertSymbolLayer( layerIdx - offset, tmpLayer );
684 
685  QList<QStandardItem *> rowItems = parent->takeRow( row );
686  parent->insertRows( row + offset, rowItems );
687  parent->updatePreview();
688 
689  QModelIndex newIdx = rowItems[ 0 ]->index();
690  layersTree->setCurrentIndex( newIdx );
691 
692  updatePreview();
693  updateUi();
694 }
695 
697 {
698  QgsSymbolLayer *layer = currentLayer();
699  if ( !layer )
700  return;
701  layer->setLocked( btnLock->isChecked() );
702  emit symbolModified();
703 }
704 
706 {
707  QModelIndex idx = layersTree->currentIndex();
708  if ( !idx.isValid() )
709  return;
710 
711  SymbolLayerItem *item = static_cast<SymbolLayerItem *>( model->itemFromIndex( idx ) );
712  if ( !item->isLayer() )
713  return;
714 
715  QgsSymbolLayer *source = item->layer();
716 
717  int insertIdx = item->row();
718  item = static_cast<SymbolLayerItem *>( item->parent() );
719 
720  QgsSymbol *parentSymbol = item->symbol();
721 
722  QgsSymbolLayer *newLayer = source->clone();
723  if ( insertIdx == -1 )
724  parentSymbol->appendSymbolLayer( newLayer );
725  else
726  parentSymbol->insertSymbolLayer( item->rowCount() - insertIdx, newLayer );
727 
728  SymbolLayerItem *newLayerItem = new SymbolLayerItem( newLayer );
729  item->insertRow( insertIdx == -1 ? 0 : insertIdx, newLayerItem );
730  if ( newLayer->subSymbol() )
731  {
732  loadSymbol( newLayer->subSymbol(), newLayerItem );
733  layersTree->setExpanded( newLayerItem->index(), true );
734  }
735  item->updatePreview();
736 
737  layersTree->setCurrentIndex( model->indexFromItem( newLayerItem ) );
738  updateUi();
739  updatePreview();
740 }
741 
743 {
744  SymbolLayerItem *item = currentLayerItem();
745  QgsSymbolLayer *layer = item->layer();
746 
747  if ( layer->subSymbol() )
748  {
749  item->removeRow( 0 );
750  }
751  // update symbol layer item
752  item->setLayer( newLayer );
753  // When it is a marker symbol
754  if ( newLayer->subSymbol() )
755  {
756  loadSymbol( newLayer->subSymbol(), item );
757  layersTree->setExpanded( item->index(), true );
758  }
759 
760  // Change the symbol at last to avoid deleting item's layer
761  QgsSymbol *symbol = static_cast<SymbolLayerItem *>( item->parent() )->symbol();
762  int layerIdx = item->parent()->rowCount() - item->row() - 1;
763  symbol->changeSymbolLayer( layerIdx, newLayer );
764 
765  item->updatePreview();
766  updatePreview();
767  // Important: This lets the layer have its own layer properties widget
768  layerChanged();
769 }
770 
771 QgsSymbolSelectorDialog::QgsSymbolSelectorDialog( QgsSymbol *symbol, QgsStyle *style, QgsVectorLayer *vl, QWidget *parent, bool embedded )
772  : QDialog( parent )
773 {
774  setLayout( new QVBoxLayout() );
775  mSelectorWidget = new QgsSymbolSelectorWidget( symbol, style, vl, this );
776  mButtonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
777 
778  connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
779  connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
780  connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsSymbolSelectorDialog::showHelp );
781 
782  layout()->addWidget( mSelectorWidget );
783  layout()->addWidget( mButtonBox );
784 
785  connect( mSelectorWidget, &QgsPanelWidget::panelAccepted, this, &QDialog::reject );
786 
787  mSelectorWidget->setMinimumSize( 460, 560 );
788  setObjectName( QStringLiteral( "SymbolSelectorDialog" ) );
790 
791  // Can be embedded in renderer properties dialog
792  if ( embedded )
793  {
794  mButtonBox->hide();
795  layout()->setContentsMargins( 0, 0, 0, 0 );
796  }
797  else
798  {
799  setWindowTitle( tr( "Symbol Selector" ) );
800  }
801  mSelectorWidget->setDockMode( embedded );
802 }
803 
805 {
806  return mSelectorWidget->advancedMenu();
807 }
808 
810 {
811  mContext = context;
812 }
813 
815 {
816  return mContext;
817 }
818 
820 {
821  return mSelectorWidget->symbol();
822 }
823 
825 {
826  // Ignore the ESC key to avoid close the dialog without the properties window
827  if ( !isWindow() && e->key() == Qt::Key_Escape )
828  {
829  e->ignore();
830  }
831  else
832  {
833  QDialog::keyPressEvent( e );
834  }
835 }
836 
837 void QgsSymbolSelectorDialog::reloadSymbol()
838 {
839  mSelectorWidget->reloadSymbol();
840 }
841 
842 void QgsSymbolSelectorDialog::loadSymbol( QgsSymbol *symbol, SymbolLayerItem *parent )
843 {
844  mSelectorWidget->loadSymbol( symbol, parent );
845 }
846 
847 void QgsSymbolSelectorDialog::updateUi()
848 {
849  mSelectorWidget->updateUi();
850 }
851 
852 void QgsSymbolSelectorDialog::updateLockButton()
853 {
854  mSelectorWidget->updateLockButton();
855 }
856 
857 SymbolLayerItem *QgsSymbolSelectorDialog::currentLayerItem()
858 {
859  return mSelectorWidget->currentLayerItem();
860 }
861 
862 QgsSymbolLayer *QgsSymbolSelectorDialog::currentLayer()
863 {
864  return mSelectorWidget->currentLayer();
865 }
866 
867 void QgsSymbolSelectorDialog::moveLayerByOffset( int offset )
868 {
869  mSelectorWidget->moveLayerByOffset( offset );
870 }
871 
872 void QgsSymbolSelectorDialog::setWidget( QWidget *widget )
873 {
874  mSelectorWidget->setWidget( widget );
875 }
876 
878 {
879  mSelectorWidget->moveLayerDown();
880 }
881 
883 {
884  mSelectorWidget->moveLayerUp();
885 }
886 
888 {
889  mSelectorWidget->addLayer();
890 }
891 
893 {
894  mSelectorWidget->removeLayer();
895 }
896 
898 {
899  mSelectorWidget->lockLayer();
900 }
901 
903 {
904  mSelectorWidget->duplicateLayer();
905 }
906 
908 {
909  mSelectorWidget->layerChanged();
910 }
911 
913 {
914  mSelectorWidget->updateLayerPreview();
915 }
916 
918 {
919  mSelectorWidget->updatePreview();
920 }
921 
923 {
924  mSelectorWidget->symbolChanged();
925 }
926 
928 {
929  mSelectorWidget->changeLayer( layer );
930 }
931 
932 QDialogButtonBox *QgsSymbolSelectorDialog::buttonBox() const
933 {
934  return mButtonBox;
935 }
936 
937 void QgsSymbolSelectorDialog::showHelp()
938 {
939  QgsHelp::openHelp( QStringLiteral( "style_library/symbol_selector.html" ) );
940 }
void duplicateLayer()
Duplicates the current symbol layer and places the duplicated layer above the current symbol layer...
bool insertSymbolLayer(int index, QgsSymbolLayer *layer)
Inserts a symbol layer to specified index.
Definition: qgssymbol.cpp:372
static QgsSymbolLayerRegistry * symbolLayerRegistry()
Returns the application&#39;s symbol layer registry, used for managing symbol layers. ...
static QgsSvgCache * svgCache()
Returns the application&#39;s SVG cache, used for caching SVG images and handling parameter replacement w...
Wrapper for iterator of features from vector data provider or vector layer.
void connectChildPanel(QgsPanelWidget *panel)
Connect the given sub panel widgets showPanel signals to this current panels main showPanel event to ...
static QIcon symbolLayerPreviewIcon(const QgsSymbolLayer *layer, QgsUnitTypes::RenderUnit u, QSize size, const QgsMapUnitScale &scale=QgsMapUnitScale())
Draws a symbol layer preview to an icon.
void setLocked(bool locked)
void removeLayer()
Remove the current active symbol layer.
bool dockMode()
Returns the dock mode state.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:62
virtual QgsSymbol * subSymbol()
Returns the symbol&#39;s sub symbol, if present.
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:182
QgsSymbol * symbol()
Returns the symbol that is currently active in the widget.
bool appendSymbolLayer(QgsSymbolLayer *layer)
Appends a symbol layer at the end of the current symbol layer list.
Definition: qgssymbol.cpp:385
QgsSymbolSelectorDialog(QgsSymbol *symbol, QgsStyle *style, QgsVectorLayer *vl, QWidget *parent=nullptr, bool embedded=false)
Constructor for QgsSymbolSelectorDialog.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void updateLayerPreview()
Update the single symbol layer preview in the widget.
static QgsImageCache * imageCache()
Returns the application&#39;s image cache, used for caching resampled versions of raster images...
static QgsSymbolLayer * defaultSymbolLayer(QgsSymbol::SymbolType type)
create a new instance of symbol layer for specified symbol type with default settings ...
void lockLayer()
Lock the current active symbol layer.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
static QIcon symbolPreviewIcon(const QgsSymbol *symbol, QSize size, int padding=0)
Returns an icon preview for a color ramp.
void setDockMode(bool dockMode) override
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs...
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:731
void symbolModified()
Emitted when a symbol is modified in the widget.
void projectColorsChanged()
Emitted whenever the project&#39;s color scheme has been changed.
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition: qgssymbol.h:182
Base class for any widget that can be shown as a inline panel.
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
bool isLocked() const
Line symbol.
Definition: qgssymbol.h:87
void symbolChanged()
Slot to update tree when a new symbol from style.
static QgsGui * instance()
Returns a pointer to the singleton instance.
Definition: qgsgui.cpp:62
void layerChanged()
Called when the layer changes in the widget.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:895
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgssymbol.h:1095
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
virtual void setDockMode(bool dockMode)
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs...
void addLayer()
Add a symbol layer to the bottom of the stack.
QgsSymbolWidgetContext context() const
Returns the context in which the symbol widget is shown, e.g., the associated map canvas and expressi...
QgsSymbolLayerAbstractMetadata * symbolLayerMetadata(const QString &name) const
Returns metadata for specified symbol layer. Returns nullptr if not found.
Contains settings which reflect the context in which a symbol (or renderer) widget is shown...
virtual QgsSymbolLayer * clone() const =0
Shall be reimplemented by subclasses to create a deep copy of the instance.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
void symbolChanged()
Slot to update tree when a new symbol from style.
QgsSymbol::SymbolType type() const
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer&#39;s project and layer.
Symbol selector widget that can be used to select and build a symbol.
void moveLayerUp()
Move the active symbol layer up.
QMenu * advancedMenu()
Returns menu for "advanced" button - create it if doesn&#39;t exist and show the advanced button...
A store for object properties.
Definition: qgsproperty.h:229
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:362
void widgetChanged()
Emitted when the widget state changes.
void changeLayer(QgsSymbolLayer *layer)
alters tree and sets proper widget when Layer Type is changed
QMenu * advancedMenu()
Returns menu for "advanced" button - create it if doesn&#39;t exist and show the advanced button...
void remoteImageFetched(const QString &url)
Emitted when the cache has finished retrieving an image file from a remote url.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:732
bool changeSymbolLayer(int index, QgsSymbolLayer *layer)
Deletes the current layer at the specified index and replaces it with layer.
Definition: qgssymbol.cpp:415
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
Marker symbol.
Definition: qgssymbol.h:86
QgsExpressionContext * expressionContext() const
Returns the expression context used for the widget, if set.
Fill symbol.
Definition: qgssymbol.h:88
Abstract base class for marker symbol layers.
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
SymbolType type() const
Returns the symbol&#39;s type.
Definition: qgssymbol.h:121
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
void duplicateLayer()
Duplicates the current symbol layer and places the duplicated layer above the current symbol layer...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:450
void changeLayer(QgsSymbolLayer *layer)
alters tree and sets proper widget when Layer Type is changed
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:133
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
void changeLayer(QgsSymbolLayer *)
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsSymbolLayer * takeSymbolLayer(int index)
Removes a symbol layer from the list and returns a pointer to it.
Definition: qgssymbol.cpp:406
void moveLayerDown()
Move the active symbol layer down.
void remoteSvgFetched(const QString &url)
Emitted when the cache has finished retrieving an SVG file from a remote url.
void appendScopes(const QList< QgsExpressionContextScope *> &scopes)
Appends a list of scopes to the end of the context.
QgsSymbol * symbol()
Returns the symbol that is currently active in the widget.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
void keyPressEvent(QKeyEvent *e) override
QDialogButtonBox * buttonBox() const
Returns a reference to the dialog&#39;s button box.
QgsSymbolSelectorWidget(QgsSymbol *symbol, QgsStyle *style, QgsVectorLayer *vl, QWidget *parent=nullptr)
Symbol selector widget that can be used to select and build a symbol.
Represents a vector layer which manages a vector based data sets.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
void loadSymbol(QgsSymbol *symbol, SymbolLayerItem *parent=nullptr)
Loads the given symbol into the widget.
void updatePreview()
Update the preview of the whole symbol in the interface.
void loadSymbol(QgsSymbol *symbol, SymbolLayerItem *parent=nullptr)
Loads the given symbol into the widget.
QgsSymbolWidgetContext context() const
Returns the context in which the symbol widget is shown, e.g., the associated map canvas and expressi...