1 /***************************************************************************
2                          qgsnewmemorylayerdialog.cpp
3                              -------------------
4     begin                : September 2014
5     copyright            : (C) 2014 by Nyall Dawson, Marco Hugentobler
6     email                : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "qgsnewmemorylayerdialog.h"
19 #include "qgsapplication.h"
20 #include "qgis.h"
21 #include "qgscoordinatereferencesystem.h"
22 #include "qgsproviderregistry.h"
23 #include "qgsvectordataprovider.h"
24 #include "qgsvectorlayer.h"
25 #include "qgsfield.h"
26 #include "qgsfields.h"
27 #include "qgssettings.h"
28 #include "qgsmemoryproviderutils.h"
29 #include "qgsgui.h"
30 #include "qgsiconutils.h"
31 
32 #include <QPushButton>
33 #include <QComboBox>
34 #include <QUuid>
35 #include <QFileDialog>
36 
runAndCreateLayer(QWidget * parent,const QgsCoordinateReferenceSystem & defaultCrs)37 QgsVectorLayer *QgsNewMemoryLayerDialog::runAndCreateLayer( QWidget *parent, const QgsCoordinateReferenceSystem &defaultCrs )
38 {
39   QgsNewMemoryLayerDialog dialog( parent );
40   dialog.setCrs( defaultCrs );
41   if ( dialog.exec() == QDialog::Rejected )
42   {
43     return nullptr;
44   }
45 
46   const QgsWkbTypes::Type geometrytype = dialog.selectedType();
47   const QgsFields fields = dialog.fields();
48   const QString name = dialog.layerName().isEmpty() ? tr( "New scratch layer" ) : dialog.layerName();
49   QgsVectorLayer *newLayer = QgsMemoryProviderUtils::createMemoryLayer( name, fields, geometrytype, dialog.crs() );
50   return newLayer;
51 }
52 
QgsNewMemoryLayerDialog(QWidget * parent,Qt::WindowFlags fl)53 QgsNewMemoryLayerDialog::QgsNewMemoryLayerDialog( QWidget *parent, Qt::WindowFlags fl )
54   : QDialog( parent, fl )
55 {
56   setupUi( this );
57   QgsGui::enableAutoGeometryRestore( this );
58 
59   mNameLineEdit->setText( tr( "New scratch layer" ) );
60 
61   const QgsWkbTypes::Type geomTypes[] =
62   {
63     QgsWkbTypes::NoGeometry,
64     QgsWkbTypes::Point,
65     QgsWkbTypes::LineString,
66     QgsWkbTypes::CompoundCurve,
67     QgsWkbTypes::Polygon,
68     QgsWkbTypes::CurvePolygon,
69     QgsWkbTypes::MultiPoint,
70     QgsWkbTypes::MultiLineString,
71     QgsWkbTypes::MultiCurve,
72     QgsWkbTypes::MultiPolygon,
73     QgsWkbTypes::MultiSurface,
74   };
75 
76   for ( const auto type : geomTypes )
77     mGeometryTypeBox->addItem( QgsIconUtils::iconForWkbType( type ), QgsWkbTypes::translatedDisplayString( type ), type );
78   mGeometryTypeBox->setCurrentIndex( -1 );
79 
80   mGeometryWithZCheckBox->setEnabled( false );
81   mGeometryWithMCheckBox->setEnabled( false );
82   mCrsSelector->setEnabled( false );
83   mCrsSelector->setShowAccuracyWarnings( true );
84 
85   mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldText.svg" ) ), tr( "Text" ), "string" );
86   mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldInteger.svg" ) ), tr( "Whole Number" ), "integer" );
87   mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldFloat.svg" ) ), tr( "Decimal Number" ), "double" );
88   mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldBool.svg" ) ), tr( "Boolean" ), "bool" );
89   mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldDate.svg" ) ), tr( "Date" ), "date" );
90   mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldTime.svg" ) ), tr( "Time" ), "time" );
91   mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldDateTime.svg" ) ), tr( "Date and Time" ), "datetime" );
92   mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldBinary.svg" ) ), tr( "Binary (BLOB)" ), "binary" );
93   mTypeBox_currentIndexChanged( 1 );
94 
95   mWidth->setValidator( new QIntValidator( 1, 255, this ) );
96   mPrecision->setValidator( new QIntValidator( 0, 30, this ) );
97 
98   mAddAttributeButton->setEnabled( false );
99   mRemoveAttributeButton->setEnabled( false );
100 
101   mOkButton = mButtonBox->button( QDialogButtonBox::Ok );
102   mOkButton->setEnabled( false );
103 
104   connect( mGeometryTypeBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewMemoryLayerDialog::geometryTypeChanged );
105   connect( mFieldNameEdit, &QLineEdit::textChanged, this, &QgsNewMemoryLayerDialog::fieldNameChanged );
106   connect( mTypeBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewMemoryLayerDialog::mTypeBox_currentIndexChanged );
107   connect( mAttributeView, &QTreeWidget::itemSelectionChanged, this, &QgsNewMemoryLayerDialog::selectionChanged );
108   connect( mAddAttributeButton, &QToolButton::clicked, this, &QgsNewMemoryLayerDialog::mAddAttributeButton_clicked );
109   connect( mRemoveAttributeButton, &QToolButton::clicked, this, &QgsNewMemoryLayerDialog::mRemoveAttributeButton_clicked );
110   connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsNewMemoryLayerDialog::showHelp );
111 }
112 
selectedType() const113 QgsWkbTypes::Type QgsNewMemoryLayerDialog::selectedType() const
114 {
115   QgsWkbTypes::Type geomType = QgsWkbTypes::Unknown;
116   geomType = static_cast<QgsWkbTypes::Type>
117              ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
118 
119   if ( geomType != QgsWkbTypes::Unknown && geomType != QgsWkbTypes::NoGeometry )
120   {
121     if ( mGeometryWithZCheckBox->isChecked() )
122       geomType = QgsWkbTypes::addZ( geomType );
123     if ( mGeometryWithMCheckBox->isChecked() )
124       geomType = QgsWkbTypes::addM( geomType );
125   }
126 
127   return geomType;
128 }
129 
geometryTypeChanged(int)130 void QgsNewMemoryLayerDialog::geometryTypeChanged( int )
131 {
132   const QgsWkbTypes::Type geomType = static_cast<QgsWkbTypes::Type>
133                                      ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
134 
135   const bool isSpatial = geomType != QgsWkbTypes::NoGeometry;
136   mGeometryWithZCheckBox->setEnabled( isSpatial );
137   mGeometryWithMCheckBox->setEnabled( isSpatial );
138   mCrsSelector->setEnabled( isSpatial );
139 
140   const bool ok = ( !mNameLineEdit->text().isEmpty() && mGeometryTypeBox->currentIndex() != -1 );
141   mOkButton->setEnabled( ok );
142 }
143 
mTypeBox_currentIndexChanged(int index)144 void QgsNewMemoryLayerDialog::mTypeBox_currentIndexChanged( int index )
145 {
146   switch ( index )
147   {
148     case 0: // Text data
149       if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 255 )
150         mWidth->setText( QStringLiteral( "255" ) );
151       mPrecision->clear();
152       mPrecision->setEnabled( false );
153       mWidth->setValidator( new QIntValidator( 1, 255, this ) );
154       break;
155     case 1: // Whole number
156       if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 10 )
157         mWidth->setText( QStringLiteral( "10" ) );
158       mPrecision->clear();
159       mPrecision->setEnabled( false );
160       mWidth->setValidator( new QIntValidator( 1, 10, this ) );
161       break;
162     case 2: // Decimal number
163       if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 30 )
164         mWidth->setText( QStringLiteral( "30" ) );
165       if ( mPrecision->text().toInt() < 1 || mPrecision->text().toInt() > 30 )
166         mPrecision->setText( QStringLiteral( "6" ) );
167       mPrecision->setEnabled( true );
168       mWidth->setValidator( new QIntValidator( 1, 20, this ) );
169       break;
170     case 3: // Boolean
171       mWidth->clear();
172       mWidth->setEnabled( false );
173       mPrecision->clear();
174       mPrecision->setEnabled( false );
175       break;
176     case 4: // Date
177       mWidth->clear();
178       mWidth->setEnabled( false );
179       mPrecision->clear();
180       mPrecision->setEnabled( false );
181       break;
182     case 5: // Time
183       mWidth->clear();
184       mWidth->setEnabled( false );
185       mPrecision->clear();
186       mPrecision->setEnabled( false );
187       break;
188     case 6: // Datetime
189       mWidth->clear();
190       mWidth->setEnabled( false );
191       mPrecision->clear();
192       mPrecision->setEnabled( false );
193       break;
194     case 7: // Binary
195       mWidth->clear();
196       mWidth->setEnabled( false );
197       mPrecision->clear();
198       mPrecision->setEnabled( false );
199       break;
200 
201     default:
202       QgsDebugMsg( QStringLiteral( "unexpected index" ) );
203       break;
204   }
205 }
206 
setCrs(const QgsCoordinateReferenceSystem & crs)207 void QgsNewMemoryLayerDialog::setCrs( const QgsCoordinateReferenceSystem &crs )
208 {
209   mCrsSelector->setCrs( crs );
210 }
211 
crs() const212 QgsCoordinateReferenceSystem QgsNewMemoryLayerDialog::crs() const
213 {
214   return mCrsSelector->crs();
215 }
216 
layerName() const217 QString QgsNewMemoryLayerDialog::layerName() const
218 {
219   return mNameLineEdit->text();
220 }
221 
fieldNameChanged(const QString & name)222 void QgsNewMemoryLayerDialog::fieldNameChanged( const QString &name )
223 {
224   mAddAttributeButton->setDisabled( name.isEmpty() || ! mAttributeView->findItems( name, Qt::MatchExactly ).isEmpty() );
225 }
226 
selectionChanged()227 void QgsNewMemoryLayerDialog::selectionChanged()
228 {
229   mRemoveAttributeButton->setDisabled( mAttributeView->selectedItems().isEmpty() );
230 }
231 
fields() const232 QgsFields QgsNewMemoryLayerDialog::fields() const
233 {
234   QgsFields fields = QgsFields();
235 
236   QTreeWidgetItemIterator it( mAttributeView );
237   while ( *it )
238   {
239     const QString name( ( *it )->text( 0 ) );
240     const QString typeName( ( *it )->text( 1 ) );
241     const int width = ( *it )->text( 2 ).toInt();
242     const int precision = ( *it )->text( 3 ).toInt();
243     QVariant::Type fieldType = QVariant::Invalid;
244     if ( typeName == QLatin1String( "string" ) )
245       fieldType = QVariant::String;
246     else if ( typeName == QLatin1String( "integer" ) )
247       fieldType = QVariant::Int;
248     else if ( typeName == QLatin1String( "double" ) )
249       fieldType = QVariant::Double;
250     else if ( typeName == QLatin1String( "bool" ) )
251       fieldType = QVariant::Bool;
252     else if ( typeName == QLatin1String( "date" ) )
253       fieldType = QVariant::Date;
254     else if ( typeName == QLatin1String( "time" ) )
255       fieldType = QVariant::Time;
256     else if ( typeName == QLatin1String( "datetime" ) )
257       fieldType = QVariant::DateTime;
258 
259     const QgsField field = QgsField( name, fieldType, typeName, width, precision );
260     fields.append( field );
261     ++it;
262   }
263 
264   return fields;
265 }
266 
mAddAttributeButton_clicked()267 void QgsNewMemoryLayerDialog::mAddAttributeButton_clicked()
268 {
269   if ( !mFieldNameEdit->text().isEmpty() )
270   {
271     const QString fieldName = mFieldNameEdit->text();
272     const QString fieldType = mTypeBox->currentData( Qt::UserRole ).toString();
273     const QString width = mWidth->text();
274     const QString precision = mPrecision->text();
275     mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << fieldName << fieldType << width << precision ) );
276 
277     mFieldNameEdit->clear();
278   }
279 }
280 
mRemoveAttributeButton_clicked()281 void QgsNewMemoryLayerDialog::mRemoveAttributeButton_clicked()
282 {
283   delete mAttributeView->currentItem();
284 }
285 
showHelp()286 void QgsNewMemoryLayerDialog::showHelp()
287 {
288   QgsHelp::openHelp( QStringLiteral( "managing_data_source/create_layers.html#creating-a-new-temporary-scratch-layer" ) );
289 }
290