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