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