QGIS API Documentation  3.6.0-Noosa (5873452)
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 
248  // setup icons
249  btnAddLayer->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
250  btnRemoveLayer->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
251  QIcon iconLock;
252  iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "locked.svg" ) ), QSize(), QIcon::Normal, QIcon::On );
253  iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "locked.svg" ) ), QSize(), QIcon::Active, QIcon::On );
254  iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "unlocked.svg" ) ), QSize(), QIcon::Normal, QIcon::Off );
255  iconLock.addFile( QgsApplication::iconPath( QStringLiteral( "unlocked.svg" ) ), QSize(), QIcon::Active, QIcon::Off );
256  btnLock->setIcon( iconLock );
257  btnDuplicate->setIcon( QIcon( QgsApplication::iconPath( "mActionDuplicateLayer.svg" ) ) );
258  btnUp->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowUp.svg" ) ) );
259  btnDown->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowDown.svg" ) ) );
260 
261  model = new QStandardItemModel( layersTree );
262  // Set the symbol
263  layersTree->setModel( model );
264  layersTree->setHeaderHidden( true );
265 
266  //get first feature from layer for previews
267  if ( mVectorLayer )
268  {
269 #if 0 // this is too expensive to do for many providers. TODO revisit when support for connection timeouts is complete across all providers
270  // short timeout for request - it doesn't really matter if we don't get the feature, and this call is blocking UI
271  QgsFeatureIterator it = mVectorLayer->getFeatures( QgsFeatureRequest().setLimit( 1 ).setConnectionTimeout( 100 ) );
272  it.nextFeature( mPreviewFeature );
273 #endif
275 #if 0
276  mPreviewExpressionContext.setFeature( mPreviewFeature );
277 #endif
278  }
279  else
280  {
281  mPreviewExpressionContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) );
282  }
283 
284  QItemSelectionModel *selModel = layersTree->selectionModel();
285  connect( selModel, &QItemSelectionModel::currentChanged, this, &QgsSymbolSelectorWidget::layerChanged );
286 
287  loadSymbol( symbol, static_cast<SymbolLayerItem *>( model->invisibleRootItem() ) );
288  updatePreview();
289 
290  connect( btnUp, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::moveLayerUp );
291  connect( btnDown, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::moveLayerDown );
292  connect( btnAddLayer, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::addLayer );
293  connect( btnRemoveLayer, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::removeLayer );
294  connect( btnLock, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::lockLayer );
295  connect( btnDuplicate, &QAbstractButton::clicked, this, &QgsSymbolSelectorWidget::duplicateLayer );
297 
298  updateUi();
299 
300  // set symbol as active item in the tree
301  QModelIndex newIndex = layersTree->model()->index( 0, 0 );
302  layersTree->setCurrentIndex( newIndex );
303 
304  setPanelTitle( tr( "Symbol Selector" ) );
305 
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  mBlockModified = true;
314  symbolChanged();
315  updatePreview();
316  mBlockModified = false;
317  } );
319  {
320  // when a remote image has been fetched, update the widget's previews
321  // this is required if the symbol utilizes remote images, and the current previews
322  // have been generated using the temporary "downloading" image. In this case
323  // we require the preview to be regenerated to use the correct fetched
324  // image
325  mBlockModified = true;
326  symbolChanged();
327  updatePreview();
328  mBlockModified = false;
329  } );
330 
332  {
333  // if project color scheme changes, we need to redraw symbols - they may use project colors and accordingly
334  // need updating to reflect the new colors
335  mBlockModified = true;
336  symbolChanged();
337  updatePreview();
338  mBlockModified = false;
339  } );
340 }
341 
343 {
344  if ( !mAdvancedMenu )
345  {
346  mAdvancedMenu = new QMenu( this );
347  // Brute force method to activate the Advanced menu
348  layerChanged();
349  }
350  return mAdvancedMenu;
351 }
352 
354 {
355  mContext = context;
356 
357  if ( mContext.expressionContext() )
358  {
359  mPreviewExpressionContext = *mContext.expressionContext();
360  if ( mVectorLayer )
361  mPreviewExpressionContext.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer ) );
362 
363  mPreviewExpressionContext.setFeature( mPreviewFeature );
364  }
365 
366  QWidget *widget = stackedWidget->currentWidget();
367  QgsLayerPropertiesWidget *layerProp = dynamic_cast< QgsLayerPropertiesWidget * >( widget );
368  QgsSymbolsListWidget *listWidget = dynamic_cast< QgsSymbolsListWidget * >( widget );
369 
370  if ( layerProp )
371  layerProp->setContext( context );
372  if ( listWidget )
373  listWidget->setContext( context );
374 
375  layerChanged();
376  updatePreview();
377 }
378 
380 {
381  return mContext;
382 }
383 
384 void QgsSymbolSelectorWidget::loadSymbol( QgsSymbol *symbol, SymbolLayerItem *parent )
385 {
386  SymbolLayerItem *symbolItem = new SymbolLayerItem( symbol );
387  QFont boldFont = symbolItem->font();
388  boldFont.setBold( true );
389  symbolItem->setFont( boldFont );
390  parent->appendRow( symbolItem );
391 
392  int count = symbol->symbolLayerCount();
393  for ( int i = count - 1; i >= 0; i-- )
394  {
395  SymbolLayerItem *layerItem = new SymbolLayerItem( symbol->symbolLayer( i ) );
396  layerItem->setEditable( false );
397  symbolItem->appendRow( layerItem );
398  if ( symbol->symbolLayer( i )->subSymbol() )
399  {
400  loadSymbol( symbol->symbolLayer( i )->subSymbol(), layerItem );
401  }
402  layersTree->setExpanded( layerItem->index(), true );
403  }
404  layersTree->setExpanded( symbolItem->index(), true );
405 }
406 
407 
409 {
410  model->clear();
411  loadSymbol( mSymbol, static_cast<SymbolLayerItem *>( model->invisibleRootItem() ) );
412 }
413 
415 {
416  QModelIndex currentIdx = layersTree->currentIndex();
417  if ( !currentIdx.isValid() )
418  return;
419 
420  SymbolLayerItem *item = static_cast<SymbolLayerItem *>( model->itemFromIndex( currentIdx ) );
421  if ( !item->isLayer() )
422  {
423  btnUp->setEnabled( false );
424  btnDown->setEnabled( false );
425  btnRemoveLayer->setEnabled( false );
426  btnLock->setEnabled( false );
427  btnDuplicate->setEnabled( false );
428  return;
429  }
430 
431  int rowCount = item->parent()->rowCount();
432  int currentRow = item->row();
433 
434  btnUp->setEnabled( currentRow > 0 );
435  btnDown->setEnabled( currentRow < rowCount - 1 );
436  btnRemoveLayer->setEnabled( rowCount > 1 );
437  btnLock->setEnabled( true );
438  btnDuplicate->setEnabled( true );
439 }
440 
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 
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 
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  }
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  loadSymbol();
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 
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 
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  mSelectorWidget = new QgsSymbolSelectorWidget( symbol, style, vl, this );
761  mButtonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
762 
763  connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
764  connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
765  connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsSymbolSelectorDialog::showHelp );
766 
767  layout()->addWidget( mSelectorWidget );
768  layout()->addWidget( mButtonBox );
769 
770  QgsSettings settings;
771  restoreGeometry( settings.value( QStringLiteral( "Windows/SymbolSelectorWidget/geometry" ) ).toByteArray() );
772 
773  // can be embedded in renderer properties dialog
774  if ( embedded )
775  {
776  mButtonBox->hide();
777  layout()->setContentsMargins( 0, 0, 0, 0 );
778  }
779  else
780  {
781  setWindowTitle( tr( "Symbol Selector" ) );
782  }
783  mSelectorWidget->setDockMode( embedded );
784 }
785 
787 {
788  QgsSettings settings;
789  settings.setValue( QStringLiteral( "Windows/SymbolSelectorWidget/geometry" ), saveGeometry() );
790 }
791 
793 {
794  return mSelectorWidget->advancedMenu();
795 }
796 
798 {
799  mContext = context;
800 }
801 
803 {
804  return mContext;
805 }
806 
808 {
809  return mSelectorWidget->symbol();
810 }
811 
813 {
814  // Ignore the ESC key to avoid close the dialog without the properties window
815  if ( !isWindow() && e->key() == Qt::Key_Escape )
816  {
817  e->ignore();
818  }
819  else
820  {
821  QDialog::keyPressEvent( e );
822  }
823 }
824 
826 {
827  mSelectorWidget->loadSymbol();
828 }
829 
830 void QgsSymbolSelectorDialog::loadSymbol( QgsSymbol *symbol, SymbolLayerItem *parent )
831 {
832  mSelectorWidget->loadSymbol( symbol, parent );
833 }
834 
836 {
837  mSelectorWidget->updateUi();
838 }
839 
841 {
842  mSelectorWidget->updateLockButton();
843 }
844 
846 {
847  return mSelectorWidget->currentLayerItem();
848 }
849 
851 {
852  return mSelectorWidget->currentLayer();
853 }
854 
856 {
857  mSelectorWidget->moveLayerByOffset( offset );
858 }
859 
860 void QgsSymbolSelectorDialog::setWidget( QWidget *widget )
861 {
862  mSelectorWidget->setWidget( widget );
863 }
864 
866 {
867  mSelectorWidget->moveLayerDown();
868 }
869 
871 {
872  mSelectorWidget->moveLayerUp();
873 }
874 
876 {
877  mSelectorWidget->addLayer();
878 }
879 
881 {
882  mSelectorWidget->removeLayer();
883 }
884 
886 {
887  mSelectorWidget->lockLayer();
888 }
889 
891 {
892  mSelectorWidget->duplicateLayer();
893 }
894 
896 {
897  mSelectorWidget->layerChanged();
898 }
899 
901 {
902  mSelectorWidget->updateLayerPreview();
903 }
904 
906 {
907  mSelectorWidget->updatePreview();
908 }
909 
911 {
912  mSelectorWidget->symbolChanged();
913 }
914 
916 {
917  mSelectorWidget->changeLayer( layer );
918 }
919 
920 void QgsSymbolSelectorDialog::showHelp()
921 {
922  QgsHelp::openHelp( QStringLiteral( "working_with_vector/style_library.html#the-symbol-selector" ) );
923 }
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:347
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:360
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:732
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgssymbol.h:920
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 NULL 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:342
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:390
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:430
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
Query 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:381
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...