QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
qgsprojectbadlayerguihandler.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsprojectbadlayerguihandler.cpp - handle bad layers
3  ---------------------
4  begin : December 2009
5  copyright : (C) 2009 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 
18 #include <QApplication>
19 #include <QDomDocument>
20 #include <QFileInfo>
21 #include <QMessageBox>
22 #include <QPushButton>
23 
24 #include "qgslogger.h"
25 #include "qgisgui.h"
26 #include "qgsproviderregistry.h"
27 
29 {
30 }
31 
33 
35 {
36  Q_UNUSED( projectDom );
37 
38  QgsDebugMsg( QString( "%1 bad layers found" ).arg( layers.size() ) );
39 
40  // make sure we have arrow cursor (and not a wait cursor)
41  QApplication::setOverrideCursor( Qt::ArrowCursor );
42 
43  QMessageBox messageBox;
44 
45  QAbstractButton *ignoreButton =
46  messageBox.addButton( tr( "Ignore" ), QMessageBox::ActionRole );
47 
48  QAbstractButton *okButton = messageBox.addButton( QMessageBox :: Ok );
49 
50  messageBox.addButton( QMessageBox :: Cancel );
51 
52  messageBox.setWindowTitle( tr( "QGIS Project Read Error" ) );
53  messageBox.setText( tr( "Unable to open one or more project layers.\nChoose "
54  "ignore to continue loading without the missing layers. Choose cancel to "
55  "return to your pre-project load state. Choose OK to try to find the "
56  "missing layers." ) );
57  messageBox.setIcon( QMessageBox::Critical );
58  messageBox.exec();
59 
61 
62  if ( messageBox.clickedButton() == okButton )
63  {
64  QgsDebugMsg( "want to find missing layers is true" );
65 
66  // attempt to find the new locations for missing layers
67  // XXX vector file hard-coded -- but what if it's raster?
68 
70  findLayers( filter, layers );
71  }
72  else if ( messageBox.clickedButton() == ignoreButton )
73  {
75  }
76  else
77  {
78  // Do nothing
79  }
80 
82 }
83 
85 {
86  QString type = layerNode.toElement().attribute( "type" );
87 
88  if ( QString::null == type )
89  {
90  QgsDebugMsg( "cannot find ``type'' attribute" );
91 
92  return IS_BOGUS;
93  }
94 
95  if ( "raster" == type )
96  {
97  QgsDebugMsg( "is a raster" );
98 
99  return IS_RASTER;
100  }
101  else if ( "vector" == type )
102  {
103  QgsDebugMsg( "is a vector" );
104 
105  return IS_VECTOR;
106  }
107 
108  QgsDebugMsg( "is unknown type " + type );
109 
110  return IS_BOGUS;
111 } // dataType_( QDomNode & layerNode )
112 
113 
115 {
116  QDomNode dataSourceNode = layerNode.namedItem( "datasource" );
117 
118  if ( dataSourceNode.isNull() )
119  {
120  QgsDebugMsg( "cannot find datasource node" );
121 
122  return QString::null;
123  }
124 
125  return dataSourceNode.toElement().text();
126 
127 } // dataSource( QDomNode & layerNode )
128 
129 
130 
131 
133 {
134  // XXX but what about rasters that can be URLs? _Can_ they be URLs?
135 
136  switch ( dataType( layerNode ) )
137  {
138  case IS_VECTOR:
139  {
140  QString ds = dataSource( layerNode );
141 
142  QgsDebugMsg( "datasource is " + ds );
143 
144  if ( ds.contains( "host=" ) )
145  {
146  return IS_URL;
147  }
148 #ifdef HAVE_POSTGRESQL
149  else if ( ds.contains( "dbname=" ) )
150  {
151  return IS_DATABASE;
152  }
153 #endif
154  // be default, then, this should be a file based layer data source
155  // XXX is this a reasonable assumption?
156 
157  return IS_FILE;
158  }
159 
160  case IS_RASTER: // rasters are currently only accessed as
161  // physical files
162  return IS_FILE;
163 
164  default:
165  QgsDebugMsg( "unknown ``type'' attribute" );
166  }
167 
168  return IS_Unknown;
169 
170 } // providerType
171 
172 
173 
175 {
176  QDomNode dataSourceNode = layerNode.namedItem( "datasource" );
177  QDomElement dataSourceElement = dataSourceNode.toElement();
178  QDomText dataSourceText = dataSourceElement.firstChild().toText();
179 
180  QgsDebugMsg( "datasource changed from " + dataSourceText.data() );
181 
182  dataSourceText.setData( dataSource );
183 
184  QgsDebugMsg( "to " + dataSourceText.data() );
185 } // setDataSource
186 
187 
188 
189 
190 bool QgsProjectBadLayerGuiHandler::findMissingFile( QString const & fileFilters, QDomNode & layerNode )
191 {
192  // Prepend that file name to the valid file format filter list since it
193  // makes it easier for the user to not only find the original file, but to
194  // perhaps find a similar file.
195 
196  QFileInfo originalDataSource( dataSource( layerNode ) );
197 
198  QString memoryQualifier; // to differentiate between last raster and
199  // vector directories
200 
201  switch ( dataType( layerNode ) )
202  {
203  case IS_VECTOR:
204  {
205  memoryQualifier = "lastVectorFileFilter";
206 
207  break;
208  }
209  case IS_RASTER:
210  {
211  memoryQualifier = "lastRasterFileFilter";
212 
213  break;
214  }
215  default:
216  QgsDebugMsg( "unable to determine data type" );
217  return false;
218  }
219 
220  // Prepend the original data source base name to make it easier to pick it
221  // out from a list of other files; however the appropriate filter strings
222  // for the file type will also be added in case the file name itself has
223  // changed, too.
224 
225  QString myFileFilters = originalDataSource.fileName() + ";;" + fileFilters;
226 
227  QStringList selectedFiles;
228  QString enc;
229  QString title = QObject::tr( "Where is '%1' (original location: %2)?" )
230  .arg( originalDataSource.fileName(),
231  originalDataSource.absoluteFilePath() );
232 
233  bool retVal = QgisGui::openFilesRememberingFilter( memoryQualifier,
234  myFileFilters,
235  selectedFiles,
236  enc,
237  title,
238  true );
239 
240  if ( selectedFiles.isEmpty() )
241  {
242  return retVal;
243  }
244  else
245  {
246  setDataSource( layerNode, selectedFiles.first() );
247  if ( ! QgsProject::instance()->read( layerNode ) )
248  {
249  QgsDebugMsg( "unable to re-read layer" );
250  }
251  }
252  return retVal;
253 } // findMissingFile
254 
255 
256 
257 
258 bool QgsProjectBadLayerGuiHandler::findLayer( QString const & fileFilters, QDomNode const & constLayerNode )
259 {
260  // XXX actually we could possibly get away with a copy of the node
261  QDomNode & layerNode = const_cast<QDomNode&>( constLayerNode );
262 
263  bool retVal = false;
264 
265  switch ( providerType( layerNode ) )
266  {
267  case IS_FILE:
268  QgsDebugMsg( "layer is file based" );
269  retVal = findMissingFile( fileFilters, layerNode );
270  break;
271 
272  case IS_DATABASE:
273  QgsDebugMsg( "layer is database based" );
274  break;
275 
276  case IS_URL:
277  QgsDebugMsg( "layer is URL based" );
278  break;
279 
280  case IS_Unknown:
281  QgsDebugMsg( "layer has an unknown type" );
282  break;
283  }
284  return retVal;
285 } // findLayer
286 
287 
288 
289 
290 void QgsProjectBadLayerGuiHandler::findLayers( QString const & fileFilters, QList<QDomNode> const & layerNodes )
291 {
292 
293  for ( QList<QDomNode>::const_iterator i = layerNodes.begin();
294  i != layerNodes.end();
295  ++i )
296  {
297  if ( findLayer( fileFilters, *i ) )
298  {
299  // If findLayer returns true, the user hit Cancel All button
300  break;
301  }
302  }
303 
304 } // findLayers
305 
bool GUI_EXPORT openFilesRememberingFilter(QString const &filterName, QString const &filters, QStringList &selectedFiles, QString &enc, QString &title, bool cancelAll)
Open files, preferring to have the default file selector be the last one used, if any; also...
Definition: qgisgui.cpp:28
static QgsProviderRegistry * instance(const QString &pluginPath=QString::null)
Means of accessing canonical single instance.
QString attribute(const QString &name, const QString &defValue) const
QString data() const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
ProviderType providerType(QDomNode &layerNode)
Return the physical storage type associated with the given layer.
void findLayers(const QString &fileFilters, const QList< QDomNode > &layerNodes)
Find relocated data sources for given layers These QDom objects represent QgsProject nodes that map t...
virtual void handleBadLayers(const QList< QDomNode > &layers, const QDomDocument &projectDom) override
Implementation of the handler.
QString tr(const char *sourceText, const char *disambiguation, int n)
static bool mIgnore
Flag to store the Ignore button press of MessageBox used by QgsLegend.
int size() const
virtual QString fileVectorFilters() const
Return vector file filter string.
QAbstractButton * clickedButton() const
QDomElement toElement() const
void setData(const QString &v)
QString text() const
DataType dataType(QDomNode &layerNode)
Returns data type associated with the given QgsProject file Dom node.
void setWindowTitle(const QString &title)
QString fileName() const
bool isEmpty() const
QString absoluteFilePath() const
void setText(const QString &text)
bool findMissingFile(const QString &fileFilters, QDomNode &layerNode)
This is used to locate files that have moved or otherwise are missing.
void setOverrideCursor(const QCursor &cursor)
QString dataSource(QDomNode &layerNode)
Return the data source for the given layer.
void restoreOverrideCursor()
T & first()
void setIcon(Icon)
iterator end()
QDomNode namedItem(const QString &name) const
bool contains(QChar ch, Qt::CaseSensitivity cs) const
bool read(const QFileInfo &file)
Reads a project file.
Definition: qgsproject.cpp:739
bool isNull() const
QDomNode firstChild() const
void setDataSource(QDomNode &layerNode, const QString &dataSource)
Set the datasource element to the new value.
ProviderType
the three flavors for data
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:382
void addButton(QAbstractButton *button, ButtonRole role)
QDomText toText() const
bool findLayer(const QString &fileFilters, const QDomNode &constLayerNode)
Find relocated data source for the given layer.
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
iterator begin()