QGIS API Documentation  2.99.0-Master (8ec3eaf)
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 
30 {
31 }
32 
34 
35 void QgsProjectBadLayerGuiHandler::handleBadLayers( const QList<QDomNode>& layers )
36 {
37  QgsDebugMsg( QString( "%1 bad layers found" ).arg( layers.size() ) );
38 
39  // make sure we have arrow cursor (and not a wait cursor)
40  QApplication::setOverrideCursor( Qt::ArrowCursor );
41 
42  QMessageBox messageBox;
43 
44  QAbstractButton *ignoreButton =
45  messageBox.addButton( tr( "Ignore" ), QMessageBox::ActionRole );
46 
47  QAbstractButton *okButton = messageBox.addButton( QMessageBox :: Ok );
48 
49  messageBox.addButton( QMessageBox :: Cancel );
50 
51  messageBox.setWindowTitle( tr( "QGIS Project Read Error" ) );
52  messageBox.setText( tr( "Unable to open one or more project layers.\nChoose "
53  "ignore to continue loading without the missing layers. Choose cancel to "
54  "return to your pre-project load state. Choose OK to try to find the "
55  "missing layers." ) );
56  messageBox.setIcon( QMessageBox::Critical );
57  messageBox.exec();
58 
60 
61  if ( messageBox.clickedButton() == okButton )
62  {
63  QgsDebugMsg( "want to find missing layers is true" );
64 
65  // attempt to find the new locations for missing layers
66  // XXX vector file hard-coded -- but what if it's raster?
67 
69  findLayers( filter, layers );
70  }
71  else if ( messageBox.clickedButton() == ignoreButton )
72  {
74  }
75  else
76  {
77  // Do nothing
78  }
79 
80  QApplication::restoreOverrideCursor();
81 }
82 
83 bool QgsProjectBadLayerGuiHandler::findLayer( const QString& fileFilters, const QDomNode& constLayerNode )
84 {
85  // XXX actually we could possibly get away with a copy of the node
86  QDomNode& layerNode = const_cast<QDomNode&>( constLayerNode );
87 
88  bool retVal = false;
89 
90  switch ( providerType( layerNode ) )
91  {
92  case IS_FILE:
93  QgsDebugMsg( "layer is file based" );
94  retVal = findMissingFile( fileFilters, layerNode );
95  break;
96 
97  case IS_DATABASE:
98  QgsDebugMsg( "layer is database based" );
99  break;
100 
101  case IS_URL:
102  QgsDebugMsg( "layer is URL based" );
103  break;
104 
105  case IS_Unknown:
106  QgsDebugMsg( "layer has an unknown type" );
107  break;
108  }
109  return retVal;
110 }
111 bool QgsProjectBadLayerGuiHandler::findMissingFile( const QString& fileFilters, QDomNode& layerNode )
112 {
113  // Prepend that file name to the valid file format filter list since it
114  // makes it easier for the user to not only find the original file, but to
115  // perhaps find a similar file.
116 
117  QFileInfo originalDataSource( dataSource( layerNode ) );
118 
119  QString memoryQualifier; // to differentiate between last raster and
120  // vector directories
121 
122  switch ( dataType( layerNode ) )
123  {
124  case IS_VECTOR:
125  {
126  memoryQualifier = QStringLiteral( "lastVectorFileFilter" );
127 
128  break;
129  }
130  case IS_RASTER:
131  {
132  memoryQualifier = QStringLiteral( "lastRasterFileFilter" );
133 
134  break;
135  }
136  default:
137  QgsDebugMsg( "unable to determine data type" );
138  return false;
139  }
140 
141  // Prepend the original data source base name to make it easier to pick it
142  // out from a list of other files; however the appropriate filter strings
143  // for the file type will also be added in case the file name itself has
144  // changed, too.
145 
146  QString myFileFilters = originalDataSource.fileName() + ";;" + fileFilters;
147 
148  QStringList selectedFiles;
149  QString enc;
150  QString title = QObject::tr( "Where is '%1' (original location: %2)?" )
151  .arg( originalDataSource.fileName(),
152  originalDataSource.absoluteFilePath() );
153 
154  bool retVal = QgisGui::openFilesRememberingFilter( memoryQualifier,
155  myFileFilters,
156  selectedFiles,
157  enc,
158  title,
159  true );
160 
161  if ( selectedFiles.isEmpty() )
162  {
163  return retVal;
164  }
165  else
166  {
167  setDataSource( layerNode, selectedFiles.first() );
168  if ( ! QgsProject::instance()->read( layerNode ) )
169  {
170  QgsDebugMsg( "unable to re-read layer" );
171  }
172  }
173  return retVal;
174 }
175 
176 void QgsProjectBadLayerGuiHandler::findLayers( const QString& fileFilters, const QList<QDomNode>& layerNodes )
177 {
178 
179  for ( QList<QDomNode>::const_iterator i = layerNodes.begin();
180  i != layerNodes.end();
181  ++i )
182  {
183  if ( findLayer( fileFilters, *i ) )
184  {
185  // If findLayer returns true, the user hit Cancel All button
186  break;
187  }
188  }
189 }
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:758
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:348
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.