annotate widgets/InteractiveFileFinder.cpp @ 577:dc511d5fee75

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