From 5382bf4220bb76055943678196a84387510ee059 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Mon, 10 Aug 2020 16:03:17 +0200 Subject: QFileDialog: reduce number of times a file is stat'ed When opening a QFileDialog with an initial directory that lives on a disconnected network drive, repeatedly testing that directory consumes a significant amount of time during which the UI is blocked. To reduce the amount of file accesses, refactor the initialization code to allow sharing of a QFileInfo for the default case of operating on a local and absolute file system. This reduces the amount of stat calls significantly during startup time, and in case of a disconnected network shaves of 10-15 seconds of blocked UI, if Windows has already noticed that the file system is disconnected. Pick-to: 5.15 Fixes: QTBUG-6039 Change-Id: Ie082e447db214033291455bef2087cd05f366806 Reviewed-by: Richard Moe Gustavsen --- src/widgets/dialogs/qfiledialog.cpp | 128 +++++++++++++++++------------------- src/widgets/dialogs/qfiledialog_p.h | 13 ++-- 2 files changed, 65 insertions(+), 76 deletions(-) diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp index 587db6343f..e81fc66a03 100644 --- a/src/widgets/dialogs/qfiledialog.cpp +++ b/src/widgets/dialogs/qfiledialog.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWidgets module of the Qt Toolkit. @@ -360,7 +360,8 @@ QFileDialog::QFileDialog(QWidget *parent, Qt::WindowFlags f) : QDialog(*new QFileDialogPrivate, parent, f) { Q_D(QFileDialog); - d->init(); + QFileDialogArgs args; + d->init(args); } /*! @@ -377,7 +378,10 @@ QFileDialog::QFileDialog(QWidget *parent, : QDialog(*new QFileDialogPrivate, parent, { }) { Q_D(QFileDialog); - d->init(QUrl::fromLocalFile(directory), filter, caption); + QFileDialogArgs args(QUrl::fromLocalFile(directory)); + args.filter = filter; + args.caption = caption; + d->init(args); } /*! @@ -387,7 +391,7 @@ QFileDialog::QFileDialog(const QFileDialogArgs &args) : QDialog(*new QFileDialogPrivate, args.parent, { }) { Q_D(QFileDialog); - d->init(args.directory, args.filter, args.caption); + d->init(args); setFileMode(args.mode); setOptions(args.options); selectFile(args.selection); @@ -2143,11 +2147,9 @@ QUrl QFileDialog::getOpenFileUrl(QWidget *parent, Options options, const QStringList &supportedSchemes) { - QFileDialogArgs args; + QFileDialogArgs args(dir); args.parent = parent; args.caption = caption; - args.directory = QFileDialogPrivate::workingDirectory(dir); - args.selection = QFileDialogPrivate::initialSelection(dir); args.filter = filter; args.mode = ExistingFile; args.options = options; @@ -2263,11 +2265,9 @@ QList QFileDialog::getOpenFileUrls(QWidget *parent, Options options, const QStringList &supportedSchemes) { - QFileDialogArgs args; + QFileDialogArgs args(dir); args.parent = parent; args.caption = caption; - args.directory = QFileDialogPrivate::workingDirectory(dir); - args.selection = QFileDialogPrivate::initialSelection(dir); args.filter = filter; args.mode = ExistingFiles; args.options = options; @@ -2509,11 +2509,9 @@ QUrl QFileDialog::getSaveFileUrl(QWidget *parent, Options options, const QStringList &supportedSchemes) { - QFileDialogArgs args; + QFileDialogArgs args(dir); args.parent = parent; args.caption = caption; - args.directory = QFileDialogPrivate::workingDirectory(dir); - args.selection = QFileDialogPrivate::initialSelection(dir); args.filter = filter; args.mode = AnyFile; args.options = options; @@ -2619,10 +2617,9 @@ QUrl QFileDialog::getExistingDirectoryUrl(QWidget *parent, Options options, const QStringList &supportedSchemes) { - QFileDialogArgs args; + QFileDialogArgs args(dir); args.parent = parent; args.caption = caption; - args.directory = QFileDialogPrivate::workingDirectory(dir); args.mode = Directory; args.options = options; @@ -2633,58 +2630,54 @@ QUrl QFileDialog::getExistingDirectoryUrl(QWidget *parent, return QUrl(); } -inline static QUrl _qt_get_directory(const QUrl &url) +inline static QUrl _qt_get_directory(const QUrl &url, const QFileInfo &local) { if (url.isLocalFile()) { - QFileInfo info = QFileInfo(QDir::current(), url.toLocalFile()); + QFileInfo info = local; + if (!local.isAbsolute()) + info = QFileInfo(QDir::current(), url.toLocalFile()); + const QFileInfo pathInfo(info.absolutePath()); + if (!pathInfo.exists() || !pathInfo.isDir()) + return QUrl(); if (info.exists() && info.isDir()) return QUrl::fromLocalFile(QDir::cleanPath(info.absoluteFilePath())); - info.setFile(info.absolutePath()); - if (info.exists() && info.isDir()) - return QUrl::fromLocalFile(info.absoluteFilePath()); - return QUrl(); + return QUrl::fromLocalFile(pathInfo.absoluteFilePath()); } else { return url; } } -/* - Get the initial directory URL - - \sa initialSelection() - */ -QUrl QFileDialogPrivate::workingDirectory(const QUrl &url) -{ - if (!url.isEmpty()) { - QUrl directory = _qt_get_directory(url); - if (!directory.isEmpty()) - return directory; - } - QUrl directory = _qt_get_directory(*lastVisitedDir()); - if (!directory.isEmpty()) - return directory; - return QUrl::fromLocalFile(QDir::currentPath()); -} /* - Get the initial selection given a path. The initial directory - can contain both the initial directory and initial selection - /home/user/foo.txt - - \sa workingDirectory() - */ -QString QFileDialogPrivate::initialSelection(const QUrl &url) -{ - if (url.isEmpty()) - return QString(); - if (url.isLocalFile()) { - QFileInfo info(url.toLocalFile()); - if (!info.isDir()) - return info.fileName(); - else - return QString(); + Initialize working directory and selection from \a url. +*/ +QFileDialogArgs::QFileDialogArgs(const QUrl &url) +{ + // default case, re-use QFileInfo to avoid stat'ing + const QFileInfo local(url.toLocalFile()); + // Get the initial directory URL + if (!url.isEmpty()) + directory = _qt_get_directory(url, local); + if (directory.isEmpty()) { + const QUrl lastVisited = *lastVisitedDir(); + if (lastVisited != url) + directory = _qt_get_directory(lastVisited, QFileInfo()); + } + if (directory.isEmpty()) + directory = QUrl::fromLocalFile(QDir::currentPath()); + + /* + The initial directory can contain both the initial directory + and initial selection, e.g. /home/user/foo.txt + */ + if (selection.isEmpty() && !url.isEmpty()) { + if (url.isLocalFile()) { + if (!local.isDir()) + selection = local.fileName(); + } else { + // With remote URLs we can only assume. + selection = url.fileName(); + } } - // With remote URLs we can only assume. - return url.fileName(); } /*! @@ -2923,14 +2916,13 @@ bool QFileDialogPrivate::restoreWidgetState(QStringList &history, int splitterPo Create widgets, layout and set default values */ -void QFileDialogPrivate::init(const QUrl &directory, const QString &nameFilter, - const QString &caption) +void QFileDialogPrivate::init(const QFileDialogArgs &args) { Q_Q(QFileDialog); - if (!caption.isEmpty()) { + if (!args.caption.isEmpty()) { useDefaultCaption = false; - setWindowTitle = caption; - q->setWindowTitle(caption); + setWindowTitle = args.caption; + q->setWindowTitle(args.caption); } q->setAcceptMode(QFileDialog::AcceptOpen); @@ -2938,17 +2930,17 @@ void QFileDialogPrivate::init(const QUrl &directory, const QString &nameFilter, if (!nativeDialogInUse) createWidgets(); q->setFileMode(QFileDialog::AnyFile); - if (!nameFilter.isEmpty()) - q->setNameFilter(nameFilter); + if (!args.filter.isEmpty()) + q->setNameFilter(args.filter); // QTBUG-70798, prevent the default blocking the restore logic. - const bool dontStoreDir = !directory.isValid() && !lastVisitedDir()->isValid(); - q->setDirectoryUrl(workingDirectory(directory)); + const bool dontStoreDir = !args.directory.isValid() && !lastVisitedDir()->isValid(); + q->setDirectoryUrl(args.directory); if (dontStoreDir) lastVisitedDir()->clear(); - if (directory.isLocalFile()) - q->selectFile(initialSelection(directory)); + if (args.directory.isLocalFile()) + q->selectFile(args.selection); else - q->selectUrl(directory); + q->selectUrl(args.directory); #if QT_CONFIG(settings) // Try to restore from the FileDialog settings group; if it fails, fall back diff --git a/src/widgets/dialogs/qfiledialog_p.h b/src/widgets/dialogs/qfiledialog_p.h index 42932bab5d..1d9929e36a 100644 --- a/src/widgets/dialogs/qfiledialog_p.h +++ b/src/widgets/dialogs/qfiledialog_p.h @@ -98,15 +98,15 @@ class QPlatformDialogHelper; struct QFileDialogArgs { - QFileDialogArgs() : parent(nullptr), mode(QFileDialog::AnyFile) {} + QFileDialogArgs(const QUrl &url = {}); - QWidget *parent; + QWidget *parent = nullptr; QString caption; QUrl directory; QString selection; QString filter; - QFileDialog::FileMode mode; - QFileDialog::Options options; + QFileDialog::FileMode mode = QFileDialog::AnyFile; + QFileDialog::Options options = {}; }; #define UrlRole (Qt::UserRole + 1) @@ -133,12 +133,9 @@ public: void createMenuActions(); void createWidgets(); - void init(const QUrl &directory = QUrl(), const QString &nameFilter = QString(), - const QString &caption = QString()); + void init(const QFileDialogArgs &args); bool itemViewKeyboardEvent(QKeyEvent *event); QString getEnvironmentVariable(const QString &string); - static QUrl workingDirectory(const QUrl &path); - static QString initialSelection(const QUrl &path); QStringList typedFiles() const; QList userSelectedFiles() const; QStringList addDefaultSuffixToFiles(const QStringList &filesToFix) const; -- cgit v1.2.3