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