QGIS API Documentation  2.99.0-Master (f59acad)
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 #include "qgsproject.h"
28 #include "qgsapplication.h"
29 
31 {
32 }
33 
35 
36 void QgsProjectBadLayerGuiHandler::handleBadLayers( const QList<QDomNode>& layers )
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 
81  QApplication::restoreOverrideCursor();
82 }
83 
84 bool QgsProjectBadLayerGuiHandler::findLayer( const QString& fileFilters, const QDomNode& constLayerNode )
85 {
86  // XXX actually we could possibly get away with a copy of the node
87  QDomNode& layerNode = const_cast<QDomNode&>( constLayerNode );
88 
89  bool retVal = false;
90 
91  switch ( providerType( layerNode ) )
92  {
93  case IS_FILE:
94  QgsDebugMsg( "layer is file based" );
95  retVal = findMissingFile( fileFilters, layerNode );
96  break;
97 
98  case IS_DATABASE:
99  QgsDebugMsg( "layer is database based" );
100  break;
101 
102  case IS_URL:
103  QgsDebugMsg( "layer is URL based" );
104  break;
105 
106  case IS_Unknown:
107  QgsDebugMsg( "layer has an unknown type" );
108  break;
109  }
110  return retVal;
111 }
112 bool QgsProjectBadLayerGuiHandler::findMissingFile( const QString& fileFilters, QDomNode& layerNode )
113 {
114  // Prepend that file name to the valid file format filter list since it
115  // makes it easier for the user to not only find the original file, but to
116  // perhaps find a similar file.
117 
118  QFileInfo originalDataSource( dataSource( layerNode ) );
119 
120  QString memoryQualifier; // to differentiate between last raster and
121  // vector directories
122 
123  switch ( dataType( layerNode ) )
124  {
125  case IS_VECTOR:
126  {
127  memoryQualifier = QStringLiteral( "lastVectorFileFilter" );
128 
129  break;
130  }
131  case IS_RASTER:
132  {
133  memoryQualifier = QStringLiteral( "lastRasterFileFilter" );
134 
135  break;
136  }
137  default:
138  QgsDebugMsg( "unable to determine data type" );
139  return false;
140  }
141 
142  // Prepend the original data source base name to make it easier to pick it
143  // out from a list of other files; however the appropriate filter strings
144  // for the file type will also be added in case the file name itself has
145  // changed, too.
146 
147  QString myFileFilters = originalDataSource.fileName() + ";;" + fileFilters;
148 
149  QStringList selectedFiles;
150  QString enc;
151  QString title = QObject::tr( "Where is '%1' (original location: %2)?" )
152  .arg( originalDataSource.fileName(),
153  originalDataSource.absoluteFilePath() );
154 
155  bool retVal = QgisGui::openFilesRememberingFilter( memoryQualifier,
156  myFileFilters,
157  selectedFiles,
158  enc,
159  title,
160  true );
161 
162  if ( selectedFiles.isEmpty() )
163  {
164  return retVal;
165  }
166  else
167  {
168  setDataSource( layerNode, selectedFiles.first() );
169  if ( ! QgsProject::instance()->read( layerNode ) )
170  {
171  QgsDebugMsg( "unable to re-read layer" );
172  }
173  }
174  return retVal;
175 }
176 
177 void QgsProjectBadLayerGuiHandler::findLayers( const QString& fileFilters, const QList<QDomNode>& layerNodes )
178 {
179 
180  for ( QList<QDomNode>::const_iterator i = layerNodes.begin();
181  i != layerNodes.end();
182  ++i )
183  {
184  if ( findLayer( fileFilters, *i ) )
185  {
186  // If findLayer returns true, the user hit Cancel All button
187  break;
188  }
189  }
190 }
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.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void setDataSource(QDomNode &layerNode, const QString &dataSource)
Set the datasource element to the new value.
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...
static bool mIgnore
Flag to store the Ignore button press of MessageBox used by QgsLegend.
virtual QString fileVectorFilters() const
Return vector file filter string.
DataType dataType(const QDomNode &layerNode)
Returns data type associated with the given QgsProject file Dom node.
bool findMissingFile(const QString &fileFilters, QDomNode &layerNode)
This is used to locate files that have moved or otherwise are missing.
QString dataSource(const QDomNode &layerNode)
Return the data source for the given layer.
bool read(const QFileInfo &file)
Reads a project file.
Definition: qgsproject.cpp:760
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:350
virtual void handleBadLayers(const QList< QDomNode > &layers) override
This method will be called whenever the project tries to load layers which cannot be accessed...
bool findLayer(const QString &fileFilters, const QDomNode &constLayerNode)
Find relocated data source for the given layer.
ProviderType providerType(const QDomNode &layerNode)
Return the physical storage type associated with the given layer.