1 /***************************************************************************
2 qgsrendererpropertiesdialog.cpp
3 ---------------------
4 begin : December 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 #include "qgsrendererpropertiesdialog.h"
16
17 #include "qgsrenderer.h"
18 #include "qgsrendererregistry.h"
19
20 #include "qgsrendererwidget.h"
21 #include "qgssinglesymbolrendererwidget.h"
22 #include "qgscategorizedsymbolrendererwidget.h"
23 #include "qgsgraduatedsymbolrendererwidget.h"
24 #include "qgsrulebasedrendererwidget.h"
25 #include "qgspointdisplacementrendererwidget.h"
26 #include "qgspointclusterrendererwidget.h"
27 #include "qgsinvertedpolygonrendererwidget.h"
28 #include "qgsheatmaprendererwidget.h"
29 #include "qgs25drendererwidget.h"
30 #include "qgsnullsymbolrendererwidget.h"
31 #include "qgspanelwidget.h"
32 #include "qgspainteffect.h"
33
34 #include "qgsorderbydialog.h"
35 #include "qgsapplication.h"
36 #include "qgslogger.h"
37 #include "qgsvectorlayer.h"
38
39 #include <QKeyEvent>
40 #include <QMessageBox>
41
_initRenderer(const QString & name,QgsRendererWidgetFunc f,const QString & iconName=QString ())42 static bool _initRenderer( const QString &name, QgsRendererWidgetFunc f, const QString &iconName = QString() )
43 {
44 QgsRendererRegistry *reg = QgsApplication::rendererRegistry();
45 QgsRendererAbstractMetadata *am = reg->rendererMetadata( name );
46 if ( !am )
47 return false;
48 QgsRendererMetadata *m = dynamic_cast<QgsRendererMetadata *>( am );
49 if ( !m )
50 return false;
51
52 m->setWidgetFunction( f );
53
54 if ( !iconName.isEmpty() )
55 {
56 m->setIcon( QgsApplication::getThemeIcon( iconName ) );
57 }
58
59 QgsDebugMsgLevel( "Set for " + name, 2 );
60 return true;
61 }
62
_initRendererWidgetFunctions()63 static void _initRendererWidgetFunctions()
64 {
65 static bool sInitialized = false;
66 if ( sInitialized )
67 return;
68
69 _initRenderer( QStringLiteral( "singleSymbol" ), QgsSingleSymbolRendererWidget::create, QStringLiteral( "rendererSingleSymbol.svg" ) );
70 _initRenderer( QStringLiteral( "categorizedSymbol" ), QgsCategorizedSymbolRendererWidget::create, QStringLiteral( "rendererCategorizedSymbol.svg" ) );
71 _initRenderer( QStringLiteral( "graduatedSymbol" ), QgsGraduatedSymbolRendererWidget::create, QStringLiteral( "rendererGraduatedSymbol.svg" ) );
72 _initRenderer( QStringLiteral( "RuleRenderer" ), QgsRuleBasedRendererWidget::create, QStringLiteral( "rendererRuleBasedSymbol.svg" ) );
73 _initRenderer( QStringLiteral( "pointDisplacement" ), QgsPointDisplacementRendererWidget::create, QStringLiteral( "rendererPointDisplacementSymbol.svg" ) );
74 _initRenderer( QStringLiteral( "pointCluster" ), QgsPointClusterRendererWidget::create, QStringLiteral( "rendererPointClusterSymbol.svg" ) );
75 _initRenderer( QStringLiteral( "invertedPolygonRenderer" ), QgsInvertedPolygonRendererWidget::create, QStringLiteral( "rendererInvertedSymbol.svg" ) );
76 _initRenderer( QStringLiteral( "heatmapRenderer" ), QgsHeatmapRendererWidget::create, QStringLiteral( "rendererHeatmapSymbol.svg" ) );
77 _initRenderer( QStringLiteral( "25dRenderer" ), Qgs25DRendererWidget::create, QStringLiteral( "renderer25dSymbol.svg" ) );
78 _initRenderer( QStringLiteral( "nullSymbol" ), QgsNullSymbolRendererWidget::create, QStringLiteral( "rendererNullSymbol.svg" ) );
79 sInitialized = true;
80 }
81
QgsRendererPropertiesDialog(QgsVectorLayer * layer,QgsStyle * style,bool embedded,QWidget * parent)82 QgsRendererPropertiesDialog::QgsRendererPropertiesDialog( QgsVectorLayer *layer, QgsStyle *style, bool embedded, QWidget *parent )
83 : QDialog( parent )
84 , mLayer( layer )
85 , mStyle( style )
86
87 {
88 setupUi( this );
89 QgsGui::enableAutoGeometryRestore( this );
90 mLayerRenderingGroupBox->setSettingGroup( QStringLiteral( "layerRenderingGroupBox" ) );
91
92 // can be embedded in vector layer properties
93 if ( embedded )
94 {
95 buttonBox->hide();
96 layout()->setContentsMargins( 0, 0, 0, 0 );
97 }
98
99 // initialize registry's widget functions
100 _initRendererWidgetFunctions();
101
102 QgsRendererRegistry *reg = QgsApplication::rendererRegistry();
103 QStringList renderers = reg->renderersList( mLayer );
104 const auto constRenderers = renderers;
105 for ( const QString &name : constRenderers )
106 {
107 QgsRendererAbstractMetadata *m = reg->rendererMetadata( name );
108 cboRenderers->addItem( m->icon(), m->visibleName(), name );
109 }
110
111 cboRenderers->setCurrentIndex( -1 ); // set no current renderer
112
113 connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsRendererPropertiesDialog::onOK );
114
115 // connect layer opacity slider and spin box
116 connect( cboRenderers, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRendererPropertiesDialog::rendererChanged );
117 connect( checkboxEnableOrderBy, &QAbstractButton::toggled, btnOrderBy, &QWidget::setEnabled );
118 connect( btnOrderBy, &QAbstractButton::clicked, this, &QgsRendererPropertiesDialog::showOrderByDialog );
119
120 syncToLayer();
121
122 QList<QWidget *> widgets;
123 widgets << mOpacityWidget
124 << cboRenderers
125 << checkboxEnableOrderBy
126 << mBlendModeComboBox
127 << mFeatureBlendComboBox
128 << mEffectWidget;
129
130 connectValueChanged( widgets, SIGNAL( widgetChanged() ) );
131 connect( mEffectWidget, &QgsPanelWidget::showPanel, this, &QgsRendererPropertiesDialog::openPanel );
132 }
133
connectValueChanged(const QList<QWidget * > & widgets,const char * slot)134 void QgsRendererPropertiesDialog::connectValueChanged( const QList<QWidget *> &widgets, const char *slot )
135 {
136 for ( QWidget *widget : widgets )
137 {
138 if ( QgsPropertyOverrideButton *w = qobject_cast<QgsPropertyOverrideButton *>( widget ) )
139 {
140 connect( w, SIGNAL( changed ), this, slot );
141 }
142 else if ( QgsFieldExpressionWidget *w = qobject_cast<QgsFieldExpressionWidget *>( widget ) )
143 {
144 connect( w, SIGNAL( fieldChanged( QString ) ), this, slot );
145 }
146 else if ( QgsOpacityWidget *w = qobject_cast<QgsOpacityWidget *>( widget ) )
147 {
148 connect( w, SIGNAL( opacityChanged( double ) ), this, slot );
149 }
150 else if ( QComboBox *w = qobject_cast<QComboBox *>( widget ) )
151 {
152 connect( w, SIGNAL( currentIndexChanged( int ) ), this, slot );
153 }
154 else if ( QSpinBox *w = qobject_cast<QSpinBox *>( widget ) )
155 {
156 connect( w, SIGNAL( valueChanged( int ) ), this, slot );
157 }
158 else if ( QDoubleSpinBox *w = qobject_cast<QDoubleSpinBox *>( widget ) )
159 {
160 connect( w, SIGNAL( valueChanged( double ) ), this, slot );
161 }
162 else if ( QgsColorButton *w = qobject_cast<QgsColorButton *>( widget ) )
163 {
164 connect( w, SIGNAL( colorChanged( QColor ) ), this, slot );
165 }
166 else if ( QCheckBox *w = qobject_cast<QCheckBox *>( widget ) )
167 {
168 connect( w, SIGNAL( toggled( bool ) ), this, slot );
169 }
170 else if ( QLineEdit *w = qobject_cast<QLineEdit *>( widget ) )
171 {
172 connect( w, SIGNAL( textEdited( QString ) ), this, slot );
173 connect( w, SIGNAL( textChanged( QString ) ), this, slot );
174 }
175 else if ( QgsEffectStackCompactWidget *w = qobject_cast<QgsEffectStackCompactWidget *>( widget ) )
176 {
177 connect( w, SIGNAL( changed() ), this, slot );
178 }
179 }
180 }
181
~QgsRendererPropertiesDialog()182 QgsRendererPropertiesDialog::~QgsRendererPropertiesDialog()
183 {
184 delete mPaintEffect;
185 }
186
setMapCanvas(QgsMapCanvas * canvas)187 void QgsRendererPropertiesDialog::setMapCanvas( QgsMapCanvas *canvas )
188 {
189 mMapCanvas = canvas;
190 if ( mActiveWidget )
191 {
192 QgsSymbolWidgetContext context;
193 context.setMapCanvas( mMapCanvas );
194 mActiveWidget->setContext( context );
195 }
196 }
197
setContext(const QgsSymbolWidgetContext & context)198 void QgsRendererPropertiesDialog::setContext( const QgsSymbolWidgetContext &context )
199 {
200 mMapCanvas = context.mapCanvas();
201 mMessageBar = context.messageBar();
202 if ( mActiveWidget )
203 {
204 mActiveWidget->setContext( context );
205 }
206 }
207
setDockMode(bool dockMode)208 void QgsRendererPropertiesDialog::setDockMode( bool dockMode )
209 {
210 mDockMode = dockMode;
211 mEffectWidget->setDockMode( dockMode );
212 if ( mActiveWidget )
213 mActiveWidget->setDockMode( mDockMode );
214 }
215
216
rendererChanged()217 void QgsRendererPropertiesDialog::rendererChanged()
218 {
219 if ( cboRenderers->currentIndex() == -1 )
220 {
221 QgsDebugMsg( QStringLiteral( "No current item -- this should never happen!" ) );
222 return;
223 }
224
225 QString rendererName = cboRenderers->currentData().toString();
226
227 //Retrieve the previous renderer: from the old active widget if possible, otherwise from the layer
228 QgsFeatureRenderer *oldRenderer = nullptr;
229 if ( mActiveWidget && mActiveWidget->renderer() )
230 {
231 oldRenderer = mActiveWidget->renderer()->clone();
232 }
233 else
234 {
235 oldRenderer = mLayer->renderer()->clone();
236 }
237
238 // get rid of old active widget (if any)
239 if ( mActiveWidget )
240 {
241 stackedWidget->removeWidget( mActiveWidget );
242
243 delete mActiveWidget;
244 mActiveWidget = nullptr;
245 }
246
247 QgsRendererWidget *w = nullptr;
248 QgsRendererAbstractMetadata *m = QgsApplication::rendererRegistry()->rendererMetadata( rendererName );
249 if ( m )
250 w = m->createRendererWidget( mLayer, mStyle, oldRenderer );
251 delete oldRenderer;
252
253 if ( w )
254 {
255 // instantiate the widget and set as active
256 mActiveWidget = w;
257 stackedWidget->addWidget( mActiveWidget );
258 stackedWidget->setCurrentWidget( mActiveWidget );
259 if ( mActiveWidget->renderer() )
260 {
261 if ( mMapCanvas || mMessageBar )
262 {
263 QgsSymbolWidgetContext context;
264 context.setMapCanvas( mMapCanvas );
265 context.setMessageBar( mMessageBar );
266 mActiveWidget->setContext( context );
267 }
268 changeOrderBy( mActiveWidget->renderer()->orderBy(), mActiveWidget->renderer()->orderByEnabled() );
269 connect( mActiveWidget, &QgsRendererWidget::layerVariablesChanged, this, &QgsRendererPropertiesDialog::layerVariablesChanged );
270 }
271 connect( mActiveWidget, &QgsPanelWidget::widgetChanged, this, &QgsRendererPropertiesDialog::widgetChanged );
272 connect( mActiveWidget, &QgsPanelWidget::showPanel, this, &QgsRendererPropertiesDialog::openPanel );
273 w->setDockMode( mDockMode );
274 }
275 else
276 {
277 // set default "no edit widget available" page
278 stackedWidget->setCurrentWidget( pageNoWidget );
279 }
280 }
281
apply()282 void QgsRendererPropertiesDialog::apply()
283 {
284 if ( !mActiveWidget || !mLayer )
285 {
286 return;
287 }
288
289 mActiveWidget->applyChanges();
290
291 QgsFeatureRenderer *renderer = mActiveWidget->renderer();
292 if ( renderer )
293 {
294 renderer->setPaintEffect( mPaintEffect->clone() );
295 // set the order by
296 renderer->setOrderBy( mOrderBy );
297 renderer->setOrderByEnabled( checkboxEnableOrderBy->isChecked() );
298
299 mLayer->setRenderer( renderer->clone() );
300 }
301
302 // set the blend modes for the layer
303 mLayer->setBlendMode( mBlendModeComboBox->blendMode() );
304 mLayer->setFeatureBlendMode( mFeatureBlendComboBox->blendMode() );
305
306 // set opacity for the layer
307 mLayer->setOpacity( mOpacityWidget->opacity() );
308 }
309
onOK()310 void QgsRendererPropertiesDialog::onOK()
311 {
312 apply();
313 accept();
314 }
315
openPanel(QgsPanelWidget * panel)316 void QgsRendererPropertiesDialog::openPanel( QgsPanelWidget *panel )
317 {
318 if ( mDockMode )
319 {
320 emit showPanel( panel );
321 }
322 else
323 {
324 // Show the dialog version if no one is connected
325 QDialog *dlg = new QDialog();
326 QString key = QStringLiteral( "/UI/paneldialog/%1" ).arg( panel->panelTitle() );
327 QgsSettings settings;
328 dlg->restoreGeometry( settings.value( key ).toByteArray() );
329 dlg->setWindowTitle( panel->panelTitle() );
330 dlg->setLayout( new QVBoxLayout() );
331 dlg->layout()->addWidget( panel );
332 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok );
333 connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
334 dlg->layout()->addWidget( buttonBox );
335 dlg->exec();
336 settings.setValue( key, dlg->saveGeometry() );
337 panel->acceptPanel();
338 }
339 }
340
syncToLayer()341 void QgsRendererPropertiesDialog::syncToLayer()
342 {
343 // Blend mode
344 mBlendModeComboBox->setBlendMode( mLayer->blendMode() );
345
346 // Feature blend mode
347 mFeatureBlendComboBox->setBlendMode( mLayer->featureBlendMode() );
348
349 // Layer opacity
350 mOpacityWidget->setOpacity( mLayer->opacity() );
351
352 //paint effect widget
353 if ( mLayer->renderer() )
354 {
355 if ( mLayer->renderer()->paintEffect() )
356 {
357 mPaintEffect = mLayer->renderer()->paintEffect()->clone();
358 mEffectWidget->setPaintEffect( mPaintEffect );
359 }
360
361 mOrderBy = mLayer->renderer()->orderBy();
362 }
363
364 // setup slot rendererChanged()
365 //setup order by
366 if ( mLayer->renderer() &&
367 mLayer->renderer()->orderByEnabled() )
368 {
369 checkboxEnableOrderBy->setChecked( true );
370 }
371 else
372 {
373 btnOrderBy->setEnabled( false );
374 checkboxEnableOrderBy->setChecked( false );
375 }
376
377 if ( mLayer->renderer() )
378 {
379 // set current renderer from layer
380 QString rendererName = mLayer->renderer()->type();
381
382 int rendererIdx = cboRenderers->findData( rendererName );
383 cboRenderers->setCurrentIndex( rendererIdx );
384
385 // no renderer found... this mustn't happen
386 Q_ASSERT( rendererIdx != -1 && "there must be a renderer!" );
387 }
388
389 }
390
showOrderByDialog()391 void QgsRendererPropertiesDialog::showOrderByDialog()
392 {
393 QgsOrderByDialog dlg( mLayer, this );
394
395 dlg.setOrderBy( mOrderBy );
396 if ( dlg.exec() )
397 {
398 mOrderBy = dlg.orderBy();
399 emit widgetChanged();
400 }
401 }
402
changeOrderBy(const QgsFeatureRequest::OrderBy & orderBy,bool orderByEnabled)403 void QgsRendererPropertiesDialog::changeOrderBy( const QgsFeatureRequest::OrderBy &orderBy, bool orderByEnabled )
404 {
405 mOrderBy = orderBy;
406 checkboxEnableOrderBy->setChecked( orderByEnabled );
407 }
408
updateUIState(bool hidden)409 void QgsRendererPropertiesDialog::updateUIState( bool hidden )
410 {
411 mLayerRenderingGroupBox->setHidden( hidden );
412 cboRenderers->setHidden( hidden );
413 }
414
415
keyPressEvent(QKeyEvent * e)416 void QgsRendererPropertiesDialog::keyPressEvent( QKeyEvent *e )
417 {
418 // Ignore the ESC key to avoid close the dialog without the properties window
419 if ( !isWindow() && e->key() == Qt::Key_Escape )
420 {
421 e->ignore();
422 }
423 else
424 {
425 QDialog::keyPressEvent( e );
426 }
427 }
428