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