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