annotate widgets/FileFinder.cpp @ 494:b3140e9e0665

* Some fairly simplistic code to set up layer type properties based on RDF data about feature types (both when running transforms and when importing features from RDF files).
author Chris Cannam
date Thu, 12 Feb 2009 15:26:43 +0000
parents 5f9a257598d8
children
rev   line source
Chris@378 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@378 2
Chris@378 3 /*
Chris@378 4 Sonic Visualiser
Chris@378 5 An audio file viewer and annotation editor.
Chris@378 6 Centre for Digital Music, Queen Mary, University of London.
Chris@378 7 This file copyright 2007 QMUL.
Chris@378 8
Chris@378 9 This program is free software; you can redistribute it and/or
Chris@378 10 modify it under the terms of the GNU General Public License as
Chris@378 11 published by the Free Software Foundation; either version 2 of the
Chris@378 12 License, or (at your option) any later version. See the file
Chris@378 13 COPYING included with this distribution for more information.
Chris@378 14 */
Chris@378 15
Chris@378 16 #include "FileFinder.h"
Chris@378 17 #include "data/fileio/FileSource.h"
Chris@378 18 #include "data/fileio/AudioFileReaderFactory.h"
Chris@378 19 #include "data/fileio/DataFileReaderFactory.h"
Chris@410 20 #include "rdf/RDFImporter.h"
Chris@456 21 #include "rdf/RDFExporter.h"
Chris@378 22
Chris@378 23 #include <QFileInfo>
Chris@378 24 #include <QMessageBox>
Chris@378 25 #include <QFileDialog>
Chris@378 26 #include <QInputDialog>
Chris@378 27 #include <QImageReader>
Chris@378 28 #include <QSettings>
Chris@378 29
Chris@378 30 #include <iostream>
Chris@378 31
Chris@378 32 FileFinder *
Chris@378 33 FileFinder::m_instance = 0;
Chris@378 34
Chris@378 35 FileFinder::FileFinder() :
Chris@378 36 m_lastLocatedLocation("")
Chris@378 37 {
Chris@378 38 }
Chris@378 39
Chris@378 40 FileFinder::~FileFinder()
Chris@378 41 {
Chris@378 42 }
Chris@378 43
Chris@378 44 FileFinder *
Chris@378 45 FileFinder::getInstance()
Chris@378 46 {
Chris@378 47 if (m_instance == 0) {
Chris@378 48 m_instance = new FileFinder();
Chris@378 49 }
Chris@378 50 return m_instance;
Chris@378 51 }
Chris@378 52
Chris@378 53 QString
Chris@378 54 FileFinder::getOpenFileName(FileType type, QString fallbackLocation)
Chris@378 55 {
Chris@378 56 QString settingsKey;
Chris@378 57 QString lastPath = fallbackLocation;
Chris@378 58
Chris@378 59 QString title = tr("Select file");
Chris@378 60 QString filter = tr("All files (*.*)");
Chris@378 61
Chris@378 62 switch (type) {
Chris@378 63
Chris@378 64 case SessionFile:
Chris@378 65 settingsKey = "sessionpath";
Chris@378 66 title = tr("Select a session file");
Chris@456 67 filter = tr("Sonic Visualiser session files (*.sv)\nRDF files (%1)\nAll files (*.*)").arg(RDFImporter::getKnownExtensions());
Chris@378 68 break;
Chris@378 69
Chris@378 70 case AudioFile:
Chris@378 71 settingsKey = "audiopath";
Chris@378 72 title = "Select an audio file";
Chris@378 73 filter = tr("Audio files (%1)\nAll files (*.*)")
Chris@378 74 .arg(AudioFileReaderFactory::getKnownExtensions());
Chris@378 75 break;
Chris@378 76
Chris@378 77 case LayerFile:
Chris@378 78 settingsKey = "layerpath";
Chris@456 79 filter = tr("All supported files (%1 %2)\nSonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nSpace-separated .lab files (*.lab)\nRDF files (%2)\nMIDI files (*.mid)\nText files (*.txt)\nAll files (*.*)")
Chris@456 80 .arg(DataFileReaderFactory::getKnownExtensions())
Chris@456 81 .arg(RDFImporter::getKnownExtensions());
Chris@378 82 break;
Chris@378 83
Chris@378 84 case LayerFileNoMidi:
Chris@378 85 settingsKey = "layerpath";
Chris@456 86 filter = tr("All supported files (%1 %2)\nSonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nSpace-separated .lab files (*.lab)\nRDF files (%2)\nText files (*.txt)\nAll files (*.*)")
Chris@456 87 .arg(DataFileReaderFactory::getKnownExtensions())
Chris@456 88 .arg(RDFImporter::getKnownExtensions());
Chris@378 89 break;
Chris@378 90
Chris@378 91 case SessionOrAudioFile:
Chris@378 92 settingsKey = "lastpath";
Chris@457 93 filter = tr("All supported files (*.sv %1 %2)\nSonic Visualiser session files (*.sv)\nAudio files (%2)\nRDF files (%1)\nAll files (*.*)")
Chris@456 94 .arg(RDFImporter::getKnownExtensions())
Chris@378 95 .arg(AudioFileReaderFactory::getKnownExtensions());
Chris@378 96 break;
Chris@378 97
Chris@378 98 case ImageFile:
Chris@378 99 settingsKey = "imagepath";
Chris@378 100 {
Chris@378 101 QStringList fmts;
Chris@378 102 QList<QByteArray> formats = QImageReader::supportedImageFormats();
Chris@378 103 for (QList<QByteArray>::iterator i = formats.begin();
Chris@378 104 i != formats.end(); ++i) {
Chris@378 105 fmts.push_back(QString("*.%1")
Chris@378 106 .arg(QString::fromLocal8Bit(*i).toLower()));
Chris@378 107 }
Chris@378 108 filter = tr("Image files (%1)\nAll files (*.*)").arg(fmts.join(" "));
Chris@378 109 }
Chris@378 110 break;
Chris@378 111
Chris@378 112 case AnyFile:
Chris@378 113 settingsKey = "lastpath";
Chris@456 114 filter = tr("All supported files (*.sv %1 %2 %3)\nSonic Visualiser session files (*.sv)\nAudio files (%1)\nLayer files (%2)\nRDF files (%3)\nAll files (*.*)")
Chris@378 115 .arg(AudioFileReaderFactory::getKnownExtensions())
Chris@410 116 .arg(DataFileReaderFactory::getKnownExtensions())
Chris@410 117 .arg(RDFImporter::getKnownExtensions());
Chris@378 118 break;
Chris@378 119 };
Chris@378 120
Chris@378 121 if (lastPath == "") {
Chris@378 122 char *home = getenv("HOME");
Chris@378 123 if (home) lastPath = home;
Chris@378 124 else lastPath = ".";
Chris@378 125 } else if (QFileInfo(lastPath).isDir()) {
Chris@378 126 lastPath = QFileInfo(lastPath).canonicalPath();
Chris@378 127 } else {
Chris@378 128 lastPath = QFileInfo(lastPath).absoluteDir().canonicalPath();
Chris@378 129 }
Chris@378 130
Chris@378 131 QSettings settings;
Chris@378 132 settings.beginGroup("FileFinder");
Chris@378 133 lastPath = settings.value(settingsKey, lastPath).toString();
Chris@378 134
Chris@378 135 QString path = "";
Chris@378 136
Chris@378 137 // Use our own QFileDialog just for symmetry with getSaveFileName below
Chris@378 138
Chris@378 139 QFileDialog dialog;
Chris@378 140 dialog.setFilters(filter.split('\n'));
Chris@378 141 dialog.setWindowTitle(title);
Chris@378 142 dialog.setDirectory(lastPath);
Chris@378 143
Chris@378 144 dialog.setAcceptMode(QFileDialog::AcceptOpen);
Chris@378 145 dialog.setFileMode(QFileDialog::ExistingFile);
Chris@378 146
Chris@378 147 if (dialog.exec()) {
Chris@378 148 QStringList files = dialog.selectedFiles();
Chris@378 149 if (!files.empty()) path = *files.begin();
Chris@378 150
Chris@378 151 QFileInfo fi(path);
Chris@378 152
Chris@378 153 if (!fi.exists()) {
Chris@378 154
Chris@378 155 QMessageBox::critical(0, tr("File does not exist"),
Chris@460 156 tr("<b>File not found</b><p>File \"%1\" does not exist").arg(path));
Chris@378 157 path = "";
Chris@378 158
Chris@378 159 } else if (!fi.isReadable()) {
Chris@378 160
Chris@378 161 QMessageBox::critical(0, tr("File is not readable"),
Chris@460 162 tr("<b>File is not readable</b><p>File \"%1\" can not be read").arg(path));
Chris@378 163 path = "";
Chris@378 164
Chris@378 165 } else if (fi.isDir()) {
Chris@378 166
Chris@378 167 QMessageBox::critical(0, tr("Directory selected"),
Chris@460 168 tr("<b>Directory selected</b><p>File \"%1\" is a directory").arg(path));
Chris@378 169 path = "";
Chris@378 170
Chris@378 171 } else if (!fi.isFile()) {
Chris@378 172
Chris@378 173 QMessageBox::critical(0, tr("Non-file selected"),
Chris@460 174 tr("<b>Not a file</b><p>Path \"%1\" is not a file").arg(path));
Chris@378 175 path = "";
Chris@378 176
Chris@378 177 } else if (fi.size() == 0) {
Chris@378 178
Chris@378 179 QMessageBox::critical(0, tr("File is empty"),
Chris@460 180 tr("<b>File is empty</b><p>File \"%1\" is empty").arg(path));
Chris@378 181 path = "";
Chris@378 182 }
Chris@378 183 }
Chris@378 184
Chris@378 185 if (path != "") {
Chris@378 186 settings.setValue(settingsKey,
Chris@378 187 QFileInfo(path).absoluteDir().canonicalPath());
Chris@378 188 }
Chris@378 189
Chris@378 190 return path;
Chris@378 191 }
Chris@378 192
Chris@378 193 QString
Chris@378 194 FileFinder::getSaveFileName(FileType type, QString fallbackLocation)
Chris@378 195 {
Chris@378 196 QString settingsKey;
Chris@378 197 QString lastPath = fallbackLocation;
Chris@378 198
Chris@378 199 QString title = tr("Select file");
Chris@378 200 QString filter = tr("All files (*.*)");
Chris@378 201
Chris@378 202 switch (type) {
Chris@378 203
Chris@378 204 case SessionFile:
Chris@378 205 settingsKey = "savesessionpath";
Chris@378 206 title = tr("Select a session file");
Chris@378 207 filter = tr("Sonic Visualiser session files (*.sv)\nAll files (*.*)");
Chris@378 208 break;
Chris@378 209
Chris@378 210 case AudioFile:
Chris@378 211 settingsKey = "saveaudiopath";
Chris@378 212 title = "Select an audio file";
Chris@378 213 title = tr("Select a file to export to");
Chris@378 214 filter = tr("WAV audio files (*.wav)\nAll files (*.*)");
Chris@378 215 break;
Chris@378 216
Chris@378 217 case LayerFile:
Chris@378 218 settingsKey = "savelayerpath";
Chris@378 219 title = tr("Select a file to export to");
Chris@456 220 filter = tr("Sonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nRDF/Turtle files (%1)\nMIDI files (*.mid)\nText files (*.txt)\nAll files (*.*)").arg(RDFExporter::getSupportedExtensions());
Chris@378 221 break;
Chris@378 222
Chris@378 223 case LayerFileNoMidi:
Chris@378 224 settingsKey = "savelayerpath";
Chris@378 225 title = tr("Select a file to export to");
Chris@456 226 filter = tr("Sonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nRDF/Turtle files (%1)\nText files (*.txt)\nAll files (*.*)").arg(RDFExporter::getSupportedExtensions());
Chris@378 227 break;
Chris@378 228
Chris@378 229 case SessionOrAudioFile:
Chris@378 230 std::cerr << "ERROR: Internal error: FileFinder::getSaveFileName: SessionOrAudioFile cannot be used here" << std::endl;
Chris@378 231 abort();
Chris@378 232
Chris@378 233 case ImageFile:
Chris@378 234 settingsKey = "saveimagepath";
Chris@378 235 title = tr("Select a file to export to");
Chris@378 236 filter = tr("Portable Network Graphics files (*.png)\nAll files (*.*)");
Chris@378 237 break;
Chris@378 238
Chris@378 239 case AnyFile:
Chris@378 240 std::cerr << "ERROR: Internal error: FileFinder::getSaveFileName: AnyFile cannot be used here" << std::endl;
Chris@378 241 abort();
Chris@378 242 };
Chris@378 243
Chris@378 244 if (lastPath == "") {
Chris@378 245 char *home = getenv("HOME");
Chris@378 246 if (home) lastPath = home;
Chris@378 247 else lastPath = ".";
Chris@378 248 } else if (QFileInfo(lastPath).isDir()) {
Chris@378 249 lastPath = QFileInfo(lastPath).canonicalPath();
Chris@378 250 } else {
Chris@378 251 lastPath = QFileInfo(lastPath).absoluteDir().canonicalPath();
Chris@378 252 }
Chris@378 253
Chris@378 254 QSettings settings;
Chris@378 255 settings.beginGroup("FileFinder");
Chris@378 256 lastPath = settings.value(settingsKey, lastPath).toString();
Chris@378 257
Chris@378 258 QString path = "";
Chris@378 259
Chris@378 260 // Use our own QFileDialog instead of static functions, as we may
Chris@378 261 // need to adjust the file extension based on the selected filter
Chris@378 262
Chris@378 263 QFileDialog dialog;
Chris@378 264 dialog.setFilters(filter.split('\n'));
Chris@378 265 dialog.setWindowTitle(title);
Chris@378 266 dialog.setDirectory(lastPath);
Chris@378 267
Chris@378 268 dialog.setAcceptMode(QFileDialog::AcceptSave);
Chris@378 269 dialog.setFileMode(QFileDialog::AnyFile);
Chris@378 270 dialog.setConfirmOverwrite(false); // we'll do that
Chris@378 271
Chris@378 272 if (type == SessionFile) {
Chris@378 273 dialog.setDefaultSuffix("sv");
Chris@378 274 } else if (type == AudioFile) {
Chris@378 275 dialog.setDefaultSuffix("wav");
Chris@378 276 } else if (type == ImageFile) {
Chris@378 277 dialog.setDefaultSuffix("png");
Chris@378 278 }
Chris@378 279
Chris@378 280 bool good = false;
Chris@378 281
Chris@378 282 while (!good) {
Chris@378 283
Chris@378 284 path = "";
Chris@378 285
Chris@378 286 if (!dialog.exec()) break;
Chris@378 287
Chris@378 288 QStringList files = dialog.selectedFiles();
Chris@378 289 if (files.empty()) break;
Chris@378 290 path = *files.begin();
Chris@378 291
Chris@378 292 QFileInfo fi(path);
Chris@378 293
Chris@378 294 std::cerr << "type = " << type << ", suffix = " << fi.suffix().toStdString() << std::endl;
Chris@378 295
Chris@378 296 if ((type == LayerFile || type == LayerFileNoMidi)
Chris@378 297 && fi.suffix() == "") {
Chris@378 298 QString expectedExtension;
Chris@378 299 QString selectedFilter = dialog.selectedFilter();
Chris@378 300 if (selectedFilter.contains(".svl")) {
Chris@378 301 expectedExtension = "svl";
Chris@378 302 } else if (selectedFilter.contains(".txt")) {
Chris@378 303 expectedExtension = "txt";
Chris@378 304 } else if (selectedFilter.contains(".csv")) {
Chris@378 305 expectedExtension = "csv";
Chris@378 306 } else if (selectedFilter.contains(".mid")) {
Chris@378 307 expectedExtension = "mid";
Chris@456 308 } else if (selectedFilter.contains(".ttl")) {
Chris@456 309 expectedExtension = "ttl";
Chris@378 310 }
Chris@378 311 std::cerr << "expected extension = " << expectedExtension.toStdString() << std::endl;
Chris@378 312 if (expectedExtension != "") {
Chris@378 313 path = QString("%1.%2").arg(path).arg(expectedExtension);
Chris@378 314 fi = QFileInfo(path);
Chris@378 315 }
Chris@378 316 }
Chris@378 317
Chris@378 318 if (fi.isDir()) {
Chris@378 319 QMessageBox::critical(0, tr("Directory selected"),
Chris@460 320 tr("<b>Directory selected</b><p>File \"%1\" is a directory").arg(path));
Chris@378 321 continue;
Chris@378 322 }
Chris@378 323
Chris@378 324 if (fi.exists()) {
Chris@378 325 if (QMessageBox::question(0, tr("File exists"),
Chris@460 326 tr("<b>File exists</b><p>The file \"%1\" already exists.\nDo you want to overwrite it?").arg(path),
Chris@378 327 QMessageBox::Ok,
Chris@378 328 QMessageBox::Cancel) != QMessageBox::Ok) {
Chris@378 329 continue;
Chris@378 330 }
Chris@378 331 }
Chris@378 332
Chris@378 333 good = true;
Chris@378 334 }
Chris@378 335
Chris@378 336 if (path != "") {
Chris@378 337 settings.setValue(settingsKey,
Chris@378 338 QFileInfo(path).absoluteDir().canonicalPath());
Chris@378 339 }
Chris@378 340
Chris@378 341 return path;
Chris@378 342 }
Chris@378 343
Chris@378 344 void
Chris@378 345 FileFinder::registerLastOpenedFilePath(FileType type, QString path)
Chris@378 346 {
Chris@378 347 QString settingsKey;
Chris@378 348
Chris@378 349 switch (type) {
Chris@378 350 case SessionFile:
Chris@378 351 settingsKey = "sessionpath";
Chris@378 352 break;
Chris@378 353
Chris@378 354 case AudioFile:
Chris@378 355 settingsKey = "audiopath";
Chris@378 356 break;
Chris@378 357
Chris@378 358 case LayerFile:
Chris@378 359 settingsKey = "layerpath";
Chris@378 360 break;
Chris@378 361
Chris@378 362 case LayerFileNoMidi:
Chris@378 363 settingsKey = "layerpath";
Chris@378 364 break;
Chris@378 365
Chris@378 366 case SessionOrAudioFile:
Chris@378 367 settingsKey = "lastpath";
Chris@378 368 break;
Chris@378 369
Chris@378 370 case ImageFile:
Chris@378 371 settingsKey = "imagepath";
Chris@378 372 break;
Chris@378 373
Chris@378 374 case AnyFile:
Chris@378 375 settingsKey = "lastpath";
Chris@378 376 break;
Chris@378 377 }
Chris@378 378
Chris@378 379 if (path != "") {
Chris@378 380 QSettings settings;
Chris@378 381 settings.beginGroup("FileFinder");
Chris@378 382 path = QFileInfo(path).absoluteDir().canonicalPath();
Chris@378 383 settings.setValue(settingsKey, path);
Chris@378 384 settings.setValue("lastpath", path);
Chris@378 385 }
Chris@378 386 }
Chris@378 387
Chris@378 388 QString
Chris@378 389 FileFinder::find(FileType type, QString location, QString lastKnownLocation)
Chris@378 390 {
Chris@378 391 if (FileSource::canHandleScheme(location)) {
Chris@378 392 if (FileSource(location).isAvailable()) {
Chris@378 393 std::cerr << "FileFinder::find: ok, it's available... returning" << std::endl;
Chris@378 394 return location;
Chris@378 395 }
Chris@378 396 }
Chris@378 397
Chris@378 398 if (QFileInfo(location).exists()) return location;
Chris@378 399
Chris@378 400 QString foundAt = "";
Chris@378 401
Chris@378 402 if ((foundAt = findRelative(location, lastKnownLocation)) != "") {
Chris@378 403 return foundAt;
Chris@378 404 }
Chris@378 405
Chris@378 406 if ((foundAt = findRelative(location, m_lastLocatedLocation)) != "") {
Chris@378 407 return foundAt;
Chris@378 408 }
Chris@378 409
Chris@378 410 return locateInteractive(type, location);
Chris@378 411 }
Chris@378 412
Chris@378 413 QString
Chris@378 414 FileFinder::findRelative(QString location, QString relativeTo)
Chris@378 415 {
Chris@378 416 if (relativeTo == "") return "";
Chris@378 417
Chris@378 418 std::cerr << "Looking for \"" << location.toStdString() << "\" next to \""
Chris@378 419 << relativeTo.toStdString() << "\"..." << std::endl;
Chris@378 420
Chris@378 421 QString fileName;
Chris@378 422 QString resolved;
Chris@378 423
Chris@378 424 if (FileSource::isRemote(location)) {
Chris@378 425 fileName = QUrl(location).path().section('/', -1, -1,
Chris@378 426 QString::SectionSkipEmpty);
Chris@378 427 } else {
Chris@378 428 if (QUrl(location).scheme() == "file") {
Chris@378 429 location = QUrl(location).toLocalFile();
Chris@378 430 }
Chris@378 431 fileName = QFileInfo(location).fileName();
Chris@378 432 }
Chris@378 433
Chris@378 434 if (FileSource::isRemote(relativeTo)) {
Chris@378 435 resolved = QUrl(relativeTo).resolved(fileName).toString();
Chris@378 436 if (!FileSource(resolved).isAvailable()) resolved = "";
Chris@378 437 std::cerr << "resolved: " << resolved.toStdString() << std::endl;
Chris@378 438 } else {
Chris@378 439 if (QUrl(relativeTo).scheme() == "file") {
Chris@378 440 relativeTo = QUrl(relativeTo).toLocalFile();
Chris@378 441 }
Chris@378 442 resolved = QFileInfo(relativeTo).dir().filePath(fileName);
Chris@378 443 if (!QFileInfo(resolved).exists() ||
Chris@378 444 !QFileInfo(resolved).isFile() ||
Chris@378 445 !QFileInfo(resolved).isReadable()) {
Chris@378 446 resolved = "";
Chris@378 447 }
Chris@378 448 }
Chris@378 449
Chris@378 450 return resolved;
Chris@378 451 }
Chris@378 452
Chris@378 453 QString
Chris@378 454 FileFinder::locateInteractive(FileType type, QString thing)
Chris@378 455 {
Chris@378 456 QString question;
Chris@378 457 if (type == AudioFile) {
Chris@460 458 question = tr("<b>File not found</b><p>Audio file \"%1\" could not be opened.\nDo you want to locate it?");
Chris@378 459 } else {
Chris@460 460 question = tr("<b>File not found</b><p>File \"%1\" could not be opened.\nDo you want to locate it?");
Chris@378 461 }
Chris@378 462
Chris@378 463 QString path = "";
Chris@378 464 bool done = false;
Chris@378 465
Chris@378 466 while (!done) {
Chris@378 467
Chris@378 468 int rv = QMessageBox::question
Chris@378 469 (0,
Chris@378 470 tr("Failed to open file"),
Chris@378 471 question.arg(thing),
Chris@378 472 tr("Locate file..."),
Chris@378 473 tr("Use URL..."),
Chris@378 474 tr("Cancel"),
Chris@378 475 0, 2);
Chris@378 476
Chris@378 477 switch (rv) {
Chris@378 478
Chris@378 479 case 0: // Locate file
Chris@378 480
Chris@378 481 if (QFileInfo(thing).dir().exists()) {
Chris@378 482 path = QFileInfo(thing).dir().canonicalPath();
Chris@378 483 }
Chris@378 484
Chris@378 485 path = getOpenFileName(type, path);
Chris@378 486 done = (path != "");
Chris@378 487 break;
Chris@378 488
Chris@378 489 case 1: // Use URL
Chris@378 490 {
Chris@378 491 bool ok = false;
Chris@378 492 path = QInputDialog::getText
Chris@378 493 (0, tr("Use URL"),
Chris@378 494 tr("Please enter the URL to use for this file:"),
Chris@378 495 QLineEdit::Normal, "", &ok);
Chris@378 496
Chris@378 497 if (ok && path != "") {
Chris@378 498 if (FileSource(path).isAvailable()) {
Chris@378 499 done = true;
Chris@378 500 } else {
Chris@378 501 QMessageBox::critical
Chris@378 502 (0, tr("Failed to open location"),
Chris@460 503 tr("<b>Failed to open location</b><p>URL \"%1\" could not be opened").arg(path));
Chris@378 504 path = "";
Chris@378 505 }
Chris@378 506 }
Chris@378 507 break;
Chris@378 508 }
Chris@378 509
Chris@378 510 case 2: // Cancel
Chris@378 511 path = "";
Chris@378 512 done = true;
Chris@378 513 break;
Chris@378 514 }
Chris@378 515 }
Chris@378 516
Chris@378 517 if (path != "") m_lastLocatedLocation = path;
Chris@378 518 return path;
Chris@378 519 }
Chris@378 520
Chris@378 521