Quantum GIS API Documentation  1.8
src/gui/qgsprojectbadlayerguihandler.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002     qgsprojectbadlayerguihandler.cpp - handle bad layers
00003     ---------------------
00004     begin                : December 2009
00005     copyright            : (C) 2009 by Martin Dobias
00006     email                : wonder.sk at gmail.com
00007  ***************************************************************************
00008  *                                                                         *
00009  *   This program is free software; you can redistribute it and/or modify  *
00010  *   it under the terms of the GNU General Public License as published by  *
00011  *   the Free Software Foundation; either version 2 of the License, or     *
00012  *   (at your option) any later version.                                   *
00013  *                                                                         *
00014  ***************************************************************************/
00015 
00016 #include "qgsprojectbadlayerguihandler.h"
00017 
00018 #include <QApplication>
00019 #include <QDomDocument>
00020 #include <QFileInfo>
00021 #include <QMessageBox>
00022 #include <QPushButton>
00023 
00024 #include "qgslogger.h"
00025 #include "qgisgui.h"
00026 #include "qgsproviderregistry.h"
00027 
00028 QgsProjectBadLayerGuiHandler::QgsProjectBadLayerGuiHandler()
00029 {
00030 }
00031 
00032 bool QgsProjectBadLayerGuiHandler::mIgnore = false;
00033 
00034 void QgsProjectBadLayerGuiHandler::handleBadLayers( QList<QDomNode> layers, QDomDocument projectDom )
00035 {
00036   Q_UNUSED( projectDom );
00037 
00038   QgsDebugMsg( QString( "%1 bad layers found" ).arg( layers.size() ) );
00039 
00040   // make sure we have arrow cursor (and not a wait cursor)
00041   QApplication::setOverrideCursor( Qt::ArrowCursor );
00042 
00043   QMessageBox messageBox;
00044 
00045   QAbstractButton *ignoreButton =
00046     messageBox.addButton( tr( "Ignore" ), QMessageBox::ActionRole );
00047 
00048   QAbstractButton *okButton = messageBox.addButton( QMessageBox :: Ok );
00049 
00050   messageBox.addButton( QMessageBox :: Cancel );
00051 
00052   messageBox.setWindowTitle( tr( "QGIS Project Read Error" ) );
00053   messageBox.setText( tr( "Unable to open one or more project layers.\nChoose "
00054                           "ignore to continue loading without the missing layers. Choose cancel to "
00055                           "return to your pre-project load state. Choose OK to try to find the "
00056                           "missing layers." ) );
00057   messageBox.setIcon( QMessageBox::Critical );
00058   messageBox.exec();
00059 
00060   QgsProjectBadLayerGuiHandler::mIgnore = false;
00061 
00062   if ( messageBox.clickedButton() == okButton )
00063   {
00064     QgsDebugMsg( "want to find missing layers is true" );
00065 
00066     // attempt to find the new locations for missing layers
00067     // XXX vector file hard-coded -- but what if it's raster?
00068 
00069     QString filter = QgsProviderRegistry::instance()->fileVectorFilters();
00070     findLayers( filter, layers );
00071   }
00072   else if ( messageBox.clickedButton() == ignoreButton )
00073   {
00074     QgsProjectBadLayerGuiHandler::mIgnore = true;
00075   }
00076   else
00077   {
00078     // Do nothing
00079   }
00080 
00081   QApplication::restoreOverrideCursor();
00082 }
00083 
00084 QgsProjectBadLayerGuiHandler::DataType QgsProjectBadLayerGuiHandler::dataType( QDomNode & layerNode )
00085 {
00086   QString type = layerNode.toElement().attribute( "type" );
00087 
00088   if ( QString::null == type )
00089   {
00090     QgsDebugMsg( "cannot find ``type'' attribute" );
00091 
00092     return IS_BOGUS;
00093   }
00094 
00095   if ( "raster" == type )
00096   {
00097     QgsDebugMsg( "is a raster" );
00098 
00099     return IS_RASTER;
00100   }
00101   else if ( "vector" == type )
00102   {
00103     QgsDebugMsg( "is a vector" );
00104 
00105     return IS_VECTOR;
00106   }
00107 
00108   QgsDebugMsg( "is unknown type " + type );
00109 
00110   return IS_BOGUS;
00111 } // dataType_( QDomNode & layerNode )
00112 
00113 
00114 QString QgsProjectBadLayerGuiHandler::dataSource( QDomNode & layerNode )
00115 {
00116   QDomNode dataSourceNode = layerNode.namedItem( "datasource" );
00117 
00118   if ( dataSourceNode.isNull() )
00119   {
00120     QgsDebugMsg( "cannot find datasource node" );
00121 
00122     return QString::null;
00123   }
00124 
00125   return dataSourceNode.toElement().text();
00126 
00127 } // dataSource( QDomNode & layerNode )
00128 
00129 
00130 
00131 
00132 QgsProjectBadLayerGuiHandler::ProviderType QgsProjectBadLayerGuiHandler::providerType( QDomNode & layerNode )
00133 {
00134   // XXX but what about rasters that can be URLs?  _Can_ they be URLs?
00135 
00136   switch ( dataType( layerNode ) )
00137   {
00138     case IS_VECTOR:
00139     {
00140       QString ds = dataSource( layerNode );
00141 
00142       QgsDebugMsg( "datasource is " + ds );
00143 
00144       if ( ds.contains( "host=" ) )
00145       {
00146         return IS_URL;
00147       }
00148 #ifdef HAVE_POSTGRESQL
00149       else if ( ds.contains( "dbname=" ) )
00150       {
00151         return IS_DATABASE;
00152       }
00153 #endif
00154       // be default, then, this should be a file based layer data source
00155       // XXX is this a reasonable assumption?
00156 
00157       return IS_FILE;
00158     }
00159 
00160     case IS_RASTER:         // rasters are currently only accessed as
00161       // physical files
00162       return IS_FILE;
00163 
00164     default:
00165       QgsDebugMsg( "unknown ``type'' attribute" );
00166   }
00167 
00168   return IS_Unknown;
00169 
00170 } // providerType
00171 
00172 
00173 
00174 void QgsProjectBadLayerGuiHandler::setDataSource( QDomNode & layerNode, QString const & dataSource )
00175 {
00176   QDomNode dataSourceNode = layerNode.namedItem( "datasource" );
00177   QDomElement dataSourceElement = dataSourceNode.toElement();
00178   QDomText dataSourceText = dataSourceElement.firstChild().toText();
00179 
00180   QgsDebugMsg( "datasource changed from " + dataSourceText.data() );
00181 
00182   dataSourceText.setData( dataSource );
00183 
00184   QgsDebugMsg( "to " + dataSourceText.data() );
00185 } // setDataSource
00186 
00187 
00188 
00189 
00190 bool QgsProjectBadLayerGuiHandler::findMissingFile( QString const & fileFilters, QDomNode & layerNode )
00191 {
00192   // Prepend that file name to the valid file format filter list since it
00193   // makes it easier for the user to not only find the original file, but to
00194   // perhaps find a similar file.
00195 
00196   QFileInfo originalDataSource( dataSource( layerNode ) );
00197 
00198   QString memoryQualifier;    // to differentiate between last raster and
00199   // vector directories
00200 
00201   switch ( dataType( layerNode ) )
00202   {
00203     case IS_VECTOR:
00204     {
00205       memoryQualifier = "lastVectorFileFilter";
00206 
00207       break;
00208     }
00209     case IS_RASTER:
00210     {
00211       memoryQualifier = "lastRasterFileFilter";
00212 
00213       break;
00214     }
00215     default:
00216       QgsDebugMsg( "unable to determine data type" );
00217       return false;
00218   }
00219 
00220   // Prepend the original data source base name to make it easier to pick it
00221   // out from a list of other files; however the appropriate filter strings
00222   // for the file type will also be added in case the file name itself has
00223   // changed, too.
00224 
00225   QString myFileFilters = originalDataSource.fileName() + ";;" + fileFilters;
00226 
00227   QStringList selectedFiles;
00228   QString enc;
00229   QString title = QObject::tr( "Where is '%1' (original location: %2)?" )
00230                   .arg( originalDataSource.fileName() )
00231                   .arg( originalDataSource.absoluteFilePath() );
00232 
00233   bool retVal = QgisGui::openFilesRememberingFilter( memoryQualifier,
00234                 myFileFilters,
00235                 selectedFiles,
00236                 enc,
00237                 title,
00238                 true );
00239 
00240   if ( selectedFiles.isEmpty() )
00241   {
00242     return retVal;
00243   }
00244   else
00245   {
00246     setDataSource( layerNode, selectedFiles.first() );
00247     if ( ! QgsProject::instance()->read( layerNode ) )
00248     {
00249       QgsDebugMsg( "unable to re-read layer" );
00250     }
00251   }
00252   return retVal;
00253 } // findMissingFile
00254 
00255 
00256 
00257 
00258 bool QgsProjectBadLayerGuiHandler::findLayer( QString const & fileFilters, QDomNode const & constLayerNode )
00259 {
00260   // XXX actually we could possibly get away with a copy of the node
00261   QDomNode & layerNode = const_cast<QDomNode&>( constLayerNode );
00262 
00263   bool retVal = false;
00264 
00265   switch ( providerType( layerNode ) )
00266   {
00267     case IS_FILE:
00268       QgsDebugMsg( "layer is file based" );
00269       retVal = findMissingFile( fileFilters, layerNode );
00270       break;
00271 
00272     case IS_DATABASE:
00273       QgsDebugMsg( "layer is database based" );
00274       break;
00275 
00276     case IS_URL:
00277       QgsDebugMsg( "layer is URL based" );
00278       break;
00279 
00280     case IS_Unknown:
00281       QgsDebugMsg( "layer has an unknown type" );
00282       break;
00283   }
00284   return retVal;
00285 } // findLayer
00286 
00287 
00288 
00289 
00290 void QgsProjectBadLayerGuiHandler::findLayers( QString const & fileFilters, QList<QDomNode> const & layerNodes )
00291 {
00292 
00293   for ( QList<QDomNode>::const_iterator i = layerNodes.begin();
00294         i != layerNodes.end();
00295         ++i )
00296   {
00297     if ( findLayer( fileFilters, *i ) )
00298     {
00299       // If findLayer returns true, the user hit Cancel All button
00300       break;
00301     }
00302   }
00303 
00304 } // findLayers
00305 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines