Skip to content
Permalink
Browse files
Move custom projections dialog to a tab in the main settings dialog
Avoids an isolated settings dialog and helps consolidate all settings
in the one place.

For now the top-level Custom Projections menu action remains
and just opens the options dialog at the corresponding page, but
in a few releases time (or in 4.0) we could safely remove this.
  • Loading branch information
nyalldawson committed Jan 14, 2022
1 parent f288ab1 commit e8f427a1f0c289b1032c47e7a1916c64d4b2694b
@@ -26,7 +26,6 @@ set(QGIS_APP_SRCS
qgsbookmarkeditordialog.cpp
qgsclipboard.cpp
qgscustomization.cpp
qgscustomprojectiondialog.cpp
qgsdatumtransformtablewidget.cpp
qgsdevtoolspanelwidget.cpp
qgsdiscoverrelationsdialog.cpp
@@ -228,6 +227,7 @@ set(QGIS_APP_SRCS

options/qgsadvancedoptions.cpp
options/qgscodeeditoroptions.cpp
options/qgscustomprojectionoptions.cpp
options/qgsgpsdeviceoptions.cpp
options/qgsoptions.cpp
options/qgsoptionsutils.cpp
@@ -1,5 +1,5 @@
/***************************************************************************
qgscustomprojectiondialog.cpp
qgscustomprojectionoptions.cpp
-------------------
begin : 2005
@@ -16,43 +16,30 @@
* *
***************************************************************************/

#include "qgscustomprojectiondialog.h"

//qgis includes
#include "qgis.h" //<--magick numbers
#include "qgisapp.h" //<--theme icons
#include "qgscustomprojectionoptions.h"
#include "qgsapplication.h"
#include "qgslogger.h"
#include "qgsprojectionselectiondialog.h"
#include "qgssettings.h"
#include "qgsgui.h"
#include "qgscoordinatereferencesystemregistry.h"

//qt includes
#include <QFileInfo>
#include <QMessageBox>
#include <QLocale>
#include <QRegularExpression>
#include <QFileInfo>

QgsCustomProjectionDialog::QgsCustomProjectionDialog( QWidget *parent, Qt::WindowFlags fl )
: QDialog( parent, fl )
QgsCustomProjectionOptionsWidget::QgsCustomProjectionOptionsWidget( QWidget *parent )
: QgsOptionsPageWidget( parent )
{
setupUi( this );
QgsGui::enableAutoGeometryRestore( this );
setObjectName( QStringLiteral( "QgsCustomProjectionOptionsWidget" ) );

connect( pbnAdd, &QPushButton::clicked, this, &QgsCustomProjectionDialog::pbnAdd_clicked );
connect( pbnRemove, &QPushButton::clicked, this, &QgsCustomProjectionDialog::pbnRemove_clicked );
connect( leNameList, &QTreeWidget::currentItemChanged, this, &QgsCustomProjectionDialog::leNameList_currentItemChanged );
connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsCustomProjectionDialog::buttonBox_accepted );
connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsCustomProjectionDialog::showHelp );
connect( pbnAdd, &QPushButton::clicked, this, &QgsCustomProjectionOptionsWidget::pbnAdd_clicked );
connect( pbnRemove, &QPushButton::clicked, this, &QgsCustomProjectionOptionsWidget::pbnRemove_clicked );
connect( leNameList, &QTreeWidget::currentItemChanged, this, &QgsCustomProjectionOptionsWidget::leNameList_currentItemChanged );

leNameList->setSelectionMode( QAbstractItemView::ExtendedSelection );

// user database is created at QGIS startup in QgisApp::createDB
// we just check whether there is our database [MD]
QFileInfo fileInfo;
fileInfo.setFile( QgsApplication::qgisSettingsDirPath() );
if ( !fileInfo.exists() )
if ( !QFileInfo::exists( QgsApplication::qgisSettingsDirPath() ) )
{
QgsDebugMsg( QStringLiteral( "The qgis.db does not exist" ) );
}
@@ -72,7 +59,7 @@ QgsCustomProjectionDialog::QgsCustomProjectionDialog( QWidget *parent, Qt::Windo

QgsCoordinateReferenceSystem crs;
Qgis::CrsDefinitionFormat format;
if ( mDefinitions[0].wkt.isEmpty() )
if ( mDefinitions.at( 0 ).wkt.isEmpty() )
{
crs.createFromProj( mDefinitions[0].proj );
format = Qgis::CrsDefinitionFormat::Proj;
@@ -90,15 +77,15 @@ QgsCustomProjectionDialog::QgsCustomProjectionDialog( QWidget *parent, Qt::Windo

leNameList->hideColumn( QgisCrsIdColumn );

connect( leName, &QLineEdit::textChanged, this, &QgsCustomProjectionDialog::updateListFromCurrentItem );
connect( leName, &QLineEdit::textChanged, this, &QgsCustomProjectionOptionsWidget::updateListFromCurrentItem );
connect( mCrsDefinitionWidget, &QgsCrsDefinitionWidget::crsChanged, this, [ = ]
{
if ( !mBlockUpdates )
updateListFromCurrentItem();
} );
}

void QgsCustomProjectionDialog::populateList()
void QgsCustomProjectionOptionsWidget::populateList()
{
const QList< QgsCoordinateReferenceSystemRegistry::UserCrsDetails > userCrsList = QgsApplication::coordinateReferenceSystemRegistry()->userCrsList();

@@ -136,7 +123,7 @@ void QgsCustomProjectionDialog::populateList()
}
}

bool QgsCustomProjectionDialog::saveCrs( QgsCoordinateReferenceSystem crs, const QString &name, const QString &existingId, bool newEntry, Qgis::CrsDefinitionFormat format )
bool QgsCustomProjectionOptionsWidget::saveCrs( const QgsCoordinateReferenceSystem &crs, const QString &name, const QString &existingId, bool newEntry, Qgis::CrsDefinitionFormat format )
{
QString id = existingId;
if ( newEntry )
@@ -162,7 +149,7 @@ bool QgsCustomProjectionDialog::saveCrs( QgsCoordinateReferenceSystem crs, const
return true;
}

void QgsCustomProjectionDialog::pbnAdd_clicked()
void QgsCustomProjectionOptionsWidget::pbnAdd_clicked()
{
QString name = tr( "new CRS" );

@@ -183,7 +170,7 @@ void QgsCustomProjectionDialog::pbnAdd_clicked()
mCrsDefinitionWidget->setFormat( Qgis::CrsDefinitionFormat::Wkt );
}

void QgsCustomProjectionDialog::pbnRemove_clicked()
void QgsCustomProjectionOptionsWidget::pbnRemove_clicked()
{
const QModelIndexList selection = leNameList->selectionModel()->selectedRows();
if ( selection.empty() )
@@ -218,7 +205,7 @@ void QgsCustomProjectionDialog::pbnRemove_clicked()
}
}

void QgsCustomProjectionDialog::leNameList_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem *previous )
void QgsCustomProjectionOptionsWidget::leNameList_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem *previous )
{
//Store the modifications made to the current element before moving on
int currentIndex, previousIndex;
@@ -252,7 +239,7 @@ void QgsCustomProjectionDialog::leNameList_currentItemChanged( QTreeWidgetItem *

mBlockUpdates++;
mCrsDefinitionWidget->setDefinitionString( !mDefinitions[currentIndex].wkt.isEmpty() ? current->data( 0, FormattedWktRole ).toString() : mDefinitions[currentIndex].proj );
mCrsDefinitionWidget->setFormat( mDefinitions[currentIndex].wkt.isEmpty() ? Qgis::CrsDefinitionFormat::Proj : Qgis::CrsDefinitionFormat::Wkt );
mCrsDefinitionWidget->setFormat( mDefinitions.at( currentIndex ).wkt.isEmpty() ? Qgis::CrsDefinitionFormat::Proj : Qgis::CrsDefinitionFormat::Wkt );
mBlockUpdates--;
}
else
@@ -267,12 +254,10 @@ void QgsCustomProjectionDialog::leNameList_currentItemChanged( QTreeWidgetItem *
}
}

void QgsCustomProjectionDialog::buttonBox_accepted()
bool QgsCustomProjectionOptionsWidget::isValid()
{
updateListFromCurrentItem();

QgsDebugMsgLevel( QStringLiteral( "We save the modified CRS." ), 4 );

//Check if all CRS are valid:
QgsCoordinateReferenceSystem crs;
for ( const Definition &def : std::as_const( mDefinitions ) )
@@ -296,7 +281,7 @@ void QgsCustomProjectionDialog::buttonBox_accepted()

QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ),
tr( "The definition of '%1' is not valid." ).arg( def.name ) );
return;
return false;
}
else if ( !crs.authid().isEmpty() && !crs.authid().startsWith( QLatin1String( "USER" ), Qt::CaseInsensitive ) )
{
@@ -334,12 +319,19 @@ void QgsCustomProjectionDialog::buttonBox_accepted()
tr( "Cannot save '%1' — the definition is equivalent to %2." ).arg( def.name, crs.authid() ) );
}
}
return;
return false;
}
}
return true;
}

void QgsCustomProjectionOptionsWidget::apply()
{
// note that isValid() will have already been called prior to this, so we don't need to re-validate!

//Modify the CRS changed:
bool saveSuccess = true;
QgsCoordinateReferenceSystem crs;
for ( const Definition &def : std::as_const( mDefinitions ) )
{
if ( !def.wkt.isEmpty() )
@@ -373,16 +365,12 @@ void QgsCustomProjectionDialog::buttonBox_accepted()
saveSuccess &= QgsApplication::coordinateReferenceSystemRegistry()->removeUserCrs( mDeletedCRSs[i].toLong() );
if ( ! saveSuccess )
{
QgsDebugMsg( QStringLiteral( "Error deleting CRS for '%1'" ).arg( mDefinitions[i].name ) );
QgsDebugMsg( QStringLiteral( "Error deleting CRS for '%1'" ).arg( mDefinitions.at( i ).name ) );
}
}
if ( saveSuccess )
{
accept();
}
}

void QgsCustomProjectionDialog::updateListFromCurrentItem()
void QgsCustomProjectionOptionsWidget::updateListFromCurrentItem()
{
QTreeWidgetItem *item = leNameList->currentItem();
if ( !item )
@@ -411,12 +399,7 @@ void QgsCustomProjectionDialog::updateListFromCurrentItem()
item->setData( 0, FormattedWktRole, mCrsDefinitionWidget->definitionString() );
}

void QgsCustomProjectionDialog::showHelp()
{
QgsHelp::openHelp( QStringLiteral( "working_with_projections/working_with_projections.html" ) );
}

QString QgsCustomProjectionDialog::multiLineWktToSingleLine( const QString &wkt )
QString QgsCustomProjectionOptionsWidget::multiLineWktToSingleLine( const QString &wkt )
{
QString res = wkt;
QRegularExpression re( QStringLiteral( "\\s*\\n\\s*" ) );
@@ -425,3 +408,33 @@ QString QgsCustomProjectionDialog::multiLineWktToSingleLine( const QString &wkt
return res;
}

QString QgsCustomProjectionOptionsWidget::helpKey() const
{
return QStringLiteral( "working_with_projections/working_with_projections.html" );
}


//
// QgsCustomProjectionOptionsFactory
//
QgsCustomProjectionOptionsFactory::QgsCustomProjectionOptionsFactory()
: QgsOptionsWidgetFactory( tr( "Custom Projections" ), QIcon() )
{

}

QIcon QgsCustomProjectionOptionsFactory::icon() const
{
return QgsApplication::getThemeIcon( QStringLiteral( "mActionCustomProjection.svg" ) );
}

QgsOptionsPageWidget *QgsCustomProjectionOptionsFactory::createWidget( QWidget *parent ) const
{
return new QgsCustomProjectionOptionsWidget( parent );
}

QStringList QgsCustomProjectionOptionsFactory::path() const
{
return {QStringLiteral( "crs_and_transforms" ) };
}

@@ -1,5 +1,5 @@
/***************************************************************************
qgscustomprojectiondialog.h
qgscustomprojectionoptions.h
-------------------
begin : 2005
@@ -15,42 +15,45 @@
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSCUSTOMCRSDIALOG_H
#define QGSCUSTOMCRSDIALOG_H
#ifndef QGSCUSTOMPROJECTIONOPTIONS_H
#define QGSCUSTOMPROJECTIONOPTIONS_H

#include "ui_qgscustomprojectiondialogbase.h"
#include "qgsoptionswidgetfactory.h"
#include "qgshelp.h"
#include "qgscoordinatereferencesystem.h"
#include "qgis_app.h"

class QDir;

/**
* The custom projection widget is used to define the projection family, ellipsoid and parameters needed by proj4 to assemble a customized projection definition.
* \ingroup app
* \class QgsCustomProjectionOptionsWidget
* \brief The custom projection widget is used to define the projection family, ellipsoid and parameters needed by proj to assemble a customized projection definition.
*
* The resulting projection will be stored in an sqlite backend.
*/
class APP_EXPORT QgsCustomProjectionDialog : public QDialog, private Ui::QgsCustomProjectionDialogBase
*/
class QgsCustomProjectionOptionsWidget: public QgsOptionsPageWidget, private Ui::QgsCustomProjectionWidgetBase
{
Q_OBJECT
public:
QgsCustomProjectionDialog( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::WindowFlags() );
QgsCustomProjectionOptionsWidget( QWidget *parent = nullptr );

public slots:
QString helpKey() const override;
bool isValid() override;
void apply() override;

private slots:
void pbnAdd_clicked();
void pbnRemove_clicked();
void leNameList_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem *prev );
void buttonBox_accepted();

private slots:

void updateListFromCurrentItem();

private:

//helper functions
void populateList();
bool saveCrs( QgsCoordinateReferenceSystem crs, const QString &name, const QString &id, bool newEntry, Qgis::CrsDefinitionFormat format );
void showHelp();
bool saveCrs( const QgsCoordinateReferenceSystem &crs, const QString &name, const QString &id, bool newEntry, Qgis::CrsDefinitionFormat format );
QString multiLineWktToSingleLine( const QString &wkt );

//These two QMap store the values as they are on the database when loading
@@ -81,8 +84,22 @@ class APP_EXPORT QgsCustomProjectionDialog : public QDialog, private Ui::QgsCust

int mBlockUpdates = 0;

};


class QgsCustomProjectionOptionsFactory : public QgsOptionsWidgetFactory
{
Q_OBJECT

public:

QgsCustomProjectionOptionsFactory();

QIcon icon() const override;
QgsOptionsPageWidget *createWidget( QWidget *parent = nullptr ) const override;
QStringList path() const override;

};


#endif
#endif // QGSCUSTOMPROJECTIONOPTIONS_H
@@ -110,6 +110,7 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QList<QgsOpti
mTreeModel->appendRow( createItem( QCoreApplication::translate( "QgsOptionsBase", "System" ), QCoreApplication::translate( "QgsOptionsBase", "System" ), QStringLiteral( "propertyicons/system.svg" ) ) );

QStandardItem *crsGroup = new QStandardItem( QCoreApplication::translate( "QgsOptionsBase", "CRS and Transforms" ) );
crsGroup->setData( QStringLiteral( "crs_and_transforms" ) );
crsGroup->setToolTip( tr( "CRS and Transforms" ) );
crsGroup->setSelectable( false );
crsGroup->appendRow( createItem( QCoreApplication::translate( "QgsOptionsBase", "CRS" ), QCoreApplication::translate( "QgsOptionsBase", "CRS" ), QStringLiteral( "propertyicons/CRS.svg" ) ) );
@@ -114,6 +114,7 @@

#include "options/qgscodeeditoroptions.h"
#include "options/qgsgpsdeviceoptions.h"
#include "options/qgscustomprojectionoptions.h"

#ifdef HAVE_3D
#include "qgs3d.h"
@@ -215,7 +216,6 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
#include "qgscustomprojectopenhandler.h"
#include "qgscustomization.h"
#include "qgscustomlayerorderwidget.h"
#include "qgscustomprojectiondialog.h"
#include "qgsdataitemproviderregistry.h"
#include "qgsdataitemguiproviderregistry.h"
#include "qgsdatasourceuri.h"
@@ -1817,6 +1817,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipBadLayers

mCodeEditorWidgetFactory.reset( std::make_unique< QgsCodeEditorOptionsFactory >() );
mBabelGpsDevicesWidgetFactory.reset( std::make_unique< QgsGpsDeviceOptionsFactory >() );
mCustomProjectionsWidgetFactory.reset( std::make_unique< QgsCustomProjectionOptionsFactory >() );

#ifdef HAVE_3D
m3DOptionsWidgetFactory.reset( std::make_unique< Qgs3DOptionsFactory >() );
@@ -16584,11 +16585,7 @@ void QgisApp::mapCanvas_keyPressed( QKeyEvent *e )

void QgisApp::customProjection()
{
// Create an instance of the Custom Projection Designer modeless dialog.
// Autodelete the dialog when closing since a pointer is not retained.
QgsCustomProjectionDialog *myDialog = new QgsCustomProjectionDialog( this );
myDialog->setAttribute( Qt::WA_DeleteOnClose );
myDialog->show();
showOptionsDialog( this, QStringLiteral( "QgsCustomProjectionOptionsWidget" ) );
}

void QgisApp::newBookmark( bool inProject )

0 comments on commit e8f427a

Please sign in to comment.