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