Add tilde (both ~ and ~<user>) expansion to QFileDialog on UNIX.

Task-number: QTBUG-3265

Change-Id: Id8062afe69e798e1f9cf3f4e967ae0d30c362b72
Reviewed-on: http://codereview.qt.nokia.com/411
Reviewed-by: Pierre Rossi <pierre.rossi@gmail.com>
bb10
Pierre Rossi 2011-05-20 14:16:33 +02:00 committed by Qt Continuous Integration System
parent 15d6f1f031
commit 94c173de05
2 changed files with 112 additions and 4 deletions

View File

@ -66,6 +66,9 @@
#if defined(Q_OS_WINCE)
extern bool qt_priv_ptr_valid;
#endif
#if defined(Q_OS_UNIX)
#include <pwd.h>
#endif
#endif
QT_BEGIN_NAMESPACE
@ -858,23 +861,78 @@ void QFileDialog::selectFile(const QString &filename)
d->lineEdit()->setText(file);
}
#ifdef Q_OS_UNIX
Q_AUTOTEST_EXPORT QString qt_tildeExpansion(const QString &path, bool *expanded = 0)
{
if (expanded != 0)
*expanded = false;
if (!path.startsWith(QLatin1Char('~')))
return path;
QString ret = path;
QStringList tokens = ret.split(QDir::separator());
if (tokens.first() == QLatin1String("~")) {
ret.replace(0, 1, QDir::homeDirPath());
} else {
QString userName = tokens.first();
userName.remove(0, 1);
#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
passwd pw;
passwd *tmpPw;
char buf[200];
const int bufSize = sizeof(buf);
int err = getpwnam_r(userName.toLocal8Bit().constData(), &pw, buf, bufSize, &tmpPw);
if (err || !tmpPw)
return ret;
const QString homePath = QString::fromLocal8Bit(pw.pw_dir);
#else
passwd *pw = getpwnam(userName.toLocal8Bit().constData());
if (!pw)
return ret;
const QString homePath = QString::fromLocal8Bit(pw->pw_dir);
#endif
ret.replace(0, tokens.first().length(), homePath);
}
if (expanded != 0)
*expanded = true;
return ret;
}
#endif
/**
Returns the text in the line edit which can be one or more file names
*/
QStringList QFileDialogPrivate::typedFiles() const
{
Q_Q(const QFileDialog);
QStringList files;
QString editText = lineEdit()->text();
if (!editText.contains(QLatin1Char('"')))
if (!editText.contains(QLatin1Char('"'))) {
#ifdef Q_OS_UNIX
const QString prefix = q->directory().absolutePath() + QDir::separator();
if (QFile::exists(prefix + editText))
files << editText;
else
files << qt_tildeExpansion(editText);
#else
files << editText;
else {
#endif
} else {
// " is used to separate files like so: "file1" "file2" "file3" ...
// ### need escape character for filenames with quotes (")
QStringList tokens = editText.split(QLatin1Char('\"'));
for (int i=0; i<tokens.size(); ++i) {
if ((i % 2) == 0)
continue; // Every even token is a separator
#ifdef Q_OS_UNIX
const QString token = tokens.at(i);
const QString prefix = q->directory().absolutePath() + QDir::separator();
if (QFile::exists(prefix + token))
files << token;
else
files << qt_tildeExpansion(token);
#else
files << toInternal(tokens.at(i));
#endif
}
}
return addDefaultSuffixToFiles(files);
@ -3338,6 +3396,17 @@ QStringList QFSCompleter::splitPath(const QString &path) const
pathCopy = pathCopy.mid(2);
else
doubleSlash.clear();
#elif defined(Q_OS_UNIX)
bool expanded;
pathCopy = qt_tildeExpansion(pathCopy, &expanded);
if (expanded) {
QFileSystemModel *dirModel;
if (proxyModel)
dirModel = qobject_cast<QFileSystemModel *>(proxyModel->sourceModel());
else
dirModel = sourceModel;
dirModel->fetchMore(dirModel->index(pathCopy));
}
#endif
QRegExp re(QLatin1Char('[') + QRegExp::escape(sep) + QLatin1Char(']'));
@ -3354,14 +3423,14 @@ QStringList QFSCompleter::splitPath(const QString &path) const
parts.append(QString());
#else
QStringList parts = pathCopy.split(re);
if (path[0] == sep[0]) // read the "/" at the beginning as the split removed it
if (pathCopy[0] == sep[0]) // read the "/" at the beginning as the split removed it
parts[0] = sep[0];
#endif
#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
bool startsFromRoot = !parts.isEmpty() && parts[0].endsWith(QLatin1Char(':'));
#else
bool startsFromRoot = path[0] == sep[0];
bool startsFromRoot = pathCopy[0] == sep[0];
#endif
if (parts.count() == 1 || (parts.count() > 1 && !startsFromRoot)) {
const QFileSystemModel *dirModel;

View File

@ -78,6 +78,10 @@
# define STRINGIFY(x) #x
# define TOSTRING(x) STRINGIFY(x)
# define SRCDIR "C:/Private/" TOSTRING(SYMBIAN_SRCDIR_UID) "/"
#elif defined(Q_OS_UNIX)
#ifdef QT_BUILD_INTERNAL
extern Q_GUI_EXPORT QString qt_tildeExpansion(const QString &path, bool *expanded = 0);
#endif
#endif
class QNonNativeFileDialog : public QFileDialog
@ -144,6 +148,10 @@ private slots:
void clearLineEdit();
void enableChooseButton();
void hooks();
#ifdef Q_OS_UNIX
void tildeExpansion_data();
void tildeExpansion();
#endif
private:
QByteArray userSettings;
@ -1323,5 +1331,36 @@ void tst_QFiledialog::hooks()
QCOMPARE(QFileDialog::getSaveFileName(), QString("saveName"));
}
#ifdef Q_OS_UNIX
void tst_QFiledialog::tildeExpansion_data()
{
QTest::addColumn<QString>("tildePath");
QTest::addColumn<QString>("expandedPath");
QTest::newRow("empty path") << QString() << QString();
QTest::newRow("~") << QString::fromLatin1("~") << QDir::homePath();
QTest::newRow("~/some/sub/dir/") << QString::fromLatin1("~/some/sub/dir") << QDir::homePath()
+ QString::fromLatin1("/some/sub/dir");
QString userHome = QString(qgetenv("USER"));
userHome.prepend('~');
QTest::newRow("current user (~<user> syntax)") << userHome << QDir::homePath();
QString invalid = QString::fromLatin1("~thisIsNotAValidUserName");
QTest::newRow("invalid user name") << invalid << invalid;
}
void tst_QFiledialog::tildeExpansion()
{
#ifndef QT_BUILD_INTERNAL
QSKIP("Test case relies on developer build (AUTOTEST_EXPORT)", SkipAll);
#else
QFETCH(QString, tildePath);
QFETCH(QString, expandedPath);
QCOMPARE(qt_tildeExpansion(tildePath), expandedPath);
#endif
}
#endif
QTEST_MAIN(tst_QFiledialog)
#include "tst_qfiledialog.moc"