1 /***************************************************************************
2 qgsdlgvectorlayerproperties.cpp
3 Unified property dialog for vector layers
4 -------------------
5 begin : 2004-01-28
6 copyright : (C) 2004 by Gary E.Sherman
7 email : sherman at mrcc.com
8 ***************************************************************************/
9
10 /***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
18
19 #include <memory>
20 #include <limits>
21
22 #include "qgsactionmanager.h"
23 #include "qgsjoindialog.h"
24 #include "qgswmsdimensiondialog.h"
25 #include "qgsapplication.h"
26 #include "qgsattributeactiondialog.h"
27 #include "qgscoordinatetransform.h"
28 #include "qgsdatumtransformdialog.h"
29 #include "qgsdiagramproperties.h"
30 #include "qgsdiagramrenderer.h"
31 #include "qgsexpressionbuilderdialog.h"
32 #include "qgsfieldcalculator.h"
33 #include "qgssourcefieldsproperties.h"
34 #include "qgsattributesformproperties.h"
35 #include "qgslabelingwidget.h"
36 #include "qgsprojectionselectiondialog.h"
37 #include "qgslogger.h"
38 #include "qgsmapcanvas.h"
39 #include "qgsmaplayerconfigwidgetfactory.h"
40 #include "qgsmaplayerstyleguiutils.h"
41 #include "qgsmetadatawidget.h"
42 #include "qgsnative.h"
43 #include "qgsproject.h"
44 #include "qgsvectorlayer.h"
45 #include "qgsvectorlayerjoininfo.h"
46 #include "qgsvectorlayerproperties.h"
47 #include "qgsconfig.h"
48 #include "qgsvectordataprovider.h"
49 #include "qgsquerybuilder.h"
50 #include "qgsdatasourceuri.h"
51 #include "qgsrenderer.h"
52 #include "qgsexpressioncontext.h"
53 #include "qgssettings.h"
54 #include "qgsrendererpropertiesdialog.h"
55 #include "qgsstyle.h"
56 #include "qgsauxiliarystorage.h"
57 #include "qgsnewauxiliarylayerdialog.h"
58 #include "qgsnewauxiliaryfielddialog.h"
59 #include "qgslabelinggui.h"
60 #include "qgssymbollayer.h"
61 #include "qgsgeometryoptions.h"
62 #include "qgsvectorlayersavestyledialog.h"
63 #include "qgsmaplayerloadstyledialog.h"
64 #include "qgsmessagebar.h"
65 #include "qgssymbolwidgetcontext.h"
66 #include "qgsexpressioncontextutils.h"
67 #include "qgsmaskingwidget.h"
68 #include "qgsvectorlayertemporalpropertieswidget.h"
69
70 #include "layertree/qgslayertreelayer.h"
71 #include "qgslayertree.h"
72
73 #include <QDesktopServices>
74 #include <QMessageBox>
75 #include <QDir>
76 #include <QFile>
77 #include <QFileDialog>
78 #include <QFileInfo>
79 #include <QFontDialog>
80 #include <QComboBox>
81 #include <QCheckBox>
82 #include <QHeaderView>
83 #include <QColorDialog>
84 #include <QMenu>
85 #include <QUrl>
86
87 #include "qgsrendererpropertiesdialog.h"
88 #include "qgsstyle.h"
89
90
QgsVectorLayerProperties(QgsMapCanvas * canvas,QgsMessageBar * messageBar,QgsVectorLayer * lyr,QWidget * parent,Qt::WindowFlags fl)91 QgsVectorLayerProperties::QgsVectorLayerProperties(
92 QgsMapCanvas *canvas,
93 QgsMessageBar *messageBar,
94 QgsVectorLayer *lyr,
95 QWidget *parent,
96 Qt::WindowFlags fl
97 )
98 : QgsOptionsDialogBase( QStringLiteral( "VectorLayerProperties" ), parent, fl )
99 , mCanvas( canvas )
100 , mMessageBar( messageBar )
101 , mLayer( lyr )
102 , mOriginalSubsetSQL( lyr->subsetString() )
103 {
104 setupUi( this );
105 connect( mLayerOrigNameLineEdit, &QLineEdit::textEdited, this, &QgsVectorLayerProperties::mLayerOrigNameLineEdit_textEdited );
106 connect( pbnQueryBuilder, &QPushButton::clicked, this, &QgsVectorLayerProperties::pbnQueryBuilder_clicked );
107 connect( pbnIndex, &QPushButton::clicked, this, &QgsVectorLayerProperties::pbnIndex_clicked );
108 connect( mCrsSelector, &QgsProjectionSelectionWidget::crsChanged, this, &QgsVectorLayerProperties::mCrsSelector_crsChanged );
109 connect( pbnUpdateExtents, &QPushButton::clicked, this, &QgsVectorLayerProperties::pbnUpdateExtents_clicked );
110 connect( mButtonAddJoin, &QPushButton::clicked, this, &QgsVectorLayerProperties::mButtonAddJoin_clicked );
111 connect( mButtonEditJoin, &QPushButton::clicked, this, &QgsVectorLayerProperties::mButtonEditJoin_clicked );
112 connect( mJoinTreeWidget, &QTreeWidget::itemDoubleClicked, this, &QgsVectorLayerProperties::mJoinTreeWidget_itemDoubleClicked );
113 connect( mButtonRemoveJoin, &QPushButton::clicked, this, &QgsVectorLayerProperties::mButtonRemoveJoin_clicked );
114 connect( mButtonAddWmsDimension, &QPushButton::clicked, this, &QgsVectorLayerProperties::mButtonAddWmsDimension_clicked );
115 connect( mButtonEditWmsDimension, &QPushButton::clicked, this, &QgsVectorLayerProperties::mButtonEditWmsDimension_clicked );
116 connect( mWmsDimensionsTreeWidget, &QTreeWidget::itemDoubleClicked, this, &QgsVectorLayerProperties::mWmsDimensionsTreeWidget_itemDoubleClicked );
117 connect( mButtonRemoveWmsDimension, &QPushButton::clicked, this, &QgsVectorLayerProperties::mButtonRemoveWmsDimension_clicked );
118 connect( mSimplifyDrawingGroupBox, &QGroupBox::toggled, this, &QgsVectorLayerProperties::mSimplifyDrawingGroupBox_toggled );
119 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsVectorLayerProperties::showHelp );
120
121 // QgsOptionsDialogBase handles saving/restoring of geometry, splitter and current tab states,
122 // switching vertical tabs between icon/text to icon-only modes (splitter collapsed to left),
123 // and connecting QDialogButtonBox's accepted/rejected signals to dialog's accept/reject slots
124 initOptionsBase( false );
125
126 mBtnStyle = new QPushButton( tr( "Style" ), this );
127 QMenu *menuStyle = new QMenu( this );
128 mActionLoadStyle = menuStyle->addAction( tr( "Load Style…" ) );
129 connect( mActionLoadStyle, &QAction::triggered, this, &QgsVectorLayerProperties::loadStyle );
130
131 // If we have multiple styles, offer an option to save them at once
132 if ( lyr->styleManager()->styles().count() > 1 )
133 {
134 mActionSaveStyle = menuStyle->addAction( tr( "Save Current Style…" ) );
135 mActionSaveMultipleStyles = menuStyle->addAction( tr( "Save All Styles…" ) );
136 connect( mActionSaveMultipleStyles, &QAction::triggered, this, &QgsVectorLayerProperties::saveMultipleStylesAs );
137 }
138 else
139 {
140 mActionSaveStyle = menuStyle->addAction( tr( "Save Style…" ) );
141 }
142 connect( mActionSaveStyle, &QAction::triggered, this, &QgsVectorLayerProperties::saveStyleAs );
143
144 menuStyle->addSeparator();
145 menuStyle->addAction( tr( "Save as Default" ), this, &QgsVectorLayerProperties::saveDefaultStyle_clicked );
146 menuStyle->addAction( tr( "Restore Default" ), this, &QgsVectorLayerProperties::loadDefaultStyle_clicked );
147 mBtnStyle->setMenu( menuStyle );
148 connect( menuStyle, &QMenu::aboutToShow, this, &QgsVectorLayerProperties::aboutToShowStyleMenu );
149 buttonBox->addButton( mBtnStyle, QDialogButtonBox::ResetRole );
150
151 mBtnMetadata = new QPushButton( tr( "Metadata" ), this );
152 QMenu *menuMetadata = new QMenu( this );
153 mActionLoadMetadata = menuMetadata->addAction( tr( "Load Metadata…" ), this, &QgsVectorLayerProperties::loadMetadata );
154 mActionSaveMetadataAs = menuMetadata->addAction( tr( "Save Metadata…" ), this, &QgsVectorLayerProperties::saveMetadataAs );
155 menuMetadata->addSeparator();
156 menuMetadata->addAction( tr( "Save as Default" ), this, &QgsVectorLayerProperties::saveDefaultMetadata );
157 menuMetadata->addAction( tr( "Restore Default" ), this, &QgsVectorLayerProperties::loadDefaultMetadata );
158 mBtnMetadata->setMenu( menuMetadata );
159 buttonBox->addButton( mBtnMetadata, QDialogButtonBox::ResetRole );
160
161 connect( lyr->styleManager(), &QgsMapLayerStyleManager::currentStyleChanged, this, &QgsVectorLayerProperties::syncToLayer );
162
163 connect( buttonBox->button( QDialogButtonBox::Apply ), &QAbstractButton::clicked, this, &QgsVectorLayerProperties::apply );
164 connect( this, &QDialog::accepted, this, &QgsVectorLayerProperties::apply );
165 connect( this, &QDialog::rejected, this, &QgsVectorLayerProperties::onCancel );
166
167 mContext << QgsExpressionContextUtils::globalScope()
168 << QgsExpressionContextUtils::projectScope( QgsProject::instance() )
169 << QgsExpressionContextUtils::atlasScope( nullptr )
170 << QgsExpressionContextUtils::mapSettingsScope( mCanvas->mapSettings() )
171 << QgsExpressionContextUtils::layerScope( mLayer );
172
173 mMapTipExpressionFieldWidget->setLayer( lyr );
174 mMapTipExpressionFieldWidget->registerExpressionContextGenerator( this );
175 mDisplayExpressionWidget->setLayer( lyr );
176 mDisplayExpressionWidget->registerExpressionContextGenerator( this );
177
178 connect( mInsertExpressionButton, &QAbstractButton::clicked, this, &QgsVectorLayerProperties::insertFieldOrExpression );
179
180 if ( !mLayer )
181 return;
182
183 QVBoxLayout *layout = nullptr;
184
185 if ( mLayer->isSpatial() )
186 {
187 // Create the Labeling dialog tab
188 layout = new QVBoxLayout( labelingFrame );
189 layout->setContentsMargins( 0, 0, 0, 0 );
190 labelingDialog = new QgsLabelingWidget( mLayer, mCanvas, labelingFrame );
191 labelingDialog->layout()->setContentsMargins( 0, 0, 0, 0 );
192 connect( labelingDialog, &QgsLabelingWidget::auxiliaryFieldCreated, this, [ = ] { updateAuxiliaryStoragePage(); } );
193 layout->addWidget( labelingDialog );
194 labelingFrame->setLayout( layout );
195
196 // Create the masking dialog tab
197 layout = new QVBoxLayout( mMaskingFrame );
198 layout->setContentsMargins( 0, 0, 0, 0 );
199 mMaskingWidget = new QgsMaskingWidget( mMaskingFrame );
200 mMaskingWidget->setLayer( mLayer );
201 mMaskingWidget->layout()->setContentsMargins( 0, 0, 0, 0 );
202 layout->addWidget( mMaskingWidget );
203 mMaskingFrame->setLayout( layout );
204 }
205 else
206 {
207 labelingDialog = nullptr;
208 mOptsPage_Labels->setEnabled( false ); // disable labeling item
209 mOptsPage_Masks->setEnabled( false ); // disable masking item
210 mGeomGroupBox->setEnabled( false );
211 mGeomGroupBox->setVisible( false );
212 mCrsGroupBox->setEnabled( false );
213 mCrsGroupBox->setVisible( false );
214 }
215
216 // Create the Actions dialog tab
217 QVBoxLayout *actionLayout = new QVBoxLayout( actionOptionsFrame );
218 actionLayout->setContentsMargins( 0, 0, 0, 0 );
219 mActionDialog = new QgsAttributeActionDialog( *mLayer->actions(), actionOptionsFrame );
220 mActionDialog->layout()->setContentsMargins( 0, 0, 0, 0 );
221 actionLayout->addWidget( mActionDialog );
222
223 mSourceFieldsPropertiesDialog = new QgsSourceFieldsProperties( mLayer, mSourceFieldsFrame );
224 mSourceFieldsPropertiesDialog->layout()->setContentsMargins( 0, 0, 0, 0 );
225 mSourceFieldsFrame->setLayout( new QVBoxLayout( mSourceFieldsFrame ) );
226 mSourceFieldsFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
227 mSourceFieldsFrame->layout()->addWidget( mSourceFieldsPropertiesDialog );
228
229 connect( mSourceFieldsPropertiesDialog, &QgsSourceFieldsProperties::toggleEditing, this, static_cast<void ( QgsVectorLayerProperties::* )()>( &QgsVectorLayerProperties::toggleEditing ) );
230
231 mAttributesFormPropertiesDialog = new QgsAttributesFormProperties( mLayer, mAttributesFormFrame );
232 mAttributesFormPropertiesDialog->layout()->setContentsMargins( 0, 0, 0, 0 );
233 mAttributesFormFrame->setLayout( new QVBoxLayout( mAttributesFormFrame ) );
234 mAttributesFormFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
235 mAttributesFormFrame->layout()->addWidget( mAttributesFormPropertiesDialog );
236
237 // Metadata tab, before the syncToLayer
238 QVBoxLayout *metadataLayout = new QVBoxLayout( metadataFrame );
239 metadataLayout->setContentsMargins( 0, 0, 0, 0 );
240 mMetadataWidget = new QgsMetadataWidget( this, mLayer );
241 mMetadataWidget->layout()->setContentsMargins( 0, 0, 0, 0 );
242 mMetadataWidget->setMapCanvas( mCanvas );
243 metadataLayout->addWidget( mMetadataWidget );
244 metadataFrame->setLayout( metadataLayout );
245
246 QVBoxLayout *temporalLayout = new QVBoxLayout( temporalFrame );
247 temporalLayout->setContentsMargins( 0, 0, 0, 0 );
248 mTemporalWidget = new QgsVectorLayerTemporalPropertiesWidget( this, mLayer );
249 temporalLayout->addWidget( mTemporalWidget );
250
251 syncToLayer();
252
253 if ( mLayer->dataProvider() )
254 {
255 //enable spatial index button group if supported by provider, or if one already exists
256 QgsVectorDataProvider::Capabilities capabilities = mLayer->dataProvider()->capabilities();
257 if ( !( capabilities & QgsVectorDataProvider::CreateSpatialIndex ) )
258 {
259 pbnIndex->setEnabled( false );
260 }
261 if ( mLayer->dataProvider()->hasSpatialIndex() == QgsFeatureSource::SpatialIndexPresent )
262 {
263 pbnIndex->setEnabled( false );
264 pbnIndex->setText( tr( "Spatial Index Exists" ) );
265 }
266
267 if ( capabilities & QgsVectorDataProvider::SelectEncoding )
268 {
269 cboProviderEncoding->addItems( QgsVectorDataProvider::availableEncodings() );
270 QString enc = mLayer->dataProvider()->encoding();
271 int encindex = cboProviderEncoding->findText( enc );
272 if ( encindex < 0 )
273 {
274 cboProviderEncoding->insertItem( 0, enc );
275 encindex = 0;
276 }
277 cboProviderEncoding->setCurrentIndex( encindex );
278 }
279 else if ( mLayer->providerType() == QLatin1String( "ogr" ) )
280 {
281 // if OGR_L_TestCapability(OLCStringsAsUTF8) returns true, OGR provider encoding can be set to only UTF-8
282 // so make encoding box grayed out
283 cboProviderEncoding->addItem( mLayer->dataProvider()->encoding() );
284 cboProviderEncoding->setEnabled( false );
285 }
286 else
287 {
288 // other providers do not use mEncoding, so hide the group completely
289 mDataSourceEncodingFrame->hide();
290 }
291 }
292
293 mCrsSelector->setCrs( mLayer->crs() );
294
295 //insert existing join info
296 const QList< QgsVectorLayerJoinInfo > &joins = mLayer->vectorJoins();
297 for ( const QgsVectorLayerJoinInfo &join : joins )
298 {
299 addJoinToTreeWidget( join );
300 }
301
302 mOldJoins = mLayer->vectorJoins();
303
304 QVBoxLayout *diagLayout = new QVBoxLayout( mDiagramFrame );
305 diagLayout->setContentsMargins( 0, 0, 0, 0 );
306 diagramPropertiesDialog = new QgsDiagramProperties( mLayer, mDiagramFrame, mCanvas );
307 diagramPropertiesDialog->layout()->setContentsMargins( 0, 0, 0, 0 );
308 connect( diagramPropertiesDialog, &QgsDiagramProperties::auxiliaryFieldCreated, this, [ = ] { updateAuxiliaryStoragePage(); } );
309 diagLayout->addWidget( diagramPropertiesDialog );
310 mDiagramFrame->setLayout( diagLayout );
311
312 // Legend tab
313 mLegendWidget->setMapCanvas( mCanvas );
314 mLegendWidget->setLayer( mLayer );
315 mLegendConfigEmbeddedWidget->setLayer( mLayer );
316
317 // WMS Name as layer short name
318 mLayerShortNameLineEdit->setText( mLayer->shortName() );
319 // WMS Name validator
320 QValidator *shortNameValidator = new QRegExpValidator( QgsApplication::shortNameRegExp(), this );
321 mLayerShortNameLineEdit->setValidator( shortNameValidator );
322
323 //layer title and abstract
324 mLayerTitleLineEdit->setText( mLayer->title() );
325 mLayerAbstractTextEdit->setPlainText( mLayer->abstract() );
326 mLayerKeywordListLineEdit->setText( mLayer->keywordList() );
327 mLayerDataUrlLineEdit->setText( mLayer->dataUrl() );
328 mLayerDataUrlFormatComboBox->setCurrentIndex(
329 mLayerDataUrlFormatComboBox->findText(
330 mLayer->dataUrlFormat()
331 )
332 );
333 //layer attribution and metadataUrl
334 mLayerAttributionLineEdit->setText( mLayer->attribution() );
335 mLayerAttributionUrlLineEdit->setText( mLayer->attributionUrl() );
336 mLayerMetadataUrlLineEdit->setText( mLayer->metadataUrl() );
337 mLayerMetadataUrlTypeComboBox->setCurrentIndex(
338 mLayerMetadataUrlTypeComboBox->findText(
339 mLayer->metadataUrlType()
340 )
341 );
342 mLayerMetadataUrlFormatComboBox->setCurrentIndex(
343 mLayerMetadataUrlFormatComboBox->findText(
344 mLayer->metadataUrlFormat()
345 )
346 );
347 mLayerLegendUrlLineEdit->setText( mLayer->legendUrl() );
348 mLayerLegendUrlFormatComboBox->setCurrentIndex(
349 mLayerLegendUrlFormatComboBox->findText(
350 mLayer->legendUrlFormat()
351 )
352 );
353
354 //insert existing dimension info
355 const QList<QgsVectorLayerServerProperties::WmsDimensionInfo> &wmsDims = mLayer->serverProperties()->wmsDimensions();
356 for ( const QgsVectorLayerServerProperties::WmsDimensionInfo &dim : wmsDims )
357 {
358 addWmsDimensionInfoToTreeWidget( dim );
359 }
360
361 QString myStyle = QgsApplication::reportStyleSheet();
362 myStyle.append( QStringLiteral( "body { margin: 10px; }\n " ) );
363 teMetadataViewer->clear();
364 teMetadataViewer->document()->setDefaultStyleSheet( myStyle );
365 teMetadataViewer->setHtml( htmlMetadata() );
366 teMetadataViewer->setOpenLinks( false );
367 connect( teMetadataViewer, &QTextBrowser::anchorClicked, this, &QgsVectorLayerProperties::urlClicked );
368 mMetadataFilled = true;
369
370 QgsSettings settings;
371 // if dialog hasn't been opened/closed yet, default to Styles tab, which is used most often
372 // this will be read by restoreOptionsBaseUi()
373 if ( !settings.contains( QStringLiteral( "/Windows/VectorLayerProperties/tab" ) ) )
374 {
375 settings.setValue( QStringLiteral( "Windows/VectorLayerProperties/tab" ),
376 mOptStackedWidget->indexOf( mOptsPage_Style ) );
377 }
378
379 QString title = tr( "Layer Properties — %1" ).arg( mLayer->name() );
380 if ( !mLayer->styleManager()->isDefault( mLayer->styleManager()->currentStyle() ) )
381 title += QStringLiteral( " (%1)" ).arg( mLayer->styleManager()->currentStyle() );
382 restoreOptionsBaseUi( title );
383
384 QList<QgsMapLayer *> dependencySources;
385 const QSet<QgsMapLayerDependency> constDependencies = mLayer->dependencies();
386 for ( const QgsMapLayerDependency &dep : constDependencies )
387 {
388 QgsMapLayer *layer = QgsProject::instance()->mapLayer( dep.layerId() );
389 if ( layer )
390 dependencySources << layer;
391 }
392
393 mLayersDependenciesTreeModel = new QgsLayerTreeFilterProxyModel( this );
394 mLayersDependenciesTreeModel->setLayerTreeModel( new QgsLayerTreeModel( QgsProject::instance()->layerTreeRoot(), mLayersDependenciesTreeModel ) );
395 mLayersDependenciesTreeModel->setCheckedLayers( dependencySources );
396 connect( QgsProject::instance(), &QObject::destroyed, this, [ = ] {mLayersDependenciesTreeView->setModel( nullptr );} );
397 mLayersDependenciesTreeView->setModel( mLayersDependenciesTreeModel );
398
399 connect( mRefreshLayerCheckBox, &QCheckBox::toggled, mRefreshLayerIntervalSpinBox, &QDoubleSpinBox::setEnabled );
400
401 // auxiliary layer
402 QMenu *menu = new QMenu( this );
403
404 mAuxiliaryLayerActionNew = new QAction( tr( "Create" ), this );
405 menu->addAction( mAuxiliaryLayerActionNew );
406 connect( mAuxiliaryLayerActionNew, &QAction::triggered, this, &QgsVectorLayerProperties::onAuxiliaryLayerNew );
407
408 mAuxiliaryLayerActionClear = new QAction( tr( "Clear" ), this );
409 menu->addAction( mAuxiliaryLayerActionClear );
410 connect( mAuxiliaryLayerActionClear, &QAction::triggered, this, &QgsVectorLayerProperties::onAuxiliaryLayerClear );
411
412 mAuxiliaryLayerActionDelete = new QAction( tr( "Delete" ), this );
413 menu->addAction( mAuxiliaryLayerActionDelete );
414 connect( mAuxiliaryLayerActionDelete, &QAction::triggered, this, &QgsVectorLayerProperties::onAuxiliaryLayerDelete );
415
416 mAuxiliaryLayerActionExport = new QAction( tr( "Export" ), this );
417 menu->addAction( mAuxiliaryLayerActionExport );
418 connect( mAuxiliaryLayerActionExport, &QAction::triggered, this, [ = ] { emit exportAuxiliaryLayer( mLayer->auxiliaryLayer() ); } );
419
420 mAuxiliaryStorageActions->setMenu( menu );
421
422 connect( mAuxiliaryStorageFieldsDeleteBtn, &QPushButton::clicked, this, &QgsVectorLayerProperties::onAuxiliaryLayerDeleteField );
423 connect( mAuxiliaryStorageFieldsAddBtn, &QPushButton::clicked, this, &QgsVectorLayerProperties::onAuxiliaryLayerAddField );
424
425 updateAuxiliaryStoragePage();
426
427 mOptsPage_Information->setProperty( "helpPage", QStringLiteral( "working_with_vector/vector_properties.html#information-properties" ) );
428 mOptsPage_Source->setProperty( "helpPage", QStringLiteral( "working_with_vector/vector_properties.html#source-properties" ) );
429 mOptsPage_Style->setProperty( "helpPage", QStringLiteral( "working_with_vector/vector_properties.html#symbology-properties" ) );
430 mOptsPage_Labels->setProperty( "helpPage", QStringLiteral( "working_with_vector/vector_properties.html#labels-properties" ) );
431 mOptsPage_Masks->setProperty( "helpPage", QStringLiteral( "working_with_vector/vector_properties.html#masks-properties" ) );
432 mOptsPage_Diagrams->setProperty( "helpPage", QStringLiteral( "working_with_vector/vector_properties.html#diagrams-properties" ) );
433 mOptsPage_SourceFields->setProperty( "helpPage", QStringLiteral( "working_with_vector/vector_properties.html#fields-properties" ) );
434 mOptsPage_AttributesForm->setProperty( "helpPage", QStringLiteral( "working_with_vector/vector_properties.html#attributes-form-properties" ) );
435 mOptsPage_Joins->setProperty( "helpPage", QStringLiteral( "working_with_vector/vector_properties.html#joins-properties" ) );
436 mOptsPage_AuxiliaryStorage->setProperty( "helpPage", QStringLiteral( "working_with_vector/vector_properties.html#auxiliary-storage-properties" ) );
437 mOptsPage_Actions->setProperty( "helpPage", QStringLiteral( "working_with_vector/vector_properties.html#actions-properties" ) );
438 mOptsPage_Display->setProperty( "helpPage", QStringLiteral( "working_with_vector/vector_properties.html#display-properties" ) );
439 mOptsPage_Rendering->setProperty( "helpPage", QStringLiteral( "working_with_vector/vector_properties.html#rendering-properties" ) );
440 mOptsPage_Variables->setProperty( "helpPage", QStringLiteral( "working_with_vector/vector_properties.html#variables-properties" ) );
441 mOptsPage_Metadata->setProperty( "helpPage", QStringLiteral( "working_with_vector/vector_properties.html#metadata-properties" ) );
442 mOptsPage_DataDependencies->setProperty( "helpPage", QStringLiteral( "working_with_vector/vector_properties.html#dependencies-properties" ) ) ;
443 mOptsPage_Legend->setProperty( "helpPage", QStringLiteral( "working_with_vector/vector_properties.html#legend-properties" ) );
444 mOptsPage_Server->setProperty( "helpPage", QStringLiteral( "working_with_vector/vector_properties.html#qgis-server-properties" ) );
445
446
447 optionsStackedWidget_CurrentChanged( mOptStackedWidget->currentIndex() );
448 }
449
toggleEditing()450 void QgsVectorLayerProperties::toggleEditing()
451 {
452 if ( !mLayer )
453 return;
454
455 emit toggleEditing( mLayer );
456
457 setPbnQueryBuilderEnabled();
458 }
459
addPropertiesPageFactory(QgsMapLayerConfigWidgetFactory * factory)460 void QgsVectorLayerProperties::addPropertiesPageFactory( QgsMapLayerConfigWidgetFactory *factory )
461 {
462 if ( !factory->supportsLayer( mLayer ) || !factory->supportLayerPropertiesDialog() )
463 {
464 return;
465 }
466
467 QgsMapLayerConfigWidget *page = factory->createWidget( mLayer, nullptr, false, this );
468 mLayerPropertiesPages << page;
469
470 const QString beforePage = factory->layerPropertiesPagePositionHint();
471 if ( beforePage.isEmpty() )
472 addPage( factory->title(), factory->title(), factory->icon(), page );
473 else
474 insertPage( factory->title(), factory->title(), factory->icon(), page, beforePage );
475 }
476
insertFieldOrExpression()477 void QgsVectorLayerProperties::insertFieldOrExpression()
478 {
479 // Convert the selected field to an expression and
480 // insert it into the action at the cursor position
481 QString expression = QStringLiteral( "[% " );
482 expression += mMapTipExpressionFieldWidget->asExpression();
483 expression += QLatin1String( " %]" );
484
485 mMapTipWidget->insertText( expression );
486 }
487
488 // in raster props, this method is called sync()
syncToLayer()489 void QgsVectorLayerProperties::syncToLayer()
490 {
491 // populate the general information
492 mLayerOrigNameLineEdit->setText( mLayer->name() );
493 txtDisplayName->setText( mLayer->name() );
494
495 //see if we are dealing with a pg layer here
496 mSubsetGroupBox->setEnabled( true );
497 txtSubsetSQL->setText( mLayer->subsetString() );
498 // if the user is allowed to type an adhoc query, the app will crash if the query
499 // is bad. For this reason, the sql box is disabled and the query must be built
500 // using the query builder, either by typing it in by hand or using the buttons, etc
501 // on the builder. If the ability to enter a query directly into the box is required,
502 // a mechanism to check it must be implemented.
503 txtSubsetSQL->setReadOnly( true );
504 txtSubsetSQL->setCaretWidth( 0 );
505 txtSubsetSQL->setCaretLineVisible( false );
506 setPbnQueryBuilderEnabled();
507
508 mMapTipWidget->setText( mLayer->mapTipTemplate() );
509 mDisplayExpressionWidget->setField( mLayer->displayExpression() );
510
511 // set up the scale based layer visibility stuff....
512 mScaleRangeWidget->setScaleRange( mLayer->minimumScale(), mLayer->maximumScale() );
513 mScaleVisibilityGroupBox->setChecked( mLayer->hasScaleBasedVisibility() );
514 mScaleRangeWidget->setMapCanvas( mCanvas );
515
516 // get simplify drawing configuration
517 const QgsVectorSimplifyMethod &simplifyMethod = mLayer->simplifyMethod();
518 mSimplifyDrawingGroupBox->setChecked( simplifyMethod.simplifyHints() != QgsVectorSimplifyMethod::NoSimplification );
519 mSimplifyDrawingSpinBox->setValue( simplifyMethod.threshold() );
520 mSimplifyDrawingSpinBox->setClearValue( 1.0 );
521
522 QString remark = QStringLiteral( " (%1)" ).arg( tr( "Not supported" ) );
523 const QgsVectorDataProvider *provider = mLayer->dataProvider();
524 if ( !( provider && ( provider->capabilities() & QgsVectorDataProvider::SimplifyGeometries ) ) )
525 {
526 mSimplifyDrawingAtProvider->setChecked( false );
527 mSimplifyDrawingAtProvider->setEnabled( false );
528 if ( !mSimplifyDrawingAtProvider->text().endsWith( remark ) )
529 mSimplifyDrawingAtProvider->setText( mSimplifyDrawingAtProvider->text().append( remark ) );
530 }
531 else
532 {
533 mSimplifyDrawingAtProvider->setChecked( !simplifyMethod.forceLocalOptimization() );
534 mSimplifyDrawingAtProvider->setEnabled( mSimplifyDrawingGroupBox->isChecked() );
535 if ( mSimplifyDrawingAtProvider->text().endsWith( remark ) )
536 {
537 QString newText = mSimplifyDrawingAtProvider->text();
538 newText.chop( remark.size() );
539 mSimplifyDrawingAtProvider->setText( newText );
540 }
541 }
542
543 // disable simplification for point layers, now it is not implemented
544 if ( mLayer->geometryType() == QgsWkbTypes::PointGeometry )
545 {
546 mSimplifyDrawingGroupBox->setChecked( false );
547 mSimplifyDrawingGroupBox->setEnabled( false );
548 }
549
550 // Default local simplification algorithm
551 mSimplifyAlgorithmComboBox->addItem( tr( "Distance" ), QgsVectorSimplifyMethod::Distance );
552 mSimplifyAlgorithmComboBox->addItem( tr( "SnapToGrid" ), QgsVectorSimplifyMethod::SnapToGrid );
553 mSimplifyAlgorithmComboBox->addItem( tr( "Visvalingam" ), QgsVectorSimplifyMethod::Visvalingam );
554 mSimplifyAlgorithmComboBox->setCurrentIndex( mSimplifyAlgorithmComboBox->findData( simplifyMethod.simplifyAlgorithm() ) );
555
556 QStringList myScalesList = Qgis::defaultProjectScales().split( ',' );
557 myScalesList.append( QStringLiteral( "1:1" ) );
558 mSimplifyMaximumScaleComboBox->updateScales( myScalesList );
559 mSimplifyMaximumScaleComboBox->setScale( simplifyMethod.maximumScale() );
560
561 mForceRasterCheckBox->setChecked( mLayer->renderer() && mLayer->renderer()->forceRasterRender() );
562
563 mRefreshLayerCheckBox->setChecked( mLayer->hasAutoRefreshEnabled() );
564 mRefreshLayerIntervalSpinBox->setEnabled( mLayer->hasAutoRefreshEnabled() );
565 mRefreshLayerIntervalSpinBox->setValue( mLayer->autoRefreshInterval() / 1000.0 );
566
567 mRefreshLayerNotificationCheckBox->setChecked( mLayer->isRefreshOnNotifyEnabled() );
568 mNotificationMessageCheckBox->setChecked( !mLayer->refreshOnNotifyMessage().isEmpty() );
569 mNotifyMessagValueLineEdit->setText( mLayer->refreshOnNotifyMessage() );
570
571
572 // load appropriate symbology page (V1 or V2)
573 updateSymbologyPage();
574
575 mActionDialog->init( *mLayer->actions(), mLayer->attributeTableConfig() );
576
577 if ( labelingDialog )
578 labelingDialog->adaptToLayer();
579
580 mSourceFieldsPropertiesDialog->init();
581 mAttributesFormPropertiesDialog->init();
582
583 // set initial state for variable editor
584 updateVariableEditor();
585
586 if ( diagramPropertiesDialog )
587 diagramPropertiesDialog->syncToLayer();
588
589 // sync all plugin dialogs
590 const auto constMLayerPropertiesPages = mLayerPropertiesPages;
591 for ( QgsMapLayerConfigWidget *page : constMLayerPropertiesPages )
592 {
593 page->syncToLayer( mLayer );
594 }
595
596 mMetadataWidget->setMetadata( &mLayer->metadata() );
597
598 mTemporalWidget->syncToLayer();
599
600 mLegendWidget->setLayer( mLayer );
601
602 }
603
apply()604 void QgsVectorLayerProperties::apply()
605 {
606 if ( labelingDialog )
607 {
608 labelingDialog->writeSettingsToLayer();
609 }
610
611 // apply legend settings
612 mLegendWidget->applyToLayer();
613 mLegendConfigEmbeddedWidget->applyToLayer();
614
615 // save metadata
616 mMetadataWidget->acceptMetadata();
617 mMetadataFilled = false;
618
619 // save masking settings
620 if ( mMaskingWidget && mMaskingWidget->hasBeenPopulated() )
621 mMaskingWidget->apply();
622
623 //
624 // Set up sql subset query if applicable
625 //
626 mSubsetGroupBox->setEnabled( true );
627
628 if ( txtSubsetSQL->text() != mLayer->subsetString() )
629 {
630 // set the subset sql for the layer
631 mLayer->setSubsetString( txtSubsetSQL->text() );
632 mMetadataFilled = false;
633 }
634 mOriginalSubsetSQL = mLayer->subsetString();
635
636 // set up the scale based layer visibility stuff....
637 mLayer->setScaleBasedVisibility( mScaleVisibilityGroupBox->isChecked() );
638 mLayer->setMaximumScale( mScaleRangeWidget->maximumScale() );
639 mLayer->setMinimumScale( mScaleRangeWidget->minimumScale() );
640
641 // provider-specific options
642 if ( mLayer->dataProvider() )
643 {
644 if ( mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::SelectEncoding )
645 {
646 mLayer->setProviderEncoding( cboProviderEncoding->currentText() );
647 }
648 }
649
650 mLayer->setDisplayExpression( mDisplayExpressionWidget->asExpression() );
651 mLayer->setMapTipTemplate( mMapTipWidget->text() );
652
653 mLayer->actions()->clearActions();
654 const auto constActions = mActionDialog->actions();
655 for ( const QgsAction &action : constActions )
656 {
657 mLayer->actions()->addAction( action );
658 }
659 QgsAttributeTableConfig attributeTableConfig = mLayer->attributeTableConfig();
660 attributeTableConfig.update( mLayer->fields() );
661 attributeTableConfig.setActionWidgetStyle( mActionDialog->attributeTableWidgetStyle() );
662 QVector<QgsAttributeTableConfig::ColumnConfig> columns = attributeTableConfig.columns();
663
664 for ( int i = 0; i < columns.size(); ++i )
665 {
666 if ( columns.at( i ).type == QgsAttributeTableConfig::Action )
667 {
668 columns[i].hidden = !mActionDialog->showWidgetInAttributeTable();
669 }
670 }
671
672 attributeTableConfig.setColumns( columns );
673
674 mLayer->setAttributeTableConfig( attributeTableConfig );
675
676 mLayer->setName( mLayerOrigNameLineEdit->text() );
677
678 mAttributesFormPropertiesDialog->apply();
679 mSourceFieldsPropertiesDialog->apply();
680
681 // Update temporal properties
682 mTemporalWidget->saveTemporalProperties();
683
684 if ( mLayer->renderer() )
685 {
686 QgsRendererPropertiesDialog *dlg = static_cast<QgsRendererPropertiesDialog *>( widgetStackRenderers->currentWidget() );
687 dlg->apply();
688 }
689
690 //apply diagram settings
691 diagramPropertiesDialog->apply();
692
693 // apply all plugin dialogs
694 const auto constMLayerPropertiesPages = mLayerPropertiesPages;
695 for ( QgsMapLayerConfigWidget *page : constMLayerPropertiesPages )
696 {
697 page->apply();
698 }
699
700 //layer title and abstract
701 if ( mLayer->shortName() != mLayerShortNameLineEdit->text() )
702 mMetadataFilled = false;
703 mLayer->setShortName( mLayerShortNameLineEdit->text() );
704
705 if ( mLayer->title() != mLayerTitleLineEdit->text() )
706 mMetadataFilled = false;
707 mLayer->setTitle( mLayerTitleLineEdit->text() );
708
709 if ( mLayer->abstract() != mLayerAbstractTextEdit->toPlainText() )
710 mMetadataFilled = false;
711 mLayer->setAbstract( mLayerAbstractTextEdit->toPlainText() );
712
713 if ( mLayer->keywordList() != mLayerKeywordListLineEdit->text() )
714 mMetadataFilled = false;
715 mLayer->setKeywordList( mLayerKeywordListLineEdit->text() );
716
717 if ( mLayer->dataUrl() != mLayerDataUrlLineEdit->text() )
718 mMetadataFilled = false;
719 mLayer->setDataUrl( mLayerDataUrlLineEdit->text() );
720
721 if ( mLayer->dataUrlFormat() != mLayerDataUrlFormatComboBox->currentText() )
722 mMetadataFilled = false;
723 mLayer->setDataUrlFormat( mLayerDataUrlFormatComboBox->currentText() );
724
725 //layer attribution and metadataUrl
726 if ( mLayer->attribution() != mLayerAttributionLineEdit->text() )
727 mMetadataFilled = false;
728 mLayer->setAttribution( mLayerAttributionLineEdit->text() );
729
730 if ( mLayer->attributionUrl() != mLayerAttributionUrlLineEdit->text() )
731 mMetadataFilled = false;
732 mLayer->setAttributionUrl( mLayerAttributionUrlLineEdit->text() );
733
734 if ( mLayer->metadataUrl() != mLayerMetadataUrlLineEdit->text() )
735 mMetadataFilled = false;
736 mLayer->setMetadataUrl( mLayerMetadataUrlLineEdit->text() );
737
738 if ( mLayer->metadataUrlType() != mLayerMetadataUrlTypeComboBox->currentText() )
739 mMetadataFilled = false;
740 mLayer->setMetadataUrlType( mLayerMetadataUrlTypeComboBox->currentText() );
741
742 if ( mLayer->metadataUrlFormat() != mLayerMetadataUrlFormatComboBox->currentText() )
743 mMetadataFilled = false;
744 mLayer->setMetadataUrlFormat( mLayerMetadataUrlFormatComboBox->currentText() );
745
746 // LegendURL
747 if ( mLayer->legendUrl() != mLayerLegendUrlLineEdit->text() )
748 mMetadataFilled = false;
749 mLayer->setLegendUrl( mLayerLegendUrlLineEdit->text() );
750
751 if ( mLayer->legendUrlFormat() != mLayerLegendUrlFormatComboBox->currentText() )
752 mMetadataFilled = false;
753 mLayer->setLegendUrlFormat( mLayerLegendUrlFormatComboBox->currentText() );
754
755 //layer simplify drawing configuration
756 QgsVectorSimplifyMethod::SimplifyHints simplifyHints = QgsVectorSimplifyMethod::NoSimplification;
757 if ( mSimplifyDrawingGroupBox->isChecked() )
758 {
759 simplifyHints |= QgsVectorSimplifyMethod::GeometrySimplification;
760 if ( mSimplifyDrawingSpinBox->value() > 1 ) simplifyHints |= QgsVectorSimplifyMethod::AntialiasingSimplification;
761 }
762 QgsVectorSimplifyMethod simplifyMethod = mLayer->simplifyMethod();
763 simplifyMethod.setSimplifyHints( simplifyHints );
764 simplifyMethod.setSimplifyAlgorithm( static_cast< QgsVectorSimplifyMethod::SimplifyAlgorithm >( mSimplifyAlgorithmComboBox->currentData().toInt() ) );
765 simplifyMethod.setThreshold( mSimplifyDrawingSpinBox->value() );
766 simplifyMethod.setForceLocalOptimization( !mSimplifyDrawingAtProvider->isChecked() );
767 simplifyMethod.setMaximumScale( mSimplifyMaximumScaleComboBox->scale() );
768 mLayer->setSimplifyMethod( simplifyMethod );
769
770 if ( mLayer->renderer() )
771 mLayer->renderer()->setForceRasterRender( mForceRasterCheckBox->isChecked() );
772
773 mLayer->setAutoRefreshInterval( mRefreshLayerIntervalSpinBox->value() * 1000.0 );
774 mLayer->setAutoRefreshEnabled( mRefreshLayerCheckBox->isChecked() );
775
776 mLayer->setRefreshOnNotifyEnabled( mRefreshLayerNotificationCheckBox->isChecked() );
777 mLayer->setRefreshOnNofifyMessage( mNotificationMessageCheckBox->isChecked() ? mNotifyMessagValueLineEdit->text() : QString() );
778
779 mOldJoins = mLayer->vectorJoins();
780
781 //save variables
782 QgsExpressionContextUtils::setLayerVariables( mLayer, mVariableEditor->variablesInActiveScope() );
783 updateVariableEditor();
784
785 // save dependencies
786 QSet<QgsMapLayerDependency> deps;
787 const auto checkedLayers = mLayersDependenciesTreeModel->checkedLayers();
788 for ( const QgsMapLayer *layer : checkedLayers )
789 deps << QgsMapLayerDependency( layer->id() );
790 if ( ! mLayer->setDependencies( deps ) )
791 {
792 QMessageBox::warning( nullptr, tr( "Save Dependency" ), tr( "This configuration introduces a cycle in data dependencies and will be ignored." ) );
793 }
794
795 mLayer->triggerRepaint();
796 // notify the project we've made a change
797 QgsProject::instance()->setDirty( true );
798 }
799
onCancel()800 void QgsVectorLayerProperties::onCancel()
801 {
802 if ( mOldJoins != mLayer->vectorJoins() )
803 {
804 // need to undo changes in vector layer joins - they are applied directly to the layer (not in apply())
805 // so other parts of the properties dialog can use the fields from the joined layers
806
807 const auto constVectorJoins = mLayer->vectorJoins();
808 for ( const QgsVectorLayerJoinInfo &info : constVectorJoins )
809 mLayer->removeJoin( info.joinLayerId() );
810
811 for ( const QgsVectorLayerJoinInfo &info : qgis::as_const( mOldJoins ) )
812 mLayer->addJoin( info );
813 }
814
815 if ( mOriginalSubsetSQL != mLayer->subsetString() )
816 {
817 // need to undo changes in subset string - they are applied directly to the layer (not in apply())
818 // by QgsQueryBuilder::accept()
819
820 mLayer->setSubsetString( mOriginalSubsetSQL );
821 }
822
823 if ( mOldStyle.xmlData() != mLayer->styleManager()->style( mLayer->styleManager()->currentStyle() ).xmlData() )
824 {
825 // need to reset style to previous - style applied directly to the layer (not in apply())
826 QString myMessage;
827 QDomDocument doc( QStringLiteral( "qgis" ) );
828 int errorLine, errorColumn;
829 doc.setContent( mOldStyle.xmlData(), false, &myMessage, &errorLine, &errorColumn );
830 mLayer->importNamedStyle( doc, myMessage );
831 syncToLayer();
832 }
833 }
834
urlClicked(const QUrl & url)835 void QgsVectorLayerProperties::urlClicked( const QUrl &url )
836 {
837 QFileInfo file( url.toLocalFile() );
838 if ( file.exists() && !file.isDir() )
839 QgsGui::instance()->nativePlatformInterface()->openFileExplorerAndSelectFile( url.toLocalFile() );
840 else
841 QDesktopServices::openUrl( url );
842 }
843
pbnQueryBuilder_clicked()844 void QgsVectorLayerProperties::pbnQueryBuilder_clicked()
845 {
846 // launch the query builder
847 QgsQueryBuilder *qb = new QgsQueryBuilder( mLayer, this );
848
849 // Set the sql in the query builder to the same in the prop dialog
850 // (in case the user has already changed it)
851 qb->setSql( txtSubsetSQL->text() );
852 // Open the query builder
853 if ( qb->exec() )
854 {
855 // if the sql is changed, update it in the prop subset text box
856 txtSubsetSQL->setText( qb->sql() );
857 //TODO If the sql is changed in the prop dialog, the layer extent should be recalculated
858
859 // The datasource for the layer needs to be updated with the new sql since this gets
860 // saved to the project file. This should happen at the map layer level...
861
862 }
863 // delete the query builder object
864 delete qb;
865 }
866
pbnIndex_clicked()867 void QgsVectorLayerProperties::pbnIndex_clicked()
868 {
869 QgsVectorDataProvider *pr = mLayer->dataProvider();
870 if ( pr )
871 {
872 setCursor( Qt::WaitCursor );
873 bool errval = pr->createSpatialIndex();
874 setCursor( Qt::ArrowCursor );
875 if ( errval )
876 {
877 pbnIndex->setEnabled( false );
878 pbnIndex->setText( tr( "Spatial Index Exists" ) );
879 QMessageBox::information( this, tr( "Spatial Index" ), tr( "Creation of spatial index successful" ) );
880 }
881 else
882 {
883 QMessageBox::warning( this, tr( "Spatial Index" ), tr( "Creation of spatial index failed" ) );
884 }
885 }
886 }
887
htmlMetadata()888 QString QgsVectorLayerProperties::htmlMetadata()
889 {
890 return mLayer->htmlMetadata();
891 }
892
mLayerOrigNameLineEdit_textEdited(const QString & text)893 void QgsVectorLayerProperties::mLayerOrigNameLineEdit_textEdited( const QString &text )
894 {
895 txtDisplayName->setText( mLayer->formatLayerName( text ) );
896 }
897
mCrsSelector_crsChanged(const QgsCoordinateReferenceSystem & crs)898 void QgsVectorLayerProperties::mCrsSelector_crsChanged( const QgsCoordinateReferenceSystem &crs )
899 {
900 QgsDatumTransformDialog::run( crs, QgsProject::instance()->crs(), this, mCanvas, tr( "Select Transformation for the vector layer" ) );
901 mLayer->setCrs( crs );
902 mMetadataFilled = false;
903 mMetadataWidget->crsChanged();
904 }
905
loadDefaultStyle_clicked()906 void QgsVectorLayerProperties::loadDefaultStyle_clicked()
907 {
908 QString msg;
909 bool defaultLoadedFlag = false;
910
911 const QgsVectorDataProvider *provider = mLayer->dataProvider();
912 if ( !provider )
913 return;
914 if ( provider->isSaveAndLoadStyleToDatabaseSupported() )
915 {
916 QMessageBox askToUser;
917 askToUser.setText( tr( "Load default style from: " ) );
918 askToUser.setIcon( QMessageBox::Question );
919 askToUser.addButton( tr( "Cancel" ), QMessageBox::RejectRole );
920 askToUser.addButton( tr( "Local Database" ), QMessageBox::NoRole );
921 askToUser.addButton( tr( "Datasource Database" ), QMessageBox::YesRole );
922
923 switch ( askToUser.exec() )
924 {
925 case 0:
926 return;
927 case 2:
928 msg = mLayer->loadNamedStyle( mLayer->styleURI(), defaultLoadedFlag );
929 if ( !defaultLoadedFlag )
930 {
931 //something went wrong - let them know why
932 QMessageBox::information( this, tr( "Default Style" ), msg );
933 }
934 if ( msg.compare( tr( "Loaded from Provider" ) ) )
935 {
936 QMessageBox::information( this, tr( "Default Style" ),
937 tr( "No default style was found for this layer." ) );
938 }
939 else
940 {
941 syncToLayer();
942 }
943
944 return;
945 default:
946 break;
947 }
948 }
949
950 QString myMessage = mLayer->loadNamedStyle( mLayer->styleURI(), defaultLoadedFlag, true );
951 // QString myMessage = layer->loadDefaultStyle( defaultLoadedFlag );
952 //reset if the default style was loaded OK only
953 if ( defaultLoadedFlag )
954 {
955 // all worked OK so no need to inform user
956 syncToLayer();
957 }
958 else
959 {
960 //something went wrong - let them know why
961 QMessageBox::information( this, tr( "Default Style" ), myMessage );
962 }
963 }
964
saveDefaultStyle_clicked()965 void QgsVectorLayerProperties::saveDefaultStyle_clicked()
966 {
967 apply();
968 QString errorMsg;
969 const QgsVectorDataProvider *provider = mLayer->dataProvider();
970 if ( !provider )
971 return;
972 if ( provider->isSaveAndLoadStyleToDatabaseSupported() )
973 {
974 QMessageBox askToUser;
975 askToUser.setText( tr( "Save default style to: " ) );
976 askToUser.setIcon( QMessageBox::Question );
977 askToUser.addButton( tr( "Cancel" ), QMessageBox::RejectRole );
978 askToUser.addButton( tr( "Local Database" ), QMessageBox::NoRole );
979 askToUser.addButton( tr( "Datasource Database" ), QMessageBox::YesRole );
980
981 switch ( askToUser.exec() )
982 {
983 case 0:
984 return;
985 case 2:
986 mLayer->saveStyleToDatabase( QString(), QString(), true, QString(), errorMsg );
987 if ( errorMsg.isNull() )
988 {
989 return;
990 }
991 break;
992 default:
993 break;
994 }
995 }
996
997 bool defaultSavedFlag = false;
998 errorMsg = mLayer->saveDefaultStyle( defaultSavedFlag );
999 if ( !defaultSavedFlag )
1000 {
1001 QMessageBox::warning( this, tr( "Default Style" ), errorMsg );
1002 }
1003 }
1004
loadMetadata()1005 void QgsVectorLayerProperties::loadMetadata()
1006 {
1007 QgsSettings myQSettings; // where we keep last used filter in persistent state
1008 QString myLastUsedDir = myQSettings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
1009
1010 QString myFileName = QFileDialog::getOpenFileName( this, tr( "Load Layer Metadata from Metadata File" ), myLastUsedDir,
1011 tr( "QGIS Layer Metadata File" ) + " (*.qmd)" );
1012 if ( myFileName.isNull() )
1013 {
1014 return;
1015 }
1016
1017 QString myMessage;
1018 bool defaultLoadedFlag = false;
1019 myMessage = mLayer->loadNamedMetadata( myFileName, defaultLoadedFlag );
1020
1021 //reset if the default style was loaded OK only
1022 if ( defaultLoadedFlag )
1023 {
1024 mMetadataWidget->setMetadata( &mLayer->metadata() );
1025 }
1026 else
1027 {
1028 //let the user know what went wrong
1029 QMessageBox::warning( this, tr( "Load Metadata" ), myMessage );
1030 }
1031
1032 QFileInfo myFI( myFileName );
1033 QString myPath = myFI.path();
1034 myQSettings.setValue( QStringLiteral( "style/lastStyleDir" ), myPath );
1035
1036 activateWindow(); // set focus back to properties dialog
1037 }
1038
saveMetadataAs()1039 void QgsVectorLayerProperties::saveMetadataAs()
1040 {
1041 QgsSettings myQSettings; // where we keep last used filter in persistent state
1042 QString myLastUsedDir = myQSettings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
1043
1044 QString myOutputFileName = QFileDialog::getSaveFileName( this, tr( "Save Layer Metadata as QMD" ),
1045 myLastUsedDir, tr( "QMD File" ) + " (*.qmd)" );
1046 if ( myOutputFileName.isNull() ) //dialog canceled
1047 {
1048 return;
1049 }
1050
1051 mMetadataWidget->acceptMetadata();
1052
1053 //ensure the user never omitted the extension from the file name
1054 if ( !myOutputFileName.endsWith( QgsMapLayer::extensionPropertyType( QgsMapLayer::Metadata ), Qt::CaseInsensitive ) )
1055 {
1056 myOutputFileName += QgsMapLayer::extensionPropertyType( QgsMapLayer::Metadata );
1057 }
1058
1059 QString myMessage;
1060 bool defaultLoadedFlag = false;
1061 myMessage = mLayer->saveNamedMetadata( myOutputFileName, defaultLoadedFlag );
1062
1063 //reset if the default style was loaded OK only
1064 if ( defaultLoadedFlag )
1065 {
1066 syncToLayer();
1067 }
1068 else
1069 {
1070 //let the user know what went wrong
1071 QMessageBox::information( this, tr( "Save Metadata" ), myMessage );
1072 }
1073
1074 QFileInfo myFI( myOutputFileName );
1075 QString myPath = myFI.path();
1076 // Persist last used dir
1077 myQSettings.setValue( QStringLiteral( "style/lastStyleDir" ), myPath );
1078 }
1079
saveDefaultMetadata()1080 void QgsVectorLayerProperties::saveDefaultMetadata()
1081 {
1082 mMetadataWidget->acceptMetadata();
1083
1084 bool defaultSavedFlag = false;
1085 QString errorMsg = mLayer->saveDefaultMetadata( defaultSavedFlag );
1086 if ( !defaultSavedFlag )
1087 {
1088 QMessageBox::warning( this, tr( "Default Metadata" ), errorMsg );
1089 }
1090 }
1091
loadDefaultMetadata()1092 void QgsVectorLayerProperties::loadDefaultMetadata()
1093 {
1094 bool defaultLoadedFlag = false;
1095 QString myMessage = mLayer->loadNamedMetadata( mLayer->metadataUri(), defaultLoadedFlag );
1096 //reset if the default metadata was loaded OK only
1097 if ( defaultLoadedFlag )
1098 {
1099 mMetadataWidget->setMetadata( &mLayer->metadata() );
1100 }
1101 else
1102 {
1103 QMessageBox::information( this, tr( "Default Metadata" ), myMessage );
1104 }
1105 }
1106
1107
saveStyleAs()1108 void QgsVectorLayerProperties::saveStyleAs()
1109 {
1110 if ( !mLayer->dataProvider() )
1111 return;
1112 QgsVectorLayerSaveStyleDialog dlg( mLayer );
1113 QgsSettings settings;
1114
1115 if ( dlg.exec() )
1116 {
1117 apply();
1118
1119 bool defaultLoadedFlag = false;
1120
1121 StyleType type = dlg.currentStyleType();
1122 switch ( type )
1123 {
1124 case QML:
1125 case SLD:
1126 {
1127 QString message;
1128 QString filePath = dlg.outputFilePath();
1129 if ( type == QML )
1130 message = mLayer->saveNamedStyle( filePath, defaultLoadedFlag, dlg.styleCategories() );
1131 else
1132 message = mLayer->saveSldStyle( filePath, defaultLoadedFlag );
1133
1134 //reset if the default style was loaded OK only
1135 if ( defaultLoadedFlag )
1136 {
1137 syncToLayer();
1138 }
1139 else
1140 {
1141 //let the user know what went wrong
1142 QMessageBox::information( this, tr( "Save Style" ), message );
1143 }
1144
1145 break;
1146 }
1147 case DB:
1148 {
1149 QString infoWindowTitle = QObject::tr( "Save style to DB (%1)" ).arg( mLayer->providerType() );
1150 QString msgError;
1151
1152 QgsVectorLayerSaveStyleDialog::SaveToDbSettings dbSettings = dlg.saveToDbSettings();
1153
1154 mLayer->saveStyleToDatabase( dbSettings.name, dbSettings.description, dbSettings.isDefault, dbSettings.uiFileContent, msgError );
1155
1156 int messageTimeout = QgsSettings().value( QStringLiteral( "qgis/messageTimeout" ), 5 ).toInt();
1157 if ( !msgError.isNull() )
1158 {
1159 mMessageBar->pushMessage( infoWindowTitle, msgError, Qgis::Warning, messageTimeout );
1160 }
1161 else
1162 {
1163 mMessageBar->pushMessage( infoWindowTitle, tr( "Style saved" ), Qgis::Info, messageTimeout );
1164 }
1165 break;
1166 }
1167 }
1168 }
1169 }
1170
saveMultipleStylesAs()1171 void QgsVectorLayerProperties::saveMultipleStylesAs()
1172 {
1173 QgsVectorLayerSaveStyleDialog dlg( mLayer );
1174 dlg.setSaveOnlyCurrentStyle( false );
1175 QgsSettings settings;
1176
1177 if ( dlg.exec() )
1178 {
1179 apply();
1180
1181 // Store the original style, that we can restore at the end
1182 const QString originalStyle { mLayer->styleManager()->currentStyle() };
1183 const QListWidget *stylesWidget { dlg.stylesWidget() };
1184
1185 // Collect selected (checked) styles for export/save
1186 QStringList stylesSelected;
1187 for ( int i = 0; i < stylesWidget->count(); i++ )
1188 {
1189 if ( stylesWidget->item( i )->checkState() == Qt::CheckState::Checked )
1190 {
1191 stylesSelected.push_back( stylesWidget->item( i )->text() );
1192 }
1193 }
1194
1195 if ( ! stylesSelected.isEmpty() )
1196 {
1197 int styleIndex = 0;
1198 for ( const QString &styleName : qgis::as_const( stylesSelected ) )
1199 {
1200 bool defaultLoadedFlag = false;
1201
1202 StyleType type = dlg.currentStyleType();
1203 mLayer->styleManager()->setCurrentStyle( styleName );
1204 switch ( type )
1205 {
1206 case QML:
1207 case SLD:
1208 {
1209 QString message;
1210 const QString filePath { dlg.outputFilePath() };
1211 QString safePath { filePath };
1212 if ( styleIndex > 0 && stylesSelected.count( ) > 1 )
1213 {
1214 int i = 1;
1215 while ( QFile::exists( safePath ) )
1216 {
1217 const QFileInfo fi { filePath };
1218 safePath = QString( filePath ).replace( '.' + fi.completeSuffix(), QStringLiteral( "_%1.%2" )
1219 .arg( QString::number( i ) )
1220 .arg( fi.completeSuffix() ) );
1221 i++;
1222 }
1223 }
1224 if ( type == QML )
1225 message = mLayer->saveNamedStyle( safePath, defaultLoadedFlag, dlg.styleCategories() );
1226 else
1227 message = mLayer->saveSldStyle( safePath, defaultLoadedFlag );
1228
1229 //reset if the default style was loaded OK only
1230 if ( defaultLoadedFlag )
1231 {
1232 syncToLayer();
1233 }
1234 else
1235 {
1236 //let the user know what went wrong
1237 QMessageBox::information( this, tr( "Save Style" ), message );
1238 }
1239
1240 break;
1241 }
1242 case DB:
1243 {
1244 QString infoWindowTitle = QObject::tr( "Save style '%1' to DB (%2)" )
1245 .arg( styleName )
1246 .arg( mLayer->providerType() );
1247 QString msgError;
1248
1249 QgsVectorLayerSaveStyleDialog::SaveToDbSettings dbSettings = dlg.saveToDbSettings();
1250
1251 // If a name is defined, we add _1 etc. else we use the style name
1252 QString name { dbSettings.name };
1253 if ( name.isEmpty() )
1254 {
1255 name = styleName;
1256 }
1257 else
1258 {
1259 QStringList ids, names, descriptions;
1260 mLayer->listStylesInDatabase( ids, names, descriptions, msgError );
1261 int i = 1;
1262 while ( names.contains( name ) )
1263 {
1264 name = QStringLiteral( "%1 %2" ).arg( name ).arg( QString::number( i ) );
1265 i++;
1266 }
1267 }
1268 mLayer->saveStyleToDatabase( name, dbSettings.description, dbSettings.isDefault, dbSettings.uiFileContent, msgError );
1269
1270 const int timeout = QgsSettings().value( QStringLiteral( "qgis/messageTimeout" ), 5 ).toInt();
1271 if ( !msgError.isNull() )
1272 {
1273 mMessageBar->pushMessage( infoWindowTitle, msgError, Qgis::Warning, timeout );
1274 }
1275 else
1276 {
1277 mMessageBar->pushMessage( infoWindowTitle, tr( "Style '%1' saved" ).arg( styleName ),
1278 Qgis::Info, timeout );
1279 }
1280 break;
1281 }
1282 }
1283 styleIndex ++;
1284 }
1285 // Restore original style
1286 mLayer->styleManager()->setCurrentStyle( originalStyle );
1287 }
1288 } // Nothing selected!
1289 }
1290
aboutToShowStyleMenu()1291 void QgsVectorLayerProperties::aboutToShowStyleMenu()
1292 {
1293 // this should be unified with QgsRasterLayerProperties::aboutToShowStyleMenu()
1294 QMenu *m = qobject_cast<QMenu *>( sender() );
1295
1296 QgsMapLayerStyleGuiUtils::instance()->removesExtraMenuSeparators( m );
1297 // re-add style manager actions!
1298 m->addSeparator();
1299 QgsMapLayerStyleGuiUtils::instance()->addStyleManagerActions( m, mLayer );
1300 }
1301
loadStyle()1302 void QgsVectorLayerProperties::loadStyle()
1303 {
1304 QgsSettings settings; // where we keep last used filter in persistent state
1305
1306 QString errorMsg;
1307 QStringList ids, names, descriptions;
1308
1309 //get the list of styles in the db
1310 int sectionLimit = mLayer->listStylesInDatabase( ids, names, descriptions, errorMsg );
1311 QgsMapLayerLoadStyleDialog dlg( mLayer );
1312 dlg.initializeLists( ids, names, descriptions, sectionLimit );
1313
1314 if ( dlg.exec() )
1315 {
1316 mOldStyle = mLayer->styleManager()->style( mLayer->styleManager()->currentStyle() );
1317 QgsMapLayer::StyleCategories categories = dlg.styleCategories();
1318 StyleType type = dlg.currentStyleType();
1319 switch ( type )
1320 {
1321 case QML:
1322 case SLD:
1323 {
1324 QString message;
1325 bool defaultLoadedFlag = false;
1326 QString filePath = dlg.filePath();
1327 if ( type == SLD )
1328 {
1329 message = mLayer->loadSldStyle( filePath, defaultLoadedFlag );
1330 }
1331 else
1332 {
1333 message = mLayer->loadNamedStyle( filePath, defaultLoadedFlag, true, categories );
1334 }
1335 //reset if the default style was loaded OK only
1336 if ( defaultLoadedFlag )
1337 {
1338 syncToLayer();
1339 }
1340 else
1341 {
1342 //let the user know what went wrong
1343 QMessageBox::warning( this, tr( "Load Style" ), message );
1344 }
1345 break;
1346 }
1347 case DB:
1348 {
1349 QString selectedStyleId = dlg.selectedStyleId();
1350
1351 QString qmlStyle = mLayer->getStyleFromDatabase( selectedStyleId, errorMsg );
1352 if ( !errorMsg.isNull() )
1353 {
1354 QMessageBox::warning( this, tr( "Load Styles from Database" ), errorMsg );
1355 return;
1356 }
1357
1358 QDomDocument myDocument( QStringLiteral( "qgis" ) );
1359 myDocument.setContent( qmlStyle );
1360
1361 if ( mLayer->importNamedStyle( myDocument, errorMsg, categories ) )
1362 {
1363 syncToLayer();
1364 }
1365 else
1366 {
1367 QMessageBox::warning( this, tr( "Load Styles from Database" ),
1368 tr( "The retrieved style is not a valid named style. Error message: %1" )
1369 .arg( errorMsg ) );
1370 }
1371 break;
1372 }
1373 }
1374 activateWindow(); // set focus back to properties dialog
1375 }
1376 }
1377
mButtonAddJoin_clicked()1378 void QgsVectorLayerProperties::mButtonAddJoin_clicked()
1379 {
1380 if ( !mLayer )
1381 return;
1382
1383 QList<QgsMapLayer *> joinedLayers;
1384 const QList< QgsVectorLayerJoinInfo > &joins = mLayer->vectorJoins();
1385 joinedLayers.reserve( joins.size() );
1386 for ( int i = 0; i < joins.size(); ++i )
1387 {
1388 joinedLayers.append( joins[i].joinLayer() );
1389 }
1390
1391 QgsJoinDialog d( mLayer, joinedLayers );
1392 if ( d.exec() == QDialog::Accepted )
1393 {
1394 QgsVectorLayerJoinInfo info = d.joinInfo();
1395 //create attribute index if possible
1396 if ( d.createAttributeIndex() )
1397 {
1398 QgsVectorLayer *joinLayer = info.joinLayer();
1399 if ( joinLayer )
1400 {
1401 joinLayer->dataProvider()->createAttributeIndex( joinLayer->fields().indexFromName( info.joinFieldName() ) );
1402 }
1403 }
1404 mLayer->addJoin( info );
1405 addJoinToTreeWidget( info );
1406 setPbnQueryBuilderEnabled();
1407 mSourceFieldsPropertiesDialog->init();
1408 mAttributesFormPropertiesDialog->init();
1409 }
1410 }
1411
mButtonEditJoin_clicked()1412 void QgsVectorLayerProperties::mButtonEditJoin_clicked()
1413 {
1414 QTreeWidgetItem *currentJoinItem = mJoinTreeWidget->currentItem();
1415 mJoinTreeWidget_itemDoubleClicked( currentJoinItem, 0 );
1416 }
1417
mJoinTreeWidget_itemDoubleClicked(QTreeWidgetItem * item,int)1418 void QgsVectorLayerProperties::mJoinTreeWidget_itemDoubleClicked( QTreeWidgetItem *item, int )
1419 {
1420 if ( !mLayer || !item )
1421 {
1422 return;
1423 }
1424
1425 QList<QgsMapLayer *> joinedLayers;
1426 QString joinLayerId = item->data( 0, Qt::UserRole ).toString();
1427 const QList< QgsVectorLayerJoinInfo > &joins = mLayer->vectorJoins();
1428 int j = -1;
1429 for ( int i = 0; i < joins.size(); ++i )
1430 {
1431 QgsVectorLayer *joinLayer = joins[i].joinLayer();
1432 if ( !joinLayer )
1433 continue; // invalid join (unresolved join layer)
1434
1435 if ( joinLayer->id() == joinLayerId )
1436 {
1437 j = i;
1438 }
1439 else
1440 {
1441 // remove already joined layers from possible list to be displayed in dialog
1442 joinedLayers.append( joinLayer );
1443 }
1444 }
1445 if ( j == -1 )
1446 {
1447 return;
1448 }
1449
1450 QgsJoinDialog d( mLayer, joinedLayers );
1451 d.setWindowTitle( tr( "Edit Vector Join" ) );
1452 d.setJoinInfo( joins[j] );
1453
1454 if ( d.exec() == QDialog::Accepted )
1455 {
1456 QgsVectorLayerJoinInfo info = d.joinInfo();
1457
1458 // remove old join
1459 mLayer->removeJoin( joinLayerId );
1460 int idx = mJoinTreeWidget->indexOfTopLevelItem( item );
1461 mJoinTreeWidget->takeTopLevelItem( idx );
1462
1463 // add the new edited
1464
1465 //create attribute index if possible
1466 if ( d.createAttributeIndex() )
1467 {
1468 QgsVectorLayer *joinLayer = info.joinLayer();
1469 if ( joinLayer )
1470 {
1471 joinLayer->dataProvider()->createAttributeIndex( joinLayer->fields().indexFromName( info.joinFieldName() ) );
1472 }
1473 }
1474 mLayer->addJoin( info );
1475 addJoinToTreeWidget( info, idx );
1476
1477 setPbnQueryBuilderEnabled();
1478 mSourceFieldsPropertiesDialog->init();
1479 mAttributesFormPropertiesDialog->init();
1480 }
1481 }
1482
addJoinToTreeWidget(const QgsVectorLayerJoinInfo & join,const int insertIndex)1483 void QgsVectorLayerProperties::addJoinToTreeWidget( const QgsVectorLayerJoinInfo &join, const int insertIndex )
1484 {
1485 QTreeWidgetItem *joinItem = new QTreeWidgetItem();
1486 joinItem->setFlags( Qt::ItemIsEnabled );
1487
1488 QgsVectorLayer *joinLayer = join.joinLayer();
1489 if ( !mLayer || !joinLayer )
1490 {
1491 return;
1492 }
1493
1494 joinItem->setText( 0, tr( "Join layer" ) );
1495 if ( mLayer->auxiliaryLayer() && mLayer->auxiliaryLayer()->id() == join.joinLayerId() )
1496 {
1497 return;
1498 }
1499
1500 joinItem->setText( 1, joinLayer->name() );
1501
1502 QFont f = joinItem->font( 0 );
1503 f.setBold( true );
1504 joinItem->setFont( 0, f );
1505 joinItem->setFont( 1, f );
1506
1507 joinItem->setData( 0, Qt::UserRole, join.joinLayerId() );
1508
1509 QTreeWidgetItem *childJoinField = new QTreeWidgetItem();
1510 childJoinField->setText( 0, tr( "Join field" ) );
1511 childJoinField->setText( 1, join.joinFieldName() );
1512 childJoinField->setFlags( Qt::ItemIsEnabled );
1513 joinItem->addChild( childJoinField );
1514
1515 QTreeWidgetItem *childTargetField = new QTreeWidgetItem();
1516 childTargetField->setText( 0, tr( "Target field" ) );
1517 childTargetField->setText( 1, join.targetFieldName() );
1518 joinItem->addChild( childTargetField );
1519
1520 QTreeWidgetItem *childMemCache = new QTreeWidgetItem();
1521 childMemCache->setText( 0, tr( "Cache join layer in virtual memory" ) );
1522 if ( join.isUsingMemoryCache() )
1523 childMemCache->setText( 1, QChar( 0x2714 ) );
1524 joinItem->addChild( childMemCache );
1525
1526 QTreeWidgetItem *childDynForm = new QTreeWidgetItem();
1527 childDynForm->setText( 0, tr( "Dynamic form" ) );
1528 if ( join.isDynamicFormEnabled() )
1529 childDynForm->setText( 1, QChar( 0x2714 ) );
1530 joinItem->addChild( childDynForm );
1531
1532 QTreeWidgetItem *childEditable = new QTreeWidgetItem();
1533 childEditable->setText( 0, tr( "Editable join layer" ) );
1534 if ( join.isEditable() )
1535 childEditable->setText( 1, QChar( 0x2714 ) );
1536 joinItem->addChild( childEditable );
1537
1538 QTreeWidgetItem *childUpsert = new QTreeWidgetItem();
1539 childUpsert->setText( 0, tr( "Upsert on edit" ) );
1540 if ( join.hasUpsertOnEdit() )
1541 childUpsert->setText( 1, QChar( 0x2714 ) );
1542 joinItem->addChild( childUpsert );
1543
1544 QTreeWidgetItem *childCascade = new QTreeWidgetItem();
1545 childCascade->setText( 0, tr( "Delete cascade" ) );
1546 if ( join.hasCascadedDelete() )
1547 childCascade->setText( 1, QChar( 0x2714 ) );
1548 joinItem->addChild( childCascade );
1549
1550 QTreeWidgetItem *childPrefix = new QTreeWidgetItem();
1551 childPrefix->setText( 0, tr( "Custom field name prefix" ) );
1552 childPrefix->setText( 1, join.prefix() );
1553 joinItem->addChild( childPrefix );
1554
1555 QTreeWidgetItem *childFields = new QTreeWidgetItem();
1556 childFields->setText( 0, tr( "Joined fields" ) );
1557 const QStringList *list = join.joinFieldNamesSubset();
1558 if ( list )
1559 childFields->setText( 1, QString::number( list->count() ) );
1560 else
1561 childFields->setText( 1, tr( "all" ) );
1562 joinItem->addChild( childFields );
1563
1564 if ( insertIndex >= 0 )
1565 mJoinTreeWidget->insertTopLevelItem( insertIndex, joinItem );
1566 else
1567 mJoinTreeWidget->addTopLevelItem( joinItem );
1568
1569 mJoinTreeWidget->setCurrentItem( joinItem );
1570 mJoinTreeWidget->header()->setSectionResizeMode( QHeaderView::ResizeToContents );
1571 }
1572
createExpressionContext() const1573 QgsExpressionContext QgsVectorLayerProperties::createExpressionContext() const
1574 {
1575 return mContext;
1576 }
1577
openPanel(QgsPanelWidget * panel)1578 void QgsVectorLayerProperties::openPanel( QgsPanelWidget *panel )
1579 {
1580 QDialog *dlg = new QDialog();
1581 QString key = QStringLiteral( "/UI/paneldialog/%1" ).arg( panel->panelTitle() );
1582 QgsSettings settings;
1583 dlg->restoreGeometry( settings.value( key ).toByteArray() );
1584 dlg->setWindowTitle( panel->panelTitle() );
1585 dlg->setLayout( new QVBoxLayout() );
1586 dlg->layout()->addWidget( panel );
1587 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok );
1588 connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
1589 dlg->layout()->addWidget( buttonBox );
1590 dlg->exec();
1591 settings.setValue( key, dlg->saveGeometry() );
1592 panel->acceptPanel();
1593 }
1594
mButtonRemoveJoin_clicked()1595 void QgsVectorLayerProperties::mButtonRemoveJoin_clicked()
1596 {
1597 QTreeWidgetItem *currentJoinItem = mJoinTreeWidget->currentItem();
1598 if ( !mLayer || !currentJoinItem )
1599 {
1600 return;
1601 }
1602
1603 mLayer->removeJoin( currentJoinItem->data( 0, Qt::UserRole ).toString() );
1604 mJoinTreeWidget->takeTopLevelItem( mJoinTreeWidget->indexOfTopLevelItem( currentJoinItem ) );
1605 setPbnQueryBuilderEnabled();
1606 mSourceFieldsPropertiesDialog->init();
1607 mAttributesFormPropertiesDialog->init();
1608 }
1609
1610
mButtonAddWmsDimension_clicked()1611 void QgsVectorLayerProperties::mButtonAddWmsDimension_clicked()
1612 {
1613 if ( !mLayer )
1614 return;
1615
1616 // get wms dimensions name
1617 QStringList alreadyDefinedDimensions;
1618 const QList<QgsVectorLayerServerProperties::WmsDimensionInfo> &dims = mLayer->serverProperties()->wmsDimensions();
1619 for ( const QgsVectorLayerServerProperties::WmsDimensionInfo &dim : dims )
1620 {
1621 alreadyDefinedDimensions << dim.name;
1622 }
1623
1624 QgsWmsDimensionDialog d( mLayer, alreadyDefinedDimensions );
1625 if ( d.exec() == QDialog::Accepted )
1626 {
1627 QgsVectorLayerServerProperties::WmsDimensionInfo info = d.info();
1628 // save dimension
1629 mLayer->serverProperties()->addWmsDimension( info );
1630 addWmsDimensionInfoToTreeWidget( info );
1631 }
1632 }
1633
mButtonEditWmsDimension_clicked()1634 void QgsVectorLayerProperties::mButtonEditWmsDimension_clicked()
1635 {
1636 QTreeWidgetItem *currentWmsDimensionItem = mWmsDimensionsTreeWidget->currentItem();
1637 mWmsDimensionsTreeWidget_itemDoubleClicked( currentWmsDimensionItem, 0 );
1638 }
1639
mWmsDimensionsTreeWidget_itemDoubleClicked(QTreeWidgetItem * item,int)1640 void QgsVectorLayerProperties::mWmsDimensionsTreeWidget_itemDoubleClicked( QTreeWidgetItem *item, int )
1641 {
1642 if ( !mLayer || !item )
1643 {
1644 return;
1645 }
1646
1647 QString wmsDimName = item->data( 0, Qt::UserRole ).toString();
1648 const QList<QgsVectorLayerServerProperties::WmsDimensionInfo> &dims = mLayer->serverProperties()->wmsDimensions();
1649 QStringList alreadyDefinedDimensions;
1650 int j = -1;
1651 for ( int i = 0; i < dims.size(); ++i )
1652 {
1653 QString dimName = dims[i].name;
1654 if ( dimName == wmsDimName )
1655 {
1656 j = i;
1657 }
1658 else
1659 {
1660 alreadyDefinedDimensions << dimName;
1661 }
1662 }
1663 if ( j == -1 )
1664 {
1665 return;
1666 }
1667
1668 QgsWmsDimensionDialog d( mLayer, alreadyDefinedDimensions );
1669 d.setWindowTitle( tr( "Edit WMS Dimension" ) );
1670 d.setInfo( dims[j] );
1671
1672 if ( d.exec() == QDialog::Accepted )
1673 {
1674 QgsVectorLayerServerProperties::WmsDimensionInfo info = d.info();
1675
1676 // remove old
1677 mLayer->serverProperties()->removeWmsDimension( wmsDimName );
1678 int idx = mWmsDimensionsTreeWidget->indexOfTopLevelItem( item );
1679 mWmsDimensionsTreeWidget->takeTopLevelItem( idx );
1680
1681 // save new
1682 mLayer->serverProperties()->addWmsDimension( info );
1683 addWmsDimensionInfoToTreeWidget( info, idx );
1684 }
1685 }
1686
addWmsDimensionInfoToTreeWidget(const QgsVectorLayerServerProperties::WmsDimensionInfo & wmsDim,const int insertIndex)1687 void QgsVectorLayerProperties::addWmsDimensionInfoToTreeWidget( const QgsVectorLayerServerProperties::WmsDimensionInfo &wmsDim, const int insertIndex )
1688 {
1689 QTreeWidgetItem *wmsDimensionItem = new QTreeWidgetItem();
1690 wmsDimensionItem->setFlags( Qt::ItemIsEnabled );
1691
1692 wmsDimensionItem->setText( 0, tr( "Dimension" ) );
1693 wmsDimensionItem->setText( 1, wmsDim.name );
1694
1695 QFont f = wmsDimensionItem->font( 0 );
1696 f.setBold( true );
1697 wmsDimensionItem->setFont( 0, f );
1698 wmsDimensionItem->setFont( 1, f );
1699
1700 wmsDimensionItem->setData( 0, Qt::UserRole, wmsDim.name );
1701
1702 QTreeWidgetItem *childWmsDimensionField = new QTreeWidgetItem();
1703 childWmsDimensionField->setText( 0, tr( "Field" ) );
1704 childWmsDimensionField->setText( 1, wmsDim.fieldName );
1705 childWmsDimensionField->setFlags( Qt::ItemIsEnabled );
1706 wmsDimensionItem->addChild( childWmsDimensionField );
1707
1708 QTreeWidgetItem *childWmsDimensionEndField = new QTreeWidgetItem();
1709 childWmsDimensionEndField->setText( 0, tr( "End field" ) );
1710 childWmsDimensionEndField->setText( 1, wmsDim.endFieldName );
1711 childWmsDimensionEndField->setFlags( Qt::ItemIsEnabled );
1712 wmsDimensionItem->addChild( childWmsDimensionEndField );
1713
1714 QTreeWidgetItem *childWmsDimensionUnits = new QTreeWidgetItem();
1715 childWmsDimensionUnits->setText( 0, tr( "Units" ) );
1716 childWmsDimensionUnits->setText( 1, wmsDim.units );
1717 childWmsDimensionUnits->setFlags( Qt::ItemIsEnabled );
1718 wmsDimensionItem->addChild( childWmsDimensionUnits );
1719
1720 QTreeWidgetItem *childWmsDimensionUnitSymbol = new QTreeWidgetItem();
1721 childWmsDimensionUnitSymbol->setText( 0, tr( "Unit symbol" ) );
1722 childWmsDimensionUnitSymbol->setText( 1, wmsDim.unitSymbol );
1723 childWmsDimensionUnitSymbol->setFlags( Qt::ItemIsEnabled );
1724 wmsDimensionItem->addChild( childWmsDimensionUnitSymbol );
1725
1726 QTreeWidgetItem *childWmsDimensionDefaultValue = new QTreeWidgetItem();
1727 childWmsDimensionDefaultValue->setText( 0, tr( "Default display" ) );
1728 childWmsDimensionDefaultValue->setText( 1, QgsVectorLayerServerProperties::wmsDimensionDefaultDisplayLabels()[wmsDim.defaultDisplayType] );
1729 childWmsDimensionDefaultValue->setFlags( Qt::ItemIsEnabled );
1730 wmsDimensionItem->addChild( childWmsDimensionDefaultValue );
1731
1732 QTreeWidgetItem *childWmsDimensionRefValue = new QTreeWidgetItem();
1733 childWmsDimensionRefValue->setText( 0, tr( "Reference value" ) );
1734 childWmsDimensionRefValue->setText( 1, wmsDim.referenceValue.toString() );
1735 childWmsDimensionRefValue->setFlags( Qt::ItemIsEnabled );
1736 wmsDimensionItem->addChild( childWmsDimensionRefValue );
1737
1738 if ( insertIndex >= 0 )
1739 mWmsDimensionsTreeWidget->insertTopLevelItem( insertIndex, wmsDimensionItem );
1740 else
1741 mWmsDimensionsTreeWidget->addTopLevelItem( wmsDimensionItem );
1742
1743 mWmsDimensionsTreeWidget->setCurrentItem( wmsDimensionItem );
1744 mWmsDimensionsTreeWidget->header()->setSectionResizeMode( QHeaderView::ResizeToContents );
1745 }
1746
mButtonRemoveWmsDimension_clicked()1747 void QgsVectorLayerProperties::mButtonRemoveWmsDimension_clicked()
1748 {
1749 QTreeWidgetItem *currentWmsDimensionItem = mWmsDimensionsTreeWidget->currentItem();
1750 if ( !mLayer || !currentWmsDimensionItem )
1751 {
1752 return;
1753 }
1754
1755 mLayer->serverProperties()->removeWmsDimension( currentWmsDimensionItem->data( 0, Qt::UserRole ).toString() );
1756 mWmsDimensionsTreeWidget->takeTopLevelItem( mWmsDimensionsTreeWidget->indexOfTopLevelItem( currentWmsDimensionItem ) );
1757 }
1758
1759
updateSymbologyPage()1760 void QgsVectorLayerProperties::updateSymbologyPage()
1761 {
1762
1763 //find out the type of renderer in the vectorlayer, create a dialog with these settings and add it to the form
1764 delete mRendererDialog;
1765 mRendererDialog = nullptr;
1766
1767 if ( mLayer->renderer() )
1768 {
1769 mRendererDialog = new QgsRendererPropertiesDialog( mLayer, QgsStyle::defaultStyle(), true, this );
1770 mRendererDialog->setDockMode( false );
1771 QgsSymbolWidgetContext context;
1772 context.setMapCanvas( mCanvas );
1773 context.setMessageBar( mMessageBar );
1774 mRendererDialog->setContext( context );
1775 connect( mRendererDialog, &QgsRendererPropertiesDialog::showPanel, this, &QgsVectorLayerProperties::openPanel );
1776 connect( mRendererDialog, &QgsRendererPropertiesDialog::layerVariablesChanged, this, &QgsVectorLayerProperties::updateVariableEditor );
1777 connect( mRendererDialog, &QgsRendererPropertiesDialog::widgetChanged, this, [ = ] { updateAuxiliaryStoragePage(); } );
1778 }
1779 else
1780 {
1781 mOptsPage_Style->setEnabled( false ); // hide symbology item
1782 }
1783
1784 if ( mRendererDialog )
1785 {
1786 mRendererDialog->layout()->setContentsMargins( 0, 0, 0, 0 );
1787 widgetStackRenderers->addWidget( mRendererDialog );
1788 widgetStackRenderers->setCurrentWidget( mRendererDialog );
1789 widgetStackRenderers->currentWidget()->layout()->setContentsMargins( 0, 0, 0, 0 );
1790 }
1791 }
1792
setPbnQueryBuilderEnabled()1793 void QgsVectorLayerProperties::setPbnQueryBuilderEnabled()
1794 {
1795 pbnQueryBuilder->setEnabled( mLayer &&
1796 mLayer->dataProvider() &&
1797 mLayer->dataProvider()->supportsSubsetString() &&
1798 !mLayer->isEditable() );
1799
1800 if ( mLayer && mLayer->isEditable() )
1801 {
1802 pbnQueryBuilder->setToolTip( tr( "Stop editing mode to enable this." ) );
1803 }
1804
1805 }
1806
pbnUpdateExtents_clicked()1807 void QgsVectorLayerProperties::pbnUpdateExtents_clicked()
1808 {
1809 mLayer->updateExtents( true ); // force update whatever options activated
1810 mMetadataFilled = false;
1811 }
1812
optionsStackedWidget_CurrentChanged(int index)1813 void QgsVectorLayerProperties::optionsStackedWidget_CurrentChanged( int index )
1814 {
1815 QgsOptionsDialogBase::optionsStackedWidget_CurrentChanged( index );
1816
1817 bool isMetadataPanel = ( index == mOptStackedWidget->indexOf( mOptsPage_Metadata ) );
1818 mBtnStyle->setVisible( ! isMetadataPanel );
1819 mBtnMetadata->setVisible( isMetadataPanel );
1820
1821 if ( index == mOptStackedWidget->indexOf( mOptsPage_Information ) && ! mMetadataFilled )
1822 {
1823 //set the metadata contents (which can be expensive)
1824 teMetadataViewer->clear();
1825 teMetadataViewer->setHtml( htmlMetadata() );
1826 mMetadataFilled = true;
1827 }
1828
1829 resizeAlltabs( index );
1830 }
1831
mSimplifyDrawingGroupBox_toggled(bool checked)1832 void QgsVectorLayerProperties::mSimplifyDrawingGroupBox_toggled( bool checked )
1833 {
1834 const QgsVectorDataProvider *provider = mLayer->dataProvider();
1835 if ( !( provider && ( provider->capabilities() & QgsVectorDataProvider::SimplifyGeometries ) != 0 ) )
1836 {
1837 mSimplifyDrawingAtProvider->setEnabled( false );
1838 }
1839 else
1840 {
1841 mSimplifyDrawingAtProvider->setEnabled( checked );
1842 }
1843 }
1844
updateVariableEditor()1845 void QgsVectorLayerProperties::updateVariableEditor()
1846 {
1847 QgsExpressionContext context;
1848 mVariableEditor->setContext( &context );
1849 mVariableEditor->context()->appendScope( QgsExpressionContextUtils::globalScope() );
1850 mVariableEditor->context()->appendScope( QgsExpressionContextUtils::projectScope( QgsProject::instance() ) );
1851 mVariableEditor->context()->appendScope( QgsExpressionContextUtils::layerScope( mLayer ) );
1852 mVariableEditor->reloadContext();
1853 mVariableEditor->setEditableScopeIndex( 2 );
1854 }
1855
showHelp()1856 void QgsVectorLayerProperties::showHelp()
1857 {
1858 const QVariant helpPage = mOptionsStackedWidget->currentWidget()->property( "helpPage" );
1859
1860 if ( helpPage.isValid() )
1861 {
1862 QgsHelp::openHelp( helpPage.toString() );
1863 }
1864 else
1865 {
1866 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html" ) );
1867 }
1868 }
1869
updateAuxiliaryStoragePage()1870 void QgsVectorLayerProperties::updateAuxiliaryStoragePage()
1871 {
1872 const QgsAuxiliaryLayer *alayer = mLayer->auxiliaryLayer();
1873
1874 if ( alayer )
1875 {
1876 // set widgets to enable state
1877 mAuxiliaryStorageInformationGrpBox->setEnabled( true );
1878 mAuxiliaryStorageFieldsGrpBox->setEnabled( true );
1879
1880 // update key
1881 mAuxiliaryStorageKeyLineEdit->setText( alayer->joinInfo().targetFieldName() );
1882
1883 // update feature count
1884 long features = alayer->featureCount();
1885 mAuxiliaryStorageFeaturesLineEdit->setText( QString::number( features ) );
1886
1887 // update actions
1888 mAuxiliaryLayerActionClear->setEnabled( true );
1889 mAuxiliaryLayerActionDelete->setEnabled( true );
1890 mAuxiliaryLayerActionExport->setEnabled( true );
1891 mAuxiliaryLayerActionNew->setEnabled( false );
1892
1893 const QgsAuxiliaryLayer *alayer = mLayer->auxiliaryLayer();
1894 if ( alayer )
1895 {
1896 const int fields = alayer->auxiliaryFields().count();
1897 mAuxiliaryStorageFieldsLineEdit->setText( QString::number( fields ) );
1898
1899 // add fields
1900 mAuxiliaryStorageFieldsTree->clear();
1901 for ( const QgsField &field : alayer->auxiliaryFields() )
1902 {
1903 const QgsPropertyDefinition prop = QgsAuxiliaryLayer::propertyDefinitionFromField( field );
1904 QTreeWidgetItem *item = new QTreeWidgetItem();
1905
1906 item->setText( 0, prop.origin() );
1907 item->setText( 1, prop.name() );
1908 item->setText( 2, prop.comment() );
1909 item->setText( 3, field.typeName() );
1910 item->setText( 4, field.name() );
1911
1912 mAuxiliaryStorageFieldsTree->addTopLevelItem( item );
1913 }
1914 }
1915 }
1916 else
1917 {
1918 mAuxiliaryStorageInformationGrpBox->setEnabled( false );
1919 mAuxiliaryStorageFieldsGrpBox->setEnabled( false );
1920
1921 mAuxiliaryLayerActionClear->setEnabled( false );
1922 mAuxiliaryLayerActionDelete->setEnabled( false );
1923 mAuxiliaryLayerActionExport->setEnabled( false );
1924 mAuxiliaryLayerActionNew->setEnabled( true );
1925
1926 mAuxiliaryStorageFieldsTree->clear();
1927 mAuxiliaryStorageKeyLineEdit->setText( QString() );
1928 mAuxiliaryStorageFieldsLineEdit->setText( QString() );
1929 mAuxiliaryStorageFeaturesLineEdit->setText( QString() );
1930 }
1931 }
1932
onAuxiliaryLayerNew()1933 void QgsVectorLayerProperties::onAuxiliaryLayerNew()
1934 {
1935 QgsAuxiliaryLayer *alayer = mLayer->auxiliaryLayer();
1936
1937 if ( alayer )
1938 return;
1939
1940 QgsNewAuxiliaryLayerDialog dlg( mLayer, this );
1941 if ( dlg.exec() == QDialog::Accepted )
1942 {
1943 updateAuxiliaryStoragePage();
1944 }
1945 }
1946
onAuxiliaryLayerClear()1947 void QgsVectorLayerProperties::onAuxiliaryLayerClear()
1948 {
1949 QgsAuxiliaryLayer *alayer = mLayer->auxiliaryLayer();
1950
1951 if ( !alayer )
1952 return;
1953
1954 const QString msg = tr( "Are you sure you want to clear auxiliary data for %1?" ).arg( mLayer->name() );
1955 QMessageBox::StandardButton reply;
1956 reply = QMessageBox::question( this, "Clear Auxiliary Data", msg, QMessageBox::Yes | QMessageBox::No );
1957
1958 if ( reply == QMessageBox::Yes )
1959 {
1960 QApplication::setOverrideCursor( Qt::WaitCursor );
1961 alayer->clear();
1962 QApplication::restoreOverrideCursor();
1963 updateAuxiliaryStoragePage();
1964 mLayer->triggerRepaint();
1965 }
1966 }
1967
onAuxiliaryLayerDelete()1968 void QgsVectorLayerProperties::onAuxiliaryLayerDelete()
1969 {
1970 QgsAuxiliaryLayer *alayer = mLayer->auxiliaryLayer();
1971 if ( !alayer )
1972 return;
1973
1974 const QString msg = tr( "Are you sure you want to delete auxiliary storage for %1?" ).arg( mLayer->name() );
1975 QMessageBox::StandardButton reply;
1976 reply = QMessageBox::question( this, "Delete Auxiliary Storage", msg, QMessageBox::Yes | QMessageBox::No );
1977
1978 if ( reply == QMessageBox::Yes )
1979 {
1980 QApplication::setOverrideCursor( Qt::WaitCursor );
1981 QgsDataSourceUri uri( alayer->source() );
1982
1983 // delete each attribute to correctly update layer settings and data
1984 // defined buttons
1985 while ( alayer->auxiliaryFields().size() > 0 )
1986 {
1987 QgsField aField = alayer->auxiliaryFields()[0];
1988 deleteAuxiliaryField( alayer->fields().indexOf( aField.name() ) );
1989 }
1990
1991 mLayer->setAuxiliaryLayer(); // remove auxiliary layer
1992 QgsAuxiliaryStorage::deleteTable( uri );
1993 QApplication::restoreOverrideCursor();
1994 updateAuxiliaryStoragePage();
1995 mLayer->triggerRepaint();
1996 }
1997 }
1998
onAuxiliaryLayerDeleteField()1999 void QgsVectorLayerProperties::onAuxiliaryLayerDeleteField()
2000 {
2001 QgsAuxiliaryLayer *alayer = mLayer->auxiliaryLayer();
2002 if ( !alayer )
2003 return;
2004
2005 QList<QTreeWidgetItem *> items = mAuxiliaryStorageFieldsTree->selectedItems();
2006 if ( items.count() < 1 )
2007 return;
2008
2009 // get auxiliary field name and index from item
2010 const QTreeWidgetItem *item = items[0];
2011 QgsPropertyDefinition def;
2012 def.setOrigin( item->text( 0 ) );
2013 def.setName( item->text( 1 ) );
2014 def.setComment( item->text( 2 ) );
2015
2016 const QString fieldName = QgsAuxiliaryLayer::nameFromProperty( def );
2017
2018 const int index = mLayer->auxiliaryLayer()->fields().indexOf( fieldName );
2019 if ( index < 0 )
2020 return;
2021
2022 // should be only 1 field
2023 const QString msg = tr( "Are you sure you want to delete auxiliary field %1 for %2?" ).arg( item->text( 1 ), item->text( 0 ) );
2024
2025 QMessageBox::StandardButton reply;
2026 const QString title = QObject::tr( "Delete Auxiliary Field" );
2027 reply = QMessageBox::question( this, title, msg, QMessageBox::Yes | QMessageBox::No );
2028
2029 if ( reply == QMessageBox::Yes )
2030 {
2031 QApplication::setOverrideCursor( Qt::WaitCursor );
2032 deleteAuxiliaryField( index );
2033 mLayer->triggerRepaint();
2034 QApplication::restoreOverrideCursor();
2035 }
2036 }
2037
onAuxiliaryLayerAddField()2038 void QgsVectorLayerProperties::onAuxiliaryLayerAddField()
2039 {
2040 QgsAuxiliaryLayer *alayer = mLayer->auxiliaryLayer();
2041 if ( !alayer )
2042 return;
2043
2044 QgsNewAuxiliaryFieldDialog dlg( QgsPropertyDefinition(), mLayer, false );
2045 if ( dlg.exec() == QDialog::Accepted )
2046 {
2047 updateAuxiliaryStoragePage();
2048 }
2049 }
2050
deleteAuxiliaryField(int index)2051 void QgsVectorLayerProperties::deleteAuxiliaryField( int index )
2052 {
2053 if ( !mLayer->auxiliaryLayer() )
2054 return;
2055
2056 int key = mLayer->auxiliaryLayer()->propertyFromIndex( index );
2057 QgsPropertyDefinition def = mLayer->auxiliaryLayer()->propertyDefinitionFromIndex( index );
2058
2059 if ( mLayer->auxiliaryLayer()->deleteAttribute( index ) )
2060 {
2061 mLayer->updateFields();
2062
2063 // immediately deactivate data defined button
2064 if ( key >= 0 && def.origin().compare( "labeling", Qt::CaseInsensitive ) == 0
2065 && labelingDialog
2066 && labelingDialog->labelingGui() )
2067 {
2068 labelingDialog->labelingGui()->deactivateField( static_cast<QgsPalLayerSettings::Property>( key ) );
2069 }
2070
2071 updateAuxiliaryStoragePage();
2072 mSourceFieldsPropertiesDialog->init();
2073 }
2074 else
2075 {
2076 const QString title = QObject::tr( "Delete Auxiliary Field" );
2077 const int timeout = QgsSettings().value( QStringLiteral( "qgis/messageTimeout" ), 5 ).toInt();
2078 const QString errors = mLayer->auxiliaryLayer()->commitErrors().join( QLatin1String( "\n " ) );
2079 const QString msg = QObject::tr( "Unable to remove auxiliary field (%1)" ).arg( errors );
2080 mMessageBar->pushMessage( title, msg, Qgis::Warning, timeout );
2081 }
2082 }
2083