From 9f6a0300a5ab5996a97b27bc8ada55f41e3ca517 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 28 Apr 2014 16:16:34 +0200 Subject: [PATCH] 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 --- src/widgets/dialogs/qfiledialog.cpp | 60 ++++++++++++------- .../dialogs/qfiledialog/tst_qfiledialog.cpp | 37 ++++++++++++ 2 files changed, 77 insertions(+), 20 deletions(-) diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp index ecbd9480ff..bb8cdec896 100644 --- a/src/widgets/dialogs/qfiledialog.cpp +++ b/src/widgets/dialogs/qfiledialog.cpp @@ -71,6 +71,7 @@ extern bool qt_priv_ptr_valid; #endif #if defined(Q_OS_UNIX) #include +#include // for pathconf() on OS X #elif defined(Q_OS_WIN) # include #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)); } /*! diff --git a/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp b/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp index e856c2efc5..047df0d3f2 100644 --- a/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp +++ b/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp @@ -43,6 +43,7 @@ #include #include +#include #include #include #include @@ -72,6 +73,7 @@ #include #if defined(Q_OS_UNIX) +#include // 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("fileNameEdit"); + QVERIFY(lineEdit); + QCOMPARE(lineEdit->text().compare(fileName, Qt::CaseInsensitive), 0); +} + void tst_QFiledialog::selectFiles() { QTemporaryDir tempDir;