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