Observe case insensitive file systems in QFileDialog::selectFile().

When stripping the root path from a file name that cannot be found
in the model, use case sensitive comparison depending on
file system.

Task-number: QTBUG-38162
Change-Id: I28e28973fca2da35a5768fdd00cc258b9669a15a
Reviewed-by: Shawn Rutledge <shawn.rutledge@digia.com>
This commit is contained in:
Friedemann Kleint 2014-04-28 16:16:34 +02:00 committed by The Qt Project
parent 386292837b
commit 9f6a0300a5
2 changed files with 77 additions and 20 deletions

View File

@ -71,6 +71,7 @@ extern bool qt_priv_ptr_valid;
#endif
#if defined(Q_OS_UNIX)
#include <pwd.h>
#include <unistd.h> // for pathconf() on OS X
#elif defined(Q_OS_WIN)
# include <QtCore/qt_windows.h>
#endif
@ -1018,6 +1019,44 @@ QUrl QFileDialog::directoryUrl() const
return QUrl::fromLocalFile(directory().absolutePath());
}
// FIXME Qt 5.4: Use upcoming QVolumeInfo class to determine this information?
static inline bool isCaseSensitiveFileSystem(const QString &path)
{
Q_UNUSED(path)
#if defined(Q_OS_WIN)
// Return case insensitive unconditionally, even if someone has a case sensitive
// file system mounted, wrongly capitalized drive letters will cause mismatches.
return false;
#elif defined(Q_OS_OSX)
return pathconf(QFile::encodeName(path).constData(), _PC_CASE_SENSITIVE);
#else
return true;
#endif
}
// Determine the file name to be set on the line edit from the path
// passed to selectFile() in mode QFileDialog::AcceptSave.
static inline QString fileFromPath(const QString &rootPath, QString path)
{
if (!QFileInfo(path).isAbsolute())
return path;
if (path.startsWith(rootPath, isCaseSensitiveFileSystem(rootPath) ? Qt::CaseSensitive : Qt::CaseInsensitive))
path.remove(0, rootPath.size());
if (path.isEmpty())
return path;
if (path.at(0) == QDir::separator()
#ifdef Q_OS_WIN
//On Windows both cases can happen
|| path.at(0) == QLatin1Char('/')
#endif
) {
path.remove(0, 1);
}
return path;
}
/*!
Selects the given \a filename in the file dialog.
@ -1049,28 +1088,9 @@ void QFileDialog::selectFile(const QString &filename)
}
QModelIndex index = d->model->index(filename);
QString file;
if (!index.isValid()) {
// save as dialog where we want to input a default value
QString text = filename;
if (QFileInfo(filename).isAbsolute()) {
QString current = d->rootPath();
text.remove(current);
if (text.at(0) == QDir::separator()
#ifdef Q_OS_WIN
//On Windows both cases can happen
|| text.at(0) == QLatin1Char('/')
#endif
)
text = text.remove(0,1);
}
file = text;
} else {
file = index.data().toString();
}
d->qFileDialogUi->listView->selectionModel()->clear();
if (!isVisible() || !d->lineEdit()->hasFocus())
d->lineEdit()->setText(file);
d->lineEdit()->setText(index.isValid() ? index.data().toString() : fileFromPath(d->rootPath(), filename));
}
/*!

View File

@ -43,6 +43,7 @@
#include <QtTest/QtTest>
#include <qcoreapplication.h>
#include <qfile.h>
#include <qdebug.h>
#include <qsharedpointer.h>
#include <qfiledialog.h>
@ -72,6 +73,7 @@
#include <QFileSystemModel>
#if defined(Q_OS_UNIX)
#include <unistd.h> // for pathconf() on OS X
#ifdef QT_BUILD_INTERNAL
QT_BEGIN_NAMESPACE
extern Q_GUI_EXPORT QString qt_tildeExpansion(const QString &path, bool *expanded = 0);
@ -79,6 +81,19 @@ QT_END_NAMESPACE
#endif
#endif
static inline bool isCaseSensitiveFileSystem(const QString &path)
{
Q_UNUSED(path)
#if defined(Q_OS_MAC)
return pathconf(QFile::encodeName(path).constData(), _PC_CASE_SENSITIVE);
#elif defined(Q_OS_WIN)
return false;
#else
return true;
#endif
}
class QNonNativeFileDialog : public QFileDialog
{
Q_OBJECT
@ -130,6 +145,7 @@ private slots:
void selectFile_data();
void selectFile();
void selectFiles();
void selectFileWrongCaseSaveAs();
void selectFilter();
void viewMode();
void proxymodel();
@ -882,6 +898,27 @@ void tst_QFiledialog::selectFile()
fd.reset(); // Ensure the file dialog let's go of the temporary file for "temp".
}
void tst_QFiledialog::selectFileWrongCaseSaveAs()
{
const QString home = QDir::homePath();
if (isCaseSensitiveFileSystem(home))
QSKIP("This test is intended for case-insensitive file systems only.");
// QTBUG-38162: when passing a wrongly capitalized path to selectFile()
// on a case-insensitive file system, the line edit should only
// contain the file name ("c:\PRogram files\foo.txt" -> "foo.txt").
const QString fileName = QStringLiteral("foo.txt");
const QString path = home + QLatin1Char('/') + fileName;
QString wrongCasePath = path;
for (int c = 0; c < wrongCasePath.size(); c += 2)
wrongCasePath[c] = wrongCasePath.at(c).isLower() ? wrongCasePath.at(c).toUpper() : wrongCasePath.at(c).toLower();
QNonNativeFileDialog fd(0, "QTBUG-38162", wrongCasePath);
fd.setAcceptMode(QFileDialog::AcceptSave);
fd.selectFile(wrongCasePath);
const QLineEdit *lineEdit = fd.findChild<QLineEdit*>("fileNameEdit");
QVERIFY(lineEdit);
QCOMPARE(lineEdit->text().compare(fileName, Qt::CaseInsensitive), 0);
}
void tst_QFiledialog::selectFiles()
{
QTemporaryDir tempDir;