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