mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-17 00:04:02 -04:00
setMaximumScale() and setMinimumScale(), maximumScale() and minimumScale() had the opposite meaning to other min/max scales in the API, and were the opposite to how these settings were exposed in the GUI. This lead to very confusing API!! Their definitions have now been swapped. setMaximumScale now sets the maximum (i.e. largest scale, or most zoomed in) at which the layer will appear, and setMinimumScale now sets the minimum (i.e. smallest scale, or most zoomed out) at which the layer will appear. The same is true for the maximumScale and minimumScale getters.
1396 lines
46 KiB
C++
1396 lines
46 KiB
C++
/***************************************************************************
|
|
qgsdlgvectorlayerproperties.cpp
|
|
Unified property dialog for vector layers
|
|
-------------------
|
|
begin : 2004-01-28
|
|
copyright : (C) 2004 by Gary E.Sherman
|
|
email : sherman at mrcc.com
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include <memory>
|
|
#include <limits>
|
|
|
|
#include "qgisapp.h"
|
|
#include "qgsactionmanager.h"
|
|
#include "qgsjoindialog.h"
|
|
#include "qgsapplication.h"
|
|
#include "qgsattributeactiondialog.h"
|
|
#include "qgsapplydialog.h"
|
|
#include "qgscontexthelp.h"
|
|
#include "qgscoordinatetransform.h"
|
|
#include "qgsdiagramproperties.h"
|
|
#include "qgsdiagramrenderer.h"
|
|
#include "qgsexpressionbuilderdialog.h"
|
|
#include "qgsfieldcalculator.h"
|
|
#include "qgsfieldsproperties.h"
|
|
#include "qgslabelingwidget.h"
|
|
#include "qgsprojectionselectiondialog.h"
|
|
#include "qgslogger.h"
|
|
#include "qgsmapcanvas.h"
|
|
#include "qgsmaplayerconfigwidgetfactory.h"
|
|
#include "qgsmaplayerstyleguiutils.h"
|
|
#include "qgspluginmetadata.h"
|
|
#include "qgspluginregistry.h"
|
|
#include "qgsproject.h"
|
|
#include "qgssavestyletodbdialog.h"
|
|
#include "qgsloadstylefromdbdialog.h"
|
|
#include "qgsvectorlayer.h"
|
|
#include "qgsvectorlayerjoininfo.h"
|
|
#include "qgsvectorlayerproperties.h"
|
|
#include "qgsconfig.h"
|
|
#include "qgsvectordataprovider.h"
|
|
#include "qgsquerybuilder.h"
|
|
#include "qgsdatasourceuri.h"
|
|
#include "qgsrenderer.h"
|
|
#include "qgsexpressioncontext.h"
|
|
#include "qgssettings.h"
|
|
#include "qgsrendererpropertiesdialog.h"
|
|
#include "qgsstyle.h"
|
|
|
|
#include "layertree/qgslayertreelayer.h"
|
|
#include "qgslayertree.h"
|
|
|
|
#include <QMessageBox>
|
|
#include <QDir>
|
|
#include <QFile>
|
|
#include <QFileDialog>
|
|
#include <QFileInfo>
|
|
#include <QFontDialog>
|
|
#include <QComboBox>
|
|
#include <QCheckBox>
|
|
#include <QHeaderView>
|
|
#include <QColorDialog>
|
|
|
|
#include "qgsrendererpropertiesdialog.h"
|
|
#include "qgsstyle.h"
|
|
|
|
QgsVectorLayerProperties::QgsVectorLayerProperties(
|
|
QgsVectorLayer *lyr,
|
|
QWidget *parent,
|
|
Qt::WindowFlags fl
|
|
)
|
|
: QgsOptionsDialogBase( QStringLiteral( "VectorLayerProperties" ), parent, fl )
|
|
, mLayer( lyr )
|
|
, mMetadataFilled( false )
|
|
, mOriginalSubsetSQL( lyr->subsetString() )
|
|
, mSaveAsMenu( nullptr )
|
|
, mLoadStyleMenu( nullptr )
|
|
, mRendererDialog( nullptr )
|
|
, labelingDialog( nullptr )
|
|
, mActionDialog( nullptr )
|
|
, diagramPropertiesDialog( nullptr )
|
|
, mFieldsPropertiesDialog( nullptr )
|
|
{
|
|
setupUi( this );
|
|
// QgsOptionsDialogBase handles saving/restoring of geometry, splitter and current tab states,
|
|
// switching vertical tabs between icon/text to icon-only modes (splitter collapsed to left),
|
|
// and connecting QDialogButtonBox's accepted/rejected signals to dialog's accept/reject slots
|
|
initOptionsBase( false );
|
|
|
|
QPushButton *b = new QPushButton( tr( "Style" ) );
|
|
QMenu *m = new QMenu( this );
|
|
mActionLoadStyle = m->addAction( tr( "Load Style" ), this, SLOT( loadStyle_clicked() ) );
|
|
mActionSaveStyleAs = m->addAction( tr( "Save Style" ), this, SLOT( saveStyleAs_clicked() ) );
|
|
m->addSeparator();
|
|
m->addAction( tr( "Save as Default" ), this, SLOT( saveDefaultStyle_clicked() ) );
|
|
m->addAction( tr( "Restore Default" ), this, SLOT( loadDefaultStyle_clicked() ) );
|
|
b->setMenu( m );
|
|
connect( m, &QMenu::aboutToShow, this, &QgsVectorLayerProperties::aboutToShowStyleMenu );
|
|
buttonBox->addButton( b, QDialogButtonBox::ResetRole );
|
|
|
|
connect( lyr->styleManager(), &QgsMapLayerStyleManager::currentStyleChanged, this, &QgsVectorLayerProperties::syncToLayer );
|
|
|
|
connect( buttonBox->button( QDialogButtonBox::Apply ), &QAbstractButton::clicked, this, &QgsVectorLayerProperties::apply );
|
|
connect( this, &QDialog::accepted, this, &QgsVectorLayerProperties::apply );
|
|
connect( this, &QDialog::rejected, this, &QgsVectorLayerProperties::onCancel );
|
|
|
|
connect( mOptionsStackedWidget, &QStackedWidget::currentChanged, this, &QgsVectorLayerProperties::mOptionsStackedWidget_CurrentChanged );
|
|
|
|
mContext << QgsExpressionContextUtils::globalScope()
|
|
<< QgsExpressionContextUtils::projectScope( QgsProject::instance() )
|
|
<< QgsExpressionContextUtils::atlasScope( nullptr )
|
|
<< QgsExpressionContextUtils::mapSettingsScope( QgisApp::instance()->mapCanvas()->mapSettings() )
|
|
<< QgsExpressionContextUtils::layerScope( mLayer );
|
|
|
|
mMapTipExpressionFieldWidget->setLayer( lyr );
|
|
mMapTipExpressionFieldWidget->registerExpressionContextGenerator( this );
|
|
mDisplayExpressionWidget->setLayer( lyr );
|
|
mDisplayExpressionWidget->registerExpressionContextGenerator( this );
|
|
|
|
connect( mInsertExpressionButton, &QAbstractButton::clicked, this, &QgsVectorLayerProperties::insertFieldOrExpression );
|
|
|
|
if ( !mLayer )
|
|
return;
|
|
|
|
QVBoxLayout *layout = nullptr;
|
|
|
|
if ( mLayer->hasGeometryType() )
|
|
{
|
|
// Create the Labeling dialog tab
|
|
layout = new QVBoxLayout( labelingFrame );
|
|
layout->setMargin( 0 );
|
|
labelingDialog = new QgsLabelingWidget( mLayer, QgisApp::instance()->mapCanvas(), labelingFrame );
|
|
labelingDialog->layout()->setContentsMargins( -1, 0, -1, 0 );
|
|
layout->addWidget( labelingDialog );
|
|
labelingFrame->setLayout( layout );
|
|
}
|
|
else
|
|
{
|
|
labelingDialog = nullptr;
|
|
mOptsPage_Labels->setEnabled( false ); // disable labeling item
|
|
}
|
|
|
|
// Create the Actions dialog tab
|
|
QVBoxLayout *actionLayout = new QVBoxLayout( actionOptionsFrame );
|
|
actionLayout->setMargin( 0 );
|
|
mActionDialog = new QgsAttributeActionDialog( *mLayer->actions(), actionOptionsFrame );
|
|
mActionDialog->layout()->setMargin( 0 );
|
|
actionLayout->addWidget( mActionDialog );
|
|
|
|
// Create the menu for the save style button to choose the output format
|
|
mSaveAsMenu = new QMenu( this );
|
|
mSaveAsMenu->addAction( tr( "QGIS Layer Style File..." ) );
|
|
mSaveAsMenu->addAction( tr( "SLD File..." ) );
|
|
|
|
//Only if the provider support loading & saving styles to db add new choices
|
|
if ( mLayer->dataProvider()->isSaveAndLoadStyleToDatabaseSupported() )
|
|
{
|
|
//for loading
|
|
mLoadStyleMenu = new QMenu( this );
|
|
mLoadStyleMenu->addAction( tr( "Load from file..." ) );
|
|
mLoadStyleMenu->addAction( tr( "Database styles manager" ) );
|
|
//mActionLoadStyle->setContextMenuPolicy( Qt::PreventContextMenu );
|
|
mActionLoadStyle->setMenu( mLoadStyleMenu );
|
|
|
|
connect( mLoadStyleMenu, &QMenu::triggered,
|
|
this, &QgsVectorLayerProperties::loadStyleMenuTriggered );
|
|
|
|
//for saving
|
|
QString providerName = mLayer->providerType();
|
|
if ( providerName == "ogr" )
|
|
{
|
|
providerName = mLayer->dataProvider()->storageType();
|
|
if ( providerName == "GPKG" )
|
|
providerName = "GeoPackage";
|
|
}
|
|
mSaveAsMenu->addAction( tr( "Save in database (%1)" ).arg( providerName ) );
|
|
}
|
|
|
|
connect( mSaveAsMenu, &QMenu::triggered,
|
|
this, &QgsVectorLayerProperties::saveStyleAsMenuTriggered );
|
|
|
|
mFieldsPropertiesDialog = new QgsFieldsProperties( mLayer, mFieldsFrame );
|
|
mFieldsPropertiesDialog->layout()->setMargin( 0 );
|
|
mFieldsFrame->setLayout( new QVBoxLayout( mFieldsFrame ) );
|
|
mFieldsFrame->layout()->setMargin( 0 );
|
|
mFieldsFrame->layout()->addWidget( mFieldsPropertiesDialog );
|
|
|
|
connect( mFieldsPropertiesDialog, &QgsFieldsProperties::toggleEditing, this, static_cast<void ( QgsVectorLayerProperties::* )()>( &QgsVectorLayerProperties::toggleEditing ) );
|
|
connect( this, static_cast<void ( QgsVectorLayerProperties::* )( QgsMapLayer * )>( &QgsVectorLayerProperties::toggleEditing ),
|
|
QgisApp::instance(), [ = ]( QgsMapLayer * layer ) { QgisApp::instance()->toggleEditing( layer ); } );
|
|
|
|
syncToLayer();
|
|
|
|
if ( mLayer->dataProvider() )//enable spatial index button group if supported by provider
|
|
{
|
|
int capabilities = mLayer->dataProvider()->capabilities();
|
|
if ( !( capabilities & QgsVectorDataProvider::CreateSpatialIndex ) )
|
|
{
|
|
pbnIndex->setEnabled( false );
|
|
}
|
|
|
|
if ( capabilities & QgsVectorDataProvider::SelectEncoding )
|
|
{
|
|
cboProviderEncoding->addItems( QgsVectorDataProvider::availableEncodings() );
|
|
QString enc = mLayer->dataProvider()->encoding();
|
|
int encindex = cboProviderEncoding->findText( enc );
|
|
if ( encindex < 0 )
|
|
{
|
|
cboProviderEncoding->insertItem( 0, enc );
|
|
encindex = 0;
|
|
}
|
|
cboProviderEncoding->setCurrentIndex( encindex );
|
|
}
|
|
else if ( mLayer->dataProvider()->name() == QLatin1String( "ogr" ) )
|
|
{
|
|
// if OGR_L_TestCapability(OLCStringsAsUTF8) returns true, OGR provider encoding can be set to only UTF-8
|
|
// so make encoding box grayed out
|
|
cboProviderEncoding->addItem( mLayer->dataProvider()->encoding() );
|
|
cboProviderEncoding->setEnabled( false );
|
|
}
|
|
else
|
|
{
|
|
// other providers do not use mEncoding, so hide the group completely
|
|
mDataSourceEncodingFrame->hide();
|
|
}
|
|
}
|
|
|
|
mCrsSelector->setCrs( mLayer->crs() );
|
|
|
|
//insert existing join info
|
|
const QList< QgsVectorLayerJoinInfo > &joins = mLayer->vectorJoins();
|
|
for ( int i = 0; i < joins.size(); ++i )
|
|
{
|
|
addJoinToTreeWidget( joins[i] );
|
|
}
|
|
|
|
mOldJoins = mLayer->vectorJoins();
|
|
|
|
QVBoxLayout *diagLayout = new QVBoxLayout( mDiagramFrame );
|
|
diagLayout->setMargin( 0 );
|
|
diagramPropertiesDialog = new QgsDiagramProperties( mLayer, mDiagramFrame, QgisApp::instance()->mapCanvas() );
|
|
diagramPropertiesDialog->layout()->setContentsMargins( -1, 0, -1, 0 );
|
|
diagLayout->addWidget( diagramPropertiesDialog );
|
|
mDiagramFrame->setLayout( diagLayout );
|
|
|
|
// Legend tab
|
|
mLegendConfigEmbeddedWidget->setLayer( mLayer );
|
|
|
|
// WMS Name as layer short name
|
|
mLayerShortNameLineEdit->setText( mLayer->shortName() );
|
|
// WMS Name validator
|
|
QValidator *shortNameValidator = new QRegExpValidator( QgsApplication::shortNameRegExp(), this );
|
|
mLayerShortNameLineEdit->setValidator( shortNameValidator );
|
|
|
|
//layer title and abstract
|
|
mLayerTitleLineEdit->setText( mLayer->title() );
|
|
mLayerAbstractTextEdit->setPlainText( mLayer->abstract() );
|
|
mLayerKeywordListLineEdit->setText( mLayer->keywordList() );
|
|
mLayerDataUrlLineEdit->setText( mLayer->dataUrl() );
|
|
mLayerDataUrlFormatComboBox->setCurrentIndex(
|
|
mLayerDataUrlFormatComboBox->findText(
|
|
mLayer->dataUrlFormat()
|
|
)
|
|
);
|
|
//layer attribution and metadataUrl
|
|
mLayerAttributionLineEdit->setText( mLayer->attribution() );
|
|
mLayerAttributionUrlLineEdit->setText( mLayer->attributionUrl() );
|
|
mLayerMetadataUrlLineEdit->setText( mLayer->metadataUrl() );
|
|
mLayerMetadataUrlTypeComboBox->setCurrentIndex(
|
|
mLayerMetadataUrlTypeComboBox->findText(
|
|
mLayer->metadataUrlType()
|
|
)
|
|
);
|
|
mLayerMetadataUrlFormatComboBox->setCurrentIndex(
|
|
mLayerMetadataUrlFormatComboBox->findText(
|
|
mLayer->metadataUrlFormat()
|
|
)
|
|
);
|
|
mLayerLegendUrlLineEdit->setText( mLayer->legendUrl() );
|
|
mLayerLegendUrlFormatComboBox->setCurrentIndex(
|
|
mLayerLegendUrlFormatComboBox->findText(
|
|
mLayer->legendUrlFormat()
|
|
)
|
|
);
|
|
|
|
QString myStyle = QgsApplication::reportStyleSheet();
|
|
myStyle.append( QStringLiteral( "body { margin: 10px; }\n " ) );
|
|
teMetadataViewer->clear();
|
|
teMetadataViewer->document()->setDefaultStyleSheet( myStyle );
|
|
teMetadataViewer->setHtml( htmlMetadata() );
|
|
mMetadataFilled = true;
|
|
|
|
QgsSettings settings;
|
|
// if dialog hasn't been opened/closed yet, default to Styles tab, which is used most often
|
|
// this will be read by restoreOptionsBaseUi()
|
|
if ( !settings.contains( QStringLiteral( "/Windows/VectorLayerProperties/tab" ) ) )
|
|
{
|
|
settings.setValue( QStringLiteral( "Windows/VectorLayerProperties/tab" ),
|
|
mOptStackedWidget->indexOf( mOptsPage_Style ) );
|
|
}
|
|
|
|
QString title = QString( tr( "Layer Properties - %1" ) ).arg( mLayer->name() );
|
|
restoreOptionsBaseUi( title );
|
|
|
|
mLayersDependenciesTreeGroup.reset( QgsProject::instance()->layerTreeRoot()->clone() );
|
|
QgsLayerTreeLayer *layer = mLayersDependenciesTreeGroup->findLayer( mLayer->id() );
|
|
if ( layer )
|
|
{
|
|
layer->parent()->takeChild( layer );
|
|
}
|
|
mLayersDependenciesTreeModel.reset( new QgsLayerTreeModel( mLayersDependenciesTreeGroup.get() ) );
|
|
// use visibility as selection
|
|
mLayersDependenciesTreeModel->setFlag( QgsLayerTreeModel::AllowNodeChangeVisibility );
|
|
|
|
mLayersDependenciesTreeGroup->setItemVisibilityChecked( false );
|
|
|
|
QSet<QString> dependencySources;
|
|
Q_FOREACH ( const QgsMapLayerDependency &dep, mLayer->dependencies() )
|
|
{
|
|
dependencySources << dep.layerId();
|
|
}
|
|
Q_FOREACH ( QgsLayerTreeLayer *layer, mLayersDependenciesTreeGroup->findLayers() )
|
|
{
|
|
layer->setItemVisibilityChecked( dependencySources.contains( layer->layerId() ) );
|
|
}
|
|
|
|
mLayersDependenciesTreeView->setModel( mLayersDependenciesTreeModel.get() );
|
|
|
|
connect( mRefreshLayerCheckBox, &QCheckBox::toggled, mRefreshLayerIntervalSpinBox, &QDoubleSpinBox::setEnabled );
|
|
|
|
} // QgsVectorLayerProperties ctor
|
|
|
|
|
|
QgsVectorLayerProperties::~QgsVectorLayerProperties()
|
|
{
|
|
}
|
|
|
|
void QgsVectorLayerProperties::toggleEditing()
|
|
{
|
|
if ( !mLayer )
|
|
return;
|
|
|
|
emit toggleEditing( mLayer );
|
|
|
|
setPbnQueryBuilderEnabled();
|
|
}
|
|
|
|
void QgsVectorLayerProperties::addPropertiesPageFactory( QgsMapLayerConfigWidgetFactory *factory )
|
|
{
|
|
if ( !factory->supportLayerPropertiesDialog() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
QListWidgetItem *item = new QListWidgetItem();
|
|
item->setIcon( factory->icon() );
|
|
item->setText( factory->title() );
|
|
item->setToolTip( factory->title() );
|
|
|
|
mOptionsListWidget->addItem( item );
|
|
|
|
QgsMapLayerConfigWidget *page = factory->createWidget( mLayer, nullptr, false, this );
|
|
mLayerPropertiesPages << page;
|
|
mOptionsStackedWidget->addWidget( page );
|
|
}
|
|
|
|
void QgsVectorLayerProperties::insertFieldOrExpression()
|
|
{
|
|
// Convert the selected field to an expression and
|
|
// insert it into the action at the cursor position
|
|
QString expression = QStringLiteral( "[% \"" );
|
|
expression += mMapTipExpressionFieldWidget->asExpression();
|
|
expression += QLatin1String( "\" %]" );
|
|
|
|
mMapTipWidget->insertText( expression );
|
|
}
|
|
|
|
// in raster props, this method is called sync()
|
|
void QgsVectorLayerProperties::syncToLayer()
|
|
{
|
|
// populate the general information
|
|
mLayerOrigNameLineEdit->setText( mLayer->originalName() );
|
|
txtDisplayName->setText( mLayer->name() );
|
|
pbnQueryBuilder->setWhatsThis( tr( "This button opens the query "
|
|
"builder and allows you to create a subset of features to display on "
|
|
"the map canvas rather than displaying all features in the layer" ) );
|
|
txtSubsetSQL->setWhatsThis( tr( "The query used to limit the features in the "
|
|
"layer is shown here. To enter or modify the query, click on the Query Builder button" ) );
|
|
|
|
//see if we are dealing with a pg layer here
|
|
mSubsetGroupBox->setEnabled( true );
|
|
txtSubsetSQL->setText( mLayer->subsetString() );
|
|
// if the user is allowed to type an adhoc query, the app will crash if the query
|
|
// is bad. For this reason, the sql box is disabled and the query must be built
|
|
// using the query builder, either by typing it in by hand or using the buttons, etc
|
|
// on the builder. If the ability to enter a query directly into the box is required,
|
|
// a mechanism to check it must be implemented.
|
|
txtSubsetSQL->setEnabled( false );
|
|
setPbnQueryBuilderEnabled();
|
|
|
|
mMapTipWidget->setText( mLayer->mapTipTemplate() );
|
|
mDisplayExpressionWidget->setField( mLayer->displayExpression() );
|
|
|
|
// set up the scale based layer visibility stuff....
|
|
mScaleRangeWidget->setScaleRange( 1.0 / mLayer->minimumScale(), 1.0 / mLayer->maximumScale() ); // caution: layer uses scale denoms, widget uses true scales
|
|
mScaleVisibilityGroupBox->setChecked( mLayer->hasScaleBasedVisibility() );
|
|
mScaleRangeWidget->setMapCanvas( QgisApp::instance()->mapCanvas() );
|
|
|
|
// get simplify drawing configuration
|
|
const QgsVectorSimplifyMethod &simplifyMethod = mLayer->simplifyMethod();
|
|
mSimplifyDrawingGroupBox->setChecked( simplifyMethod.simplifyHints() != QgsVectorSimplifyMethod::NoSimplification );
|
|
mSimplifyDrawingSpinBox->setValue( simplifyMethod.threshold() );
|
|
|
|
QString remark = QStringLiteral( " (%1)" ).arg( tr( "Not supported" ) );
|
|
if ( !( mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::SimplifyGeometries ) )
|
|
{
|
|
mSimplifyDrawingAtProvider->setChecked( false );
|
|
mSimplifyDrawingAtProvider->setEnabled( false );
|
|
if ( !mSimplifyDrawingAtProvider->text().endsWith( remark ) )
|
|
mSimplifyDrawingAtProvider->setText( mSimplifyDrawingAtProvider->text().append( remark ) );
|
|
}
|
|
else
|
|
{
|
|
mSimplifyDrawingAtProvider->setChecked( !simplifyMethod.forceLocalOptimization() );
|
|
mSimplifyDrawingAtProvider->setEnabled( mSimplifyDrawingGroupBox->isChecked() );
|
|
if ( mSimplifyDrawingAtProvider->text().endsWith( remark ) )
|
|
{
|
|
QString newText = mSimplifyDrawingAtProvider->text();
|
|
newText.chop( remark.size() );
|
|
mSimplifyDrawingAtProvider->setText( newText );
|
|
}
|
|
}
|
|
|
|
// disable simplification for point layers, now it is not implemented
|
|
if ( mLayer->geometryType() == QgsWkbTypes::PointGeometry )
|
|
{
|
|
mSimplifyDrawingGroupBox->setChecked( false );
|
|
mSimplifyDrawingGroupBox->setEnabled( false );
|
|
}
|
|
|
|
// Default local simplification algorithm
|
|
mSimplifyAlgorithmComboBox->addItem( tr( "Distance" ), ( int )QgsVectorSimplifyMethod::Distance );
|
|
mSimplifyAlgorithmComboBox->addItem( tr( "SnapToGrid" ), ( int )QgsVectorSimplifyMethod::SnapToGrid );
|
|
mSimplifyAlgorithmComboBox->addItem( tr( "Visvalingam" ), ( int )QgsVectorSimplifyMethod::Visvalingam );
|
|
mSimplifyAlgorithmComboBox->setCurrentIndex( mSimplifyAlgorithmComboBox->findData( ( int )simplifyMethod.simplifyAlgorithm() ) );
|
|
|
|
QStringList myScalesList = PROJECT_SCALES.split( ',' );
|
|
myScalesList.append( QStringLiteral( "1:1" ) );
|
|
mSimplifyMaximumScaleComboBox->updateScales( myScalesList );
|
|
mSimplifyMaximumScaleComboBox->setScale( 1.0 / simplifyMethod.maximumScale() );
|
|
|
|
mForceRasterCheckBox->setChecked( mLayer->renderer() && mLayer->renderer()->forceRasterRender() );
|
|
|
|
mRefreshLayerCheckBox->setChecked( mLayer->hasAutoRefreshEnabled() );
|
|
mRefreshLayerIntervalSpinBox->setEnabled( mLayer->hasAutoRefreshEnabled() );
|
|
mRefreshLayerIntervalSpinBox->setValue( mLayer->autoRefreshInterval() / 1000.0 );
|
|
|
|
// load appropriate symbology page (V1 or V2)
|
|
updateSymbologyPage();
|
|
|
|
mActionDialog->init( *mLayer->actions(), mLayer->attributeTableConfig() );
|
|
|
|
if ( labelingDialog )
|
|
labelingDialog->adaptToLayer();
|
|
|
|
mFieldsPropertiesDialog->init();
|
|
|
|
// set initial state for variable editor
|
|
updateVariableEditor();
|
|
|
|
// updates the init python code and ui
|
|
updateFieldsPropertiesDialog();
|
|
|
|
} // syncToLayer()
|
|
|
|
|
|
|
|
void QgsVectorLayerProperties::apply()
|
|
{
|
|
if ( labelingDialog )
|
|
{
|
|
labelingDialog->writeSettingsToLayer();
|
|
}
|
|
|
|
// apply legend settings
|
|
mLegendConfigEmbeddedWidget->applyToLayer();
|
|
|
|
//
|
|
// Set up sql subset query if applicable
|
|
//
|
|
mSubsetGroupBox->setEnabled( true );
|
|
|
|
if ( txtSubsetSQL->toPlainText() != mLayer->subsetString() )
|
|
{
|
|
// set the subset sql for the layer
|
|
mLayer->setSubsetString( txtSubsetSQL->toPlainText() );
|
|
mMetadataFilled = false;
|
|
}
|
|
mOriginalSubsetSQL = mLayer->subsetString();
|
|
|
|
// set up the scale based layer visibility stuff....
|
|
mLayer->setScaleBasedVisibility( mScaleVisibilityGroupBox->isChecked() );
|
|
// caution: layer uses scale denoms, widget uses true scales
|
|
mLayer->setMinimumScale( mScaleRangeWidget->maximumScaleDenom() );
|
|
mLayer->setMaximumScale( mScaleRangeWidget->minimumScaleDenom() );
|
|
|
|
// provider-specific options
|
|
if ( mLayer->dataProvider() )
|
|
{
|
|
if ( mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::SelectEncoding )
|
|
{
|
|
mLayer->setProviderEncoding( cboProviderEncoding->currentText() );
|
|
}
|
|
}
|
|
|
|
mLayer->setDisplayExpression( mDisplayExpressionWidget->currentField() );
|
|
mLayer->setMapTipTemplate( mMapTipWidget->text() );
|
|
|
|
mLayer->actions()->clearActions();
|
|
Q_FOREACH ( const QgsAction &action, mActionDialog->actions() )
|
|
{
|
|
mLayer->actions()->addAction( action );
|
|
}
|
|
QgsAttributeTableConfig attributeTableConfig = mLayer->attributeTableConfig();
|
|
attributeTableConfig.update( mLayer->fields() );
|
|
attributeTableConfig.setActionWidgetStyle( mActionDialog->attributeTableWidgetStyle() );
|
|
QVector<QgsAttributeTableConfig::ColumnConfig> columns = attributeTableConfig.columns();
|
|
|
|
for ( int i = 0; i < columns.size(); ++i )
|
|
{
|
|
if ( columns.at( i ).type == QgsAttributeTableConfig::Action )
|
|
{
|
|
columns[i].hidden = !mActionDialog->showWidgetInAttributeTable();
|
|
}
|
|
}
|
|
|
|
attributeTableConfig.setColumns( columns );
|
|
|
|
mLayer->setAttributeTableConfig( attributeTableConfig );
|
|
|
|
mLayer->setName( mLayerOrigNameLineEdit->text() );
|
|
|
|
// Apply fields settings
|
|
mFieldsPropertiesDialog->apply();
|
|
|
|
if ( mLayer->renderer() )
|
|
{
|
|
QgsRendererPropertiesDialog *dlg = static_cast<QgsRendererPropertiesDialog *>( widgetStackRenderers->currentWidget() );
|
|
dlg->apply();
|
|
}
|
|
|
|
//apply diagram settings
|
|
diagramPropertiesDialog->apply();
|
|
|
|
// apply all plugin dialogs
|
|
Q_FOREACH ( QgsMapLayerConfigWidget *page, mLayerPropertiesPages )
|
|
{
|
|
page->apply();
|
|
}
|
|
|
|
//layer title and abstract
|
|
if ( mLayer->shortName() != mLayerShortNameLineEdit->text() )
|
|
mMetadataFilled = false;
|
|
mLayer->setShortName( mLayerShortNameLineEdit->text() );
|
|
|
|
if ( mLayer->title() != mLayerTitleLineEdit->text() )
|
|
mMetadataFilled = false;
|
|
mLayer->setTitle( mLayerTitleLineEdit->text() );
|
|
|
|
if ( mLayer->abstract() != mLayerAbstractTextEdit->toPlainText() )
|
|
mMetadataFilled = false;
|
|
mLayer->setAbstract( mLayerAbstractTextEdit->toPlainText() );
|
|
|
|
if ( mLayer->keywordList() != mLayerKeywordListLineEdit->text() )
|
|
mMetadataFilled = false;
|
|
mLayer->setKeywordList( mLayerKeywordListLineEdit->text() );
|
|
|
|
if ( mLayer->dataUrl() != mLayerDataUrlLineEdit->text() )
|
|
mMetadataFilled = false;
|
|
mLayer->setDataUrl( mLayerDataUrlLineEdit->text() );
|
|
|
|
if ( mLayer->dataUrlFormat() != mLayerDataUrlFormatComboBox->currentText() )
|
|
mMetadataFilled = false;
|
|
mLayer->setDataUrlFormat( mLayerDataUrlFormatComboBox->currentText() );
|
|
|
|
//layer attribution and metadataUrl
|
|
if ( mLayer->attribution() != mLayerAttributionLineEdit->text() )
|
|
mMetadataFilled = false;
|
|
mLayer->setAttribution( mLayerAttributionLineEdit->text() );
|
|
|
|
if ( mLayer->attributionUrl() != mLayerAttributionUrlLineEdit->text() )
|
|
mMetadataFilled = false;
|
|
mLayer->setAttributionUrl( mLayerAttributionUrlLineEdit->text() );
|
|
|
|
if ( mLayer->metadataUrl() != mLayerMetadataUrlLineEdit->text() )
|
|
mMetadataFilled = false;
|
|
mLayer->setMetadataUrl( mLayerMetadataUrlLineEdit->text() );
|
|
|
|
if ( mLayer->metadataUrlType() != mLayerMetadataUrlTypeComboBox->currentText() )
|
|
mMetadataFilled = false;
|
|
mLayer->setMetadataUrlType( mLayerMetadataUrlTypeComboBox->currentText() );
|
|
|
|
if ( mLayer->metadataUrlFormat() != mLayerMetadataUrlFormatComboBox->currentText() )
|
|
mMetadataFilled = false;
|
|
mLayer->setMetadataUrlFormat( mLayerMetadataUrlFormatComboBox->currentText() );
|
|
|
|
// LegendURL
|
|
if ( mLayer->legendUrl() != mLayerLegendUrlLineEdit->text() )
|
|
mMetadataFilled = false;
|
|
mLayer->setLegendUrl( mLayerLegendUrlLineEdit->text() );
|
|
|
|
if ( mLayer->legendUrlFormat() != mLayerLegendUrlFormatComboBox->currentText() )
|
|
mMetadataFilled = false;
|
|
mLayer->setLegendUrlFormat( mLayerLegendUrlFormatComboBox->currentText() );
|
|
|
|
//layer simplify drawing configuration
|
|
QgsVectorSimplifyMethod::SimplifyHints simplifyHints = QgsVectorSimplifyMethod::NoSimplification;
|
|
if ( mSimplifyDrawingGroupBox->isChecked() )
|
|
{
|
|
simplifyHints |= QgsVectorSimplifyMethod::GeometrySimplification;
|
|
if ( mSimplifyDrawingSpinBox->value() > 1 ) simplifyHints |= QgsVectorSimplifyMethod::AntialiasingSimplification;
|
|
}
|
|
QgsVectorSimplifyMethod simplifyMethod = mLayer->simplifyMethod();
|
|
simplifyMethod.setSimplifyHints( simplifyHints );
|
|
simplifyMethod.setSimplifyAlgorithm( static_cast< QgsVectorSimplifyMethod::SimplifyAlgorithm >( mSimplifyAlgorithmComboBox->currentData().toInt() ) );
|
|
simplifyMethod.setThreshold( mSimplifyDrawingSpinBox->value() );
|
|
simplifyMethod.setForceLocalOptimization( !mSimplifyDrawingAtProvider->isChecked() );
|
|
simplifyMethod.setMaximumScale( 1.0 / mSimplifyMaximumScaleComboBox->scale() );
|
|
mLayer->setSimplifyMethod( simplifyMethod );
|
|
|
|
if ( mLayer->renderer() )
|
|
mLayer->renderer()->setForceRasterRender( mForceRasterCheckBox->isChecked() );
|
|
|
|
mLayer->setAutoRefreshInterval( mRefreshLayerIntervalSpinBox->value() * 1000.0 );
|
|
mLayer->setAutoRefreshEnabled( mRefreshLayerCheckBox->isChecked() );
|
|
|
|
mOldJoins = mLayer->vectorJoins();
|
|
|
|
//save variables
|
|
QgsExpressionContextUtils::setLayerVariables( mLayer, mVariableEditor->variablesInActiveScope() );
|
|
updateVariableEditor();
|
|
|
|
// save dependencies
|
|
QSet<QgsMapLayerDependency> deps;
|
|
Q_FOREACH ( const QgsLayerTreeLayer *layer, mLayersDependenciesTreeGroup->findLayers() )
|
|
{
|
|
if ( layer->isVisible() )
|
|
deps << QgsMapLayerDependency( layer->layerId() );
|
|
}
|
|
if ( ! mLayer->setDependencies( deps ) )
|
|
{
|
|
QMessageBox::warning( nullptr, tr( "Dependency cycle" ), tr( "This configuration introduces a cycle in data dependencies and will be ignored" ) );
|
|
}
|
|
|
|
// update symbology
|
|
emit refreshLegend( mLayer->id() );
|
|
|
|
mLayer->triggerRepaint();
|
|
// notify the project we've made a change
|
|
QgsProject::instance()->setDirty( true );
|
|
}
|
|
|
|
void QgsVectorLayerProperties::onCancel()
|
|
{
|
|
if ( mOldJoins != mLayer->vectorJoins() )
|
|
{
|
|
// need to undo changes in vector layer joins - they are applied directly to the layer (not in apply())
|
|
// so other parts of the properties dialog can use the fields from the joined layers
|
|
|
|
Q_FOREACH ( const QgsVectorLayerJoinInfo &info, mLayer->vectorJoins() )
|
|
mLayer->removeJoin( info.joinLayerId() );
|
|
|
|
Q_FOREACH ( const QgsVectorLayerJoinInfo &info, mOldJoins )
|
|
mLayer->addJoin( info );
|
|
}
|
|
|
|
if ( mOriginalSubsetSQL != mLayer->subsetString() )
|
|
{
|
|
// need to undo changes in subset string - they are applied directly to the layer (not in apply())
|
|
// by QgsQueryBuilder::accept()
|
|
|
|
mLayer->setSubsetString( mOriginalSubsetSQL );
|
|
}
|
|
|
|
if ( mOldStyle.xmlData() != mLayer->styleManager()->style( mLayer->styleManager()->currentStyle() ).xmlData() )
|
|
{
|
|
// need to reset style to previous - style applied directly to the layer (not in apply())
|
|
QString myMessage;
|
|
QDomDocument doc( QStringLiteral( "qgis" ) );
|
|
int errorLine, errorColumn;
|
|
doc.setContent( mOldStyle.xmlData(), false, &myMessage, &errorLine, &errorColumn );
|
|
mLayer->importNamedStyle( doc, myMessage );
|
|
syncToLayer();
|
|
}
|
|
}
|
|
|
|
void QgsVectorLayerProperties::on_pbnQueryBuilder_clicked()
|
|
{
|
|
// launch the query builder
|
|
QgsQueryBuilder *qb = new QgsQueryBuilder( mLayer, this );
|
|
|
|
// Set the sql in the query builder to the same in the prop dialog
|
|
// (in case the user has already changed it)
|
|
qb->setSql( txtSubsetSQL->toPlainText() );
|
|
// Open the query builder
|
|
if ( qb->exec() )
|
|
{
|
|
// if the sql is changed, update it in the prop subset text box
|
|
txtSubsetSQL->setText( qb->sql() );
|
|
//TODO If the sql is changed in the prop dialog, the layer extent should be recalculated
|
|
|
|
// The datasource for the layer needs to be updated with the new sql since this gets
|
|
// saved to the project file. This should happen at the map layer level...
|
|
|
|
}
|
|
// delete the query builder object
|
|
delete qb;
|
|
}
|
|
|
|
void QgsVectorLayerProperties::on_pbnIndex_clicked()
|
|
{
|
|
QgsVectorDataProvider *pr = mLayer->dataProvider();
|
|
if ( pr )
|
|
{
|
|
setCursor( Qt::WaitCursor );
|
|
bool errval = pr->createSpatialIndex();
|
|
setCursor( Qt::ArrowCursor );
|
|
if ( errval )
|
|
{
|
|
QMessageBox::information( this, tr( "Spatial Index" ), tr( "Creation of spatial index successful" ) );
|
|
}
|
|
else
|
|
{
|
|
// TODO: Remind the user to use OGR >= 1.2.6 and Shapefile
|
|
QMessageBox::information( this, tr( "Spatial Index" ), tr( "Creation of spatial index failed" ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
QString QgsVectorLayerProperties::htmlMetadata()
|
|
{
|
|
return mLayer->htmlMetadata();
|
|
}
|
|
|
|
void QgsVectorLayerProperties::on_mLayerOrigNameLineEdit_textEdited( const QString &text )
|
|
{
|
|
txtDisplayName->setText( mLayer->capitalizeLayerName( text ) );
|
|
}
|
|
|
|
void QgsVectorLayerProperties::on_mCrsSelector_crsChanged( const QgsCoordinateReferenceSystem &crs )
|
|
{
|
|
mLayer->setCrs( crs );
|
|
mMetadataFilled = false;
|
|
}
|
|
|
|
void QgsVectorLayerProperties::loadDefaultStyle_clicked()
|
|
{
|
|
QString msg;
|
|
bool defaultLoadedFlag = false;
|
|
|
|
if ( mLayer->dataProvider()->isSaveAndLoadStyleToDatabaseSupported() )
|
|
{
|
|
QMessageBox askToUser;
|
|
askToUser.setText( tr( "Load default style from: " ) );
|
|
askToUser.setIcon( QMessageBox::Question );
|
|
askToUser.addButton( tr( "Cancel" ), QMessageBox::RejectRole );
|
|
askToUser.addButton( tr( "Local database" ), QMessageBox::NoRole );
|
|
askToUser.addButton( tr( "Datasource database" ), QMessageBox::YesRole );
|
|
|
|
switch ( askToUser.exec() )
|
|
{
|
|
case 0:
|
|
return;
|
|
case 2:
|
|
msg = mLayer->loadNamedStyle( mLayer->styleURI(), defaultLoadedFlag );
|
|
if ( !defaultLoadedFlag )
|
|
{
|
|
//something went wrong - let them know why
|
|
QMessageBox::information( this, tr( "Default Style" ), msg );
|
|
}
|
|
if ( msg.compare( tr( "Loaded from Provider" ) ) )
|
|
{
|
|
QMessageBox::information( this, tr( "Default Style" ),
|
|
tr( "No default style was found for this layer" ) );
|
|
}
|
|
else
|
|
{
|
|
syncToLayer();
|
|
}
|
|
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
QString myMessage = mLayer->loadNamedStyle( mLayer->styleURI(), defaultLoadedFlag, true );
|
|
// QString myMessage = layer->loadDefaultStyle( defaultLoadedFlag );
|
|
//reset if the default style was loaded ok only
|
|
if ( defaultLoadedFlag )
|
|
{
|
|
// all worked ok so no need to inform user
|
|
syncToLayer();
|
|
}
|
|
else
|
|
{
|
|
//something went wrong - let them know why
|
|
QMessageBox::information( this, tr( "Default Style" ), myMessage );
|
|
}
|
|
}
|
|
|
|
void QgsVectorLayerProperties::saveDefaultStyle_clicked()
|
|
{
|
|
apply();
|
|
QString errorMsg;
|
|
if ( mLayer->dataProvider()->isSaveAndLoadStyleToDatabaseSupported() )
|
|
{
|
|
QMessageBox askToUser;
|
|
askToUser.setText( tr( "Save default style to: " ) );
|
|
askToUser.setIcon( QMessageBox::Question );
|
|
askToUser.addButton( tr( "Cancel" ), QMessageBox::RejectRole );
|
|
askToUser.addButton( tr( "Local database" ), QMessageBox::NoRole );
|
|
askToUser.addButton( tr( "Datasource database" ), QMessageBox::YesRole );
|
|
|
|
switch ( askToUser.exec() )
|
|
{
|
|
case 0:
|
|
return;
|
|
case 2:
|
|
mLayer->saveStyleToDatabase( QLatin1String( "" ), QLatin1String( "" ), true, QLatin1String( "" ), errorMsg );
|
|
if ( errorMsg.isNull() )
|
|
{
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool defaultSavedFlag = false;
|
|
errorMsg = mLayer->saveDefaultStyle( defaultSavedFlag );
|
|
if ( !defaultSavedFlag )
|
|
{
|
|
QMessageBox::warning( this, tr( "Default Style" ), errorMsg );
|
|
}
|
|
}
|
|
|
|
|
|
void QgsVectorLayerProperties::loadStyle_clicked()
|
|
{
|
|
QgsSettings myQSettings; // where we keep last used filter in persistent state
|
|
QString myLastUsedDir = myQSettings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
|
|
|
|
QString myFileName = QFileDialog::getOpenFileName( this, tr( "Load layer properties from style file" ), myLastUsedDir,
|
|
tr( "QGIS Layer Style File" ) + " (*.qml);;" + tr( "SLD File" ) + " (*.sld)" );
|
|
if ( myFileName.isNull() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
mOldStyle = mLayer->styleManager()->style( mLayer->styleManager()->currentStyle() );
|
|
|
|
QString myMessage;
|
|
bool defaultLoadedFlag = false;
|
|
|
|
if ( myFileName.endsWith( QLatin1String( ".sld" ), Qt::CaseInsensitive ) )
|
|
{
|
|
// load from SLD
|
|
myMessage = mLayer->loadSldStyle( myFileName, defaultLoadedFlag );
|
|
}
|
|
else
|
|
{
|
|
myMessage = mLayer->loadNamedStyle( myFileName, defaultLoadedFlag );
|
|
}
|
|
//reset if the default style was loaded ok only
|
|
if ( defaultLoadedFlag )
|
|
{
|
|
syncToLayer();
|
|
}
|
|
else
|
|
{
|
|
//let the user know what went wrong
|
|
QMessageBox::warning( this, tr( "Load Style" ), myMessage );
|
|
}
|
|
|
|
QFileInfo myFI( myFileName );
|
|
QString myPath = myFI.path();
|
|
myQSettings.setValue( QStringLiteral( "style/lastStyleDir" ), myPath );
|
|
|
|
activateWindow(); // set focus back to properties dialog
|
|
}
|
|
|
|
|
|
void QgsVectorLayerProperties::saveStyleAs_clicked()
|
|
{
|
|
saveStyleAs( QML );
|
|
}
|
|
|
|
void QgsVectorLayerProperties::saveStyleAsMenuTriggered( QAction *action )
|
|
{
|
|
QMenu *menu = qobject_cast<QMenu *>( sender() );
|
|
if ( !menu )
|
|
return;
|
|
|
|
int index = mSaveAsMenu->actions().indexOf( action );
|
|
if ( index < 0 )
|
|
return;
|
|
|
|
saveStyleAs( static_cast< StyleType >( index ) );
|
|
}
|
|
|
|
void QgsVectorLayerProperties::saveStyleAs( StyleType styleType )
|
|
{
|
|
QgsSettings myQSettings; // where we keep last used filter in persistent state
|
|
QString myLastUsedDir = myQSettings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
|
|
|
|
if ( styleType == DB )
|
|
{
|
|
QString infoWindowTitle = QObject::tr( "Save style to DB (%1)" ).arg( mLayer->providerType() );
|
|
QString msgError;
|
|
|
|
QgsSaveStyleToDbDialog askToUser;
|
|
//Ask the user for a name and a description about the style
|
|
if ( askToUser.exec() == QDialog::Accepted )
|
|
{
|
|
QString styleName = askToUser.getName();
|
|
QString styleDesc = askToUser.getDescription();
|
|
QString uiFileContent = askToUser.getUIFileContent();
|
|
bool isDefault = askToUser.isDefault();
|
|
|
|
apply();
|
|
|
|
mLayer->saveStyleToDatabase( styleName, styleDesc, isDefault, uiFileContent, msgError );
|
|
|
|
if ( !msgError.isNull() )
|
|
{
|
|
QgisApp::instance()->messageBar()->pushMessage( infoWindowTitle, msgError, QgsMessageBar::WARNING, QgisApp::instance()->messageTimeout() );
|
|
}
|
|
else
|
|
{
|
|
QgisApp::instance()->messageBar()->pushMessage( infoWindowTitle, tr( "Style saved" ), QgsMessageBar::INFO, QgisApp::instance()->messageTimeout() );
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
QString format, extension;
|
|
if ( styleType == SLD )
|
|
{
|
|
format = tr( "SLD File" ) + " (*.sld)";
|
|
extension = QStringLiteral( ".sld" );
|
|
}
|
|
else
|
|
{
|
|
format = tr( "QGIS Layer Style File" ) + " (*.qml)";
|
|
extension = QStringLiteral( ".qml" );
|
|
}
|
|
|
|
QString myOutputFileName = QFileDialog::getSaveFileName( this, tr( "Save layer properties as style file" ),
|
|
myLastUsedDir, format );
|
|
if ( myOutputFileName.isNull() ) //dialog canceled
|
|
{
|
|
return;
|
|
}
|
|
|
|
apply(); // make sure the style to save is uptodate
|
|
|
|
QString myMessage;
|
|
bool defaultLoadedFlag = false;
|
|
|
|
//ensure the user never omitted the extension from the file name
|
|
if ( !myOutputFileName.endsWith( extension, Qt::CaseInsensitive ) )
|
|
{
|
|
myOutputFileName += extension;
|
|
}
|
|
|
|
if ( styleType == SLD )
|
|
{
|
|
// convert to SLD
|
|
myMessage = mLayer->saveSldStyle( myOutputFileName, defaultLoadedFlag );
|
|
}
|
|
else
|
|
{
|
|
myMessage = mLayer->saveNamedStyle( myOutputFileName, defaultLoadedFlag );
|
|
}
|
|
|
|
//reset if the default style was loaded ok only
|
|
if ( defaultLoadedFlag )
|
|
{
|
|
syncToLayer();
|
|
}
|
|
else
|
|
{
|
|
//let the user know what went wrong
|
|
QMessageBox::information( this, tr( "Saved Style" ), myMessage );
|
|
}
|
|
|
|
QFileInfo myFI( myOutputFileName );
|
|
QString myPath = myFI.path();
|
|
// Persist last used dir
|
|
myQSettings.setValue( QStringLiteral( "style/lastStyleDir" ), myPath );
|
|
}
|
|
}
|
|
|
|
void QgsVectorLayerProperties::loadStyleMenuTriggered( QAction *action )
|
|
{
|
|
QMenu *menu = qobject_cast<QMenu *>( sender() );
|
|
if ( !menu )
|
|
return;
|
|
|
|
int index = mLoadStyleMenu->actions().indexOf( action );
|
|
|
|
if ( index == 0 ) //Load from filesystem
|
|
{
|
|
loadStyle_clicked();
|
|
}
|
|
else if ( index == 1 ) //Load from database
|
|
{
|
|
showListOfStylesFromDatabase();
|
|
}
|
|
|
|
}
|
|
|
|
void QgsVectorLayerProperties::aboutToShowStyleMenu()
|
|
{
|
|
// this should be unified with QgsRasterLayerProperties::aboutToShowStyleMenu()
|
|
|
|
QMenu *m = qobject_cast<QMenu *>( sender() );
|
|
if ( !m )
|
|
return;
|
|
|
|
// first get rid of previously added style manager actions (they are dynamic)
|
|
bool gotFirstSeparator = false;
|
|
QList<QAction *> actions = m->actions();
|
|
for ( int i = 0; i < actions.count(); ++i )
|
|
{
|
|
if ( actions[i]->isSeparator() )
|
|
{
|
|
if ( gotFirstSeparator )
|
|
{
|
|
// remove all actions after second separator (including it)
|
|
while ( actions.count() != i )
|
|
delete actions.takeAt( i );
|
|
break;
|
|
}
|
|
else
|
|
gotFirstSeparator = true;
|
|
}
|
|
}
|
|
|
|
// re-add style manager actions!
|
|
m->addSeparator();
|
|
QgsMapLayerStyleGuiUtils::instance()->addStyleManagerActions( m, mLayer );
|
|
}
|
|
|
|
void QgsVectorLayerProperties::showListOfStylesFromDatabase()
|
|
{
|
|
QString errorMsg;
|
|
QStringList ids, names, descriptions;
|
|
|
|
//get the list of styles in the db
|
|
int sectionLimit = mLayer->listStylesInDatabase( ids, names, descriptions, errorMsg );
|
|
if ( !errorMsg.isNull() )
|
|
{
|
|
QMessageBox::warning( this, tr( "Error occurred retrieving styles from database" ), errorMsg );
|
|
return;
|
|
}
|
|
|
|
QgsLoadStyleFromDBDialog dialog;
|
|
dialog.setLayer( mLayer );
|
|
dialog.initializeLists( ids, names, descriptions, sectionLimit );
|
|
|
|
if ( dialog.exec() == QDialog::Accepted )
|
|
{
|
|
QString selectedStyleId = dialog.getSelectedStyleId();
|
|
|
|
QString qmlStyle = mLayer->getStyleFromDatabase( selectedStyleId, errorMsg );
|
|
if ( !errorMsg.isNull() )
|
|
{
|
|
QMessageBox::warning( this, tr( "Error occurred retrieving styles from database" ), errorMsg );
|
|
return;
|
|
}
|
|
|
|
QDomDocument myDocument( QStringLiteral( "qgis" ) );
|
|
myDocument.setContent( qmlStyle );
|
|
|
|
if ( mLayer->importNamedStyle( myDocument, errorMsg ) )
|
|
{
|
|
syncToLayer();
|
|
}
|
|
else
|
|
{
|
|
QMessageBox::warning( this, tr( "Error occurred retrieving styles from database" ),
|
|
tr( "The retrieved style is not a valid named style. Error message: %1" )
|
|
.arg( errorMsg ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void QgsVectorLayerProperties::on_mButtonAddJoin_clicked()
|
|
{
|
|
if ( !mLayer )
|
|
return;
|
|
|
|
QList<QgsMapLayer *> joinedLayers;
|
|
const QList< QgsVectorLayerJoinInfo > &joins = mLayer->vectorJoins();
|
|
joinedLayers.reserve( joins.size() );
|
|
for ( int i = 0; i < joins.size(); ++i )
|
|
{
|
|
joinedLayers.append( joins[i].joinLayer() );
|
|
}
|
|
|
|
QgsJoinDialog d( mLayer, joinedLayers );
|
|
if ( d.exec() == QDialog::Accepted )
|
|
{
|
|
QgsVectorLayerJoinInfo info = d.joinInfo();
|
|
//create attribute index if possible
|
|
if ( d.createAttributeIndex() )
|
|
{
|
|
QgsVectorLayer *joinLayer = info.joinLayer();
|
|
if ( joinLayer )
|
|
{
|
|
joinLayer->dataProvider()->createAttributeIndex( joinLayer->fields().indexFromName( info.joinFieldName() ) );
|
|
}
|
|
}
|
|
mLayer->addJoin( info );
|
|
addJoinToTreeWidget( info );
|
|
setPbnQueryBuilderEnabled();
|
|
mFieldsPropertiesDialog->init();
|
|
}
|
|
}
|
|
|
|
void QgsVectorLayerProperties::on_mButtonEditJoin_clicked()
|
|
{
|
|
QTreeWidgetItem *currentJoinItem = mJoinTreeWidget->currentItem();
|
|
on_mJoinTreeWidget_itemDoubleClicked( currentJoinItem, 0 );
|
|
}
|
|
|
|
void QgsVectorLayerProperties::on_mJoinTreeWidget_itemDoubleClicked( QTreeWidgetItem *item, int )
|
|
{
|
|
if ( !mLayer || !item )
|
|
{
|
|
return;
|
|
}
|
|
|
|
QList<QgsMapLayer *> joinedLayers;
|
|
QString joinLayerId = item->data( 0, Qt::UserRole ).toString();
|
|
const QList< QgsVectorLayerJoinInfo > &joins = mLayer->vectorJoins();
|
|
int j = -1;
|
|
for ( int i = 0; i < joins.size(); ++i )
|
|
{
|
|
QgsVectorLayer *joinLayer = joins[i].joinLayer();
|
|
if ( !joinLayer )
|
|
continue; // invalid join (unresolved join layer)
|
|
|
|
if ( joinLayer->id() == joinLayerId )
|
|
{
|
|
j = i;
|
|
}
|
|
else
|
|
{
|
|
// remove already joined layers from possible list to be displayed in dialog
|
|
joinedLayers.append( joinLayer );
|
|
}
|
|
}
|
|
if ( j == -1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
QgsJoinDialog d( mLayer, joinedLayers );
|
|
d.setJoinInfo( joins[j] );
|
|
|
|
if ( d.exec() == QDialog::Accepted )
|
|
{
|
|
QgsVectorLayerJoinInfo info = d.joinInfo();
|
|
|
|
// remove old join
|
|
mLayer->removeJoin( joinLayerId );
|
|
int idx = mJoinTreeWidget->indexOfTopLevelItem( item );
|
|
mJoinTreeWidget->takeTopLevelItem( idx );
|
|
|
|
// add the new edited
|
|
|
|
//create attribute index if possible
|
|
if ( d.createAttributeIndex() )
|
|
{
|
|
QgsVectorLayer *joinLayer = info.joinLayer();
|
|
if ( joinLayer )
|
|
{
|
|
joinLayer->dataProvider()->createAttributeIndex( joinLayer->fields().indexFromName( info.joinFieldName() ) );
|
|
}
|
|
}
|
|
mLayer->addJoin( info );
|
|
addJoinToTreeWidget( info, idx );
|
|
|
|
setPbnQueryBuilderEnabled();
|
|
mFieldsPropertiesDialog->init();
|
|
}
|
|
}
|
|
|
|
void QgsVectorLayerProperties::addJoinToTreeWidget( const QgsVectorLayerJoinInfo &join, const int insertIndex )
|
|
{
|
|
QTreeWidgetItem *joinItem = new QTreeWidgetItem();
|
|
|
|
QgsVectorLayer *joinLayer = join.joinLayer();
|
|
if ( !mLayer || !joinLayer )
|
|
{
|
|
return;
|
|
}
|
|
|
|
joinItem->setText( 0, joinLayer->name() );
|
|
joinItem->setData( 0, Qt::UserRole, join.joinLayerId() );
|
|
|
|
joinItem->setText( 1, join.joinFieldName() );
|
|
joinItem->setText( 2, join.targetFieldName() );
|
|
|
|
if ( join.isUsingMemoryCache() )
|
|
{
|
|
joinItem->setText( 3, QChar( 0x2714 ) );
|
|
}
|
|
|
|
joinItem->setText( 4, join.prefix() );
|
|
|
|
const QStringList *list = join.joinFieldNamesSubset();
|
|
if ( list )
|
|
{
|
|
joinItem->setText( 5, QStringLiteral( "%1" ).arg( list->count() ) );
|
|
}
|
|
else
|
|
{
|
|
joinItem->setText( 5, tr( "all" ) );
|
|
}
|
|
|
|
if ( insertIndex >= 0 )
|
|
{
|
|
mJoinTreeWidget->insertTopLevelItem( insertIndex, joinItem );
|
|
}
|
|
else
|
|
{
|
|
mJoinTreeWidget->addTopLevelItem( joinItem );
|
|
}
|
|
for ( int c = 0; c < 5; c++ )
|
|
{
|
|
mJoinTreeWidget->resizeColumnToContents( c );
|
|
}
|
|
mJoinTreeWidget->setCurrentItem( joinItem );
|
|
}
|
|
|
|
QgsExpressionContext QgsVectorLayerProperties::createExpressionContext() const
|
|
{
|
|
return mContext;
|
|
}
|
|
|
|
void QgsVectorLayerProperties::openPanel( QgsPanelWidget *panel )
|
|
{
|
|
QDialog *dlg = new QDialog();
|
|
QString key = QStringLiteral( "/UI/paneldialog/%1" ).arg( panel->panelTitle() );
|
|
QgsSettings settings;
|
|
dlg->restoreGeometry( settings.value( key ).toByteArray() );
|
|
dlg->setWindowTitle( panel->panelTitle() );
|
|
dlg->setLayout( new QVBoxLayout() );
|
|
dlg->layout()->addWidget( panel );
|
|
QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok );
|
|
connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
|
|
dlg->layout()->addWidget( buttonBox );
|
|
dlg->exec();
|
|
settings.setValue( key, dlg->saveGeometry() );
|
|
panel->acceptPanel();
|
|
}
|
|
|
|
void QgsVectorLayerProperties::on_mButtonRemoveJoin_clicked()
|
|
{
|
|
QTreeWidgetItem *currentJoinItem = mJoinTreeWidget->currentItem();
|
|
if ( !mLayer || !currentJoinItem )
|
|
{
|
|
return;
|
|
}
|
|
|
|
mLayer->removeJoin( currentJoinItem->data( 0, Qt::UserRole ).toString() );
|
|
mJoinTreeWidget->takeTopLevelItem( mJoinTreeWidget->indexOfTopLevelItem( currentJoinItem ) );
|
|
setPbnQueryBuilderEnabled();
|
|
mFieldsPropertiesDialog->init();
|
|
}
|
|
|
|
|
|
void QgsVectorLayerProperties::updateSymbologyPage()
|
|
{
|
|
|
|
//find out the type of renderer in the vectorlayer, create a dialog with these settings and add it to the form
|
|
delete mRendererDialog;
|
|
mRendererDialog = nullptr;
|
|
|
|
if ( mLayer->renderer() )
|
|
{
|
|
mRendererDialog = new QgsRendererPropertiesDialog( mLayer, QgsStyle::defaultStyle(), true, this );
|
|
mRendererDialog->setDockMode( false );
|
|
mRendererDialog->setMapCanvas( QgisApp::instance()->mapCanvas() );
|
|
connect( mRendererDialog, &QgsRendererPropertiesDialog::showPanel, this, &QgsVectorLayerProperties::openPanel );
|
|
connect( mRendererDialog, &QgsRendererPropertiesDialog::layerVariablesChanged, this, &QgsVectorLayerProperties::updateVariableEditor );
|
|
|
|
// display the menu to choose the output format (fix #5136)
|
|
mActionSaveStyleAs->setText( tr( "Save Style" ) );
|
|
mActionSaveStyleAs->setMenu( mSaveAsMenu );
|
|
disconnect( mActionSaveStyleAs, &QAction::triggered, this, &QgsVectorLayerProperties::saveStyleAs_clicked );
|
|
}
|
|
else
|
|
{
|
|
mOptsPage_Style->setEnabled( false ); // hide symbology item
|
|
}
|
|
|
|
if ( mRendererDialog )
|
|
{
|
|
mRendererDialog->layout()->setMargin( 0 );
|
|
widgetStackRenderers->addWidget( mRendererDialog );
|
|
widgetStackRenderers->setCurrentWidget( mRendererDialog );
|
|
widgetStackRenderers->currentWidget()->layout()->setMargin( 0 );
|
|
}
|
|
}
|
|
|
|
void QgsVectorLayerProperties::setPbnQueryBuilderEnabled()
|
|
{
|
|
pbnQueryBuilder->setEnabled( mLayer &&
|
|
mLayer->dataProvider() &&
|
|
mLayer->dataProvider()->supportsSubsetString() &&
|
|
!mLayer->isEditable() );
|
|
|
|
if ( mLayer && mLayer->isEditable() )
|
|
{
|
|
pbnQueryBuilder->setToolTip( tr( "Stop editing mode to enable this." ) );
|
|
}
|
|
|
|
}
|
|
|
|
void QgsVectorLayerProperties::on_pbnUpdateExtents_clicked()
|
|
{
|
|
mLayer->updateExtents();
|
|
mMetadataFilled = false;
|
|
}
|
|
|
|
void QgsVectorLayerProperties::mOptionsStackedWidget_CurrentChanged( int indx )
|
|
{
|
|
if ( indx != mOptStackedWidget->indexOf( mOptsPage_Information ) || mMetadataFilled )
|
|
return;
|
|
|
|
//set the metadata contents (which can be expensive)
|
|
teMetadataViewer->clear();
|
|
teMetadataViewer->setHtml( htmlMetadata() );
|
|
mMetadataFilled = true;
|
|
}
|
|
|
|
void QgsVectorLayerProperties::on_mSimplifyDrawingGroupBox_toggled( bool checked )
|
|
{
|
|
if ( !( mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::SimplifyGeometries ) )
|
|
{
|
|
mSimplifyDrawingAtProvider->setEnabled( false );
|
|
}
|
|
else
|
|
{
|
|
mSimplifyDrawingAtProvider->setEnabled( checked );
|
|
}
|
|
}
|
|
|
|
void QgsVectorLayerProperties::updateVariableEditor()
|
|
{
|
|
QgsExpressionContext context;
|
|
mVariableEditor->setContext( &context );
|
|
mVariableEditor->context()->appendScope( QgsExpressionContextUtils::globalScope() );
|
|
mVariableEditor->context()->appendScope( QgsExpressionContextUtils::projectScope( QgsProject::instance() ) );
|
|
mVariableEditor->context()->appendScope( QgsExpressionContextUtils::layerScope( mLayer ) );
|
|
mVariableEditor->reloadContext();
|
|
mVariableEditor->setEditableScopeIndex( 2 );
|
|
}
|
|
|
|
void QgsVectorLayerProperties::updateFieldsPropertiesDialog()
|
|
{
|
|
QgsEditFormConfig cfg = mLayer->editFormConfig();
|
|
mFieldsPropertiesDialog->setEditFormInit( cfg.uiForm(), cfg.initFunction(), cfg.initCode(), cfg.initFilePath(), cfg.initCodeSource() );
|
|
}
|